Merge all the server components

This commit is contained in:
Tom Wiesing 2022-09-15 15:04:35 +02:00
parent 85b5603d9d
commit f5f2ac1a03
No known key found for this signature in database
25 changed files with 365 additions and 352 deletions

View file

@ -4,11 +4,16 @@ import (
"embed"
"github.com/FAU-CDI/wisski-distillery/internal/component"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/core"
)
type Dis struct {
component.ComponentBase
Instances *instances.Instances
ResolverFile string
}
func (dis Dis) Name() string {
@ -35,7 +40,9 @@ func (dis Dis) Stack() component.Installable {
"GLOBAL_AUTHORIZED_KEYS_FILE": dis.Config.GlobalAuthorizedKeysFile,
"SELF_OVERRIDES_FILE": dis.Config.SelfOverridesFile,
},
CopyContextFiles: []string{dis.Config.CurrentExecutable()},
TouchFiles: []string{dis.ResolverFile},
CopyContextFiles: []string{core.Executable},
})
}

View file

@ -0,0 +1,26 @@
package dis
import (
"net/http"
"github.com/tkw1536/goprogram/stream"
)
func (dis Dis) info(io stream.IOStream) (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
all, err := dis.Instances.All()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
io.EPrintln(err)
return
}
for _, wk := range all {
w.WriteHeader(http.StatusOK)
w.Write([]byte(wk.Slug))
w.Write([]byte("\n"))
}
}), nil
}

View file

@ -0,0 +1,61 @@
package dis
import (
"fmt"
"os"
"path/filepath"
"regexp"
"github.com/FAU-CDI/wdresolve"
"github.com/FAU-CDI/wdresolve/resolvers"
"github.com/tkw1536/goprogram/stream"
)
func (dis Dis) ResolverConfigPath() string {
return filepath.Join(dis.Dir, dis.ResolverFile)
}
func (dis Dis) resolver(io stream.IOStream) (p wdresolve.ResolveHandler, err error) {
p.TrustXForwardedProto = true
fallback := &resolvers.Regexp{
Data: map[string]string{},
}
// handle the default domain name!
domainName := dis.Config.DefaultDomain
if domainName != "" {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domainName))] = fmt.Sprintf("https://$1.%s", domainName)
io.Printf("registering default domain %s\n", domainName)
}
// handle the extra domains!
for _, domain := range dis.Config.SelfExtraDomains {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domain))] = fmt.Sprintf("https://$1.%s", domainName)
io.Printf("registering legacy domain %s\n", domain)
}
// open the prefix file
prefixFile := dis.ResolverConfigPath()
fs, err := os.Open(prefixFile)
io.Println("loading prefixes from ", prefixFile)
if err != nil {
return p, err
}
defer fs.Close()
// read the prefixes
// TODO: Do we want to load these without a file?
prefixes, err := resolvers.ReadPrefixes(fs)
if err != nil {
return p, err
}
// and use that as the resolver!
p.Resolver = resolvers.InOrder{
prefixes,
fallback,
}
return p, nil
}

View file

@ -0,0 +1,137 @@
package dis
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"github.com/tkw1536/goprogram/stream"
)
// self returns the handler for the self overrides
func (dis Dis) self(io stream.IOStream) (redirect Redirect, err error) {
// open the overrides file
overrides, err := os.Open(dis.Config.SelfOverridesFile)
io.Printf("loading overrides from %q\n", dis.Config.SelfOverridesFile)
if err != nil {
return redirect, err
}
defer overrides.Close()
// decode the overrides file
if err := json.NewDecoder(overrides).Decode(&redirect.Overrides); err != nil {
return redirect, err
}
if redirect.Overrides == nil {
redirect.Overrides = make(map[string]string)
}
redirect.Overrides["/"] = dis.Config.SelfRedirect.String()
// create a redirect server
redirect.Fallback, err = dis.selfFallback()
if err != nil {
return redirect, err
}
redirect.Absolute = false
redirect.Overrides = nil
redirect.Permanent = false
// and return!
return redirect, nil
}
func (dis *Dis) selfFallback() (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
dis.serveFallback(w, r)
}), nil
}
var notFoundText = []byte("not found")
func (dis *Dis) serveFallback(w http.ResponseWriter, r *http.Request) {
slug := dis.Config.SlugFromHost(r.Host)
if slug == "" {
w.WriteHeader(http.StatusNotFound)
w.Write(notFoundText)
return
}
if ok, _ := dis.Instances.Has(slug); !ok {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "WissKI %q not found\n", slug)
return
}
w.WriteHeader(http.StatusBadGateway)
fmt.Fprintf(w, "WissKI %q is currently offline\n", slug)
}
// Redirect implements a redirect server that redirects all requests.
// It implements http.Handler.
type Redirect struct {
// Target is the target URL to redirect to.
Target string
// Fallback is used when target is the empty string.
Fallback http.Handler
// Absolute determines if the request path should be appended to the target URL when redirecting.
// By default this path is always appended, set Absolute to true to prevent this.
Absolute bool
// Overrides is a map from paths to URLs that should override the default target.
Overrides map[string]string
// Permanent determines if the redirect responses issued should return
// Permanent Redirect (Status Code 308) or Temporary Redirect (Status Code 307).
Permanent bool
}
// Redirect determines the redirect URL for a specific incoming request
// If it returns the empty string, the fallback is used.
func (redirect Redirect) Redirect(r *http.Request) string {
// if we have an override for this URL, use it immediatly
url := strings.TrimSuffix(r.URL.Path, "/")
if override, ok := redirect.Overrides[url]; ok {
return override
}
if redirect.Target == "" {
return ""
}
// if we are in absolute redirect mode, always return the absolute URL
if redirect.Absolute {
return redirect.Target
}
// return the target + the redirected URL
dest := strings.TrimSuffix(redirect.Target, "/") + r.URL.Path
if len(r.URL.RawQuery) > 0 {
dest += "?" + r.URL.RawQuery
}
return dest
}
// ServeHTTP implements the http.Handler interface and redirects a single request to redirect.Target.
func (redirect Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) {
dest := redirect.Redirect(r)
if dest == "" {
redirect.Fallback.ServeHTTP(w, r)
return
}
// determine if we are temporary or permanent redirect
status := http.StatusTemporaryRedirect
if redirect.Permanent {
status = http.StatusPermanentRedirect
}
// and do the redirect
http.Redirect(w, r, dest, status)
}

View file

@ -0,0 +1,39 @@
package dis
import (
"net/http"
"github.com/tkw1536/goprogram/stream"
)
// Server returns an http.Mux that implements the main server instance
func (dis Dis) Server(io stream.IOStream) (http.Handler, error) {
// self server
self, err := dis.self(io)
if err != nil {
return nil, err
}
resolver, err := dis.resolver(io)
if err != nil {
return nil, err
}
info, err := dis.info(io)
if err != nil {
return nil, err
}
// resolver
mux := http.NewServeMux()
mux.Handle("/", self)
mux.Handle("/go/", resolver)
mux.Handle("/wisski/navigate", resolver)
// TODO: Fix me!
mux.Handle("/dis/", info)
return mux, nil
}

View file

@ -2,4 +2,4 @@ FROM docker.io/library/alpine
COPY wdcli /wdcli
EXPOSE 8888
CMD ["/wdcli","--internal-in-docker","--config","${CONFIG_PATH}","dis_server","--bind","0.0.0.0:8888"]
CMD ["/wdcli","--internal-in-docker","--config","${CONFIG_PATH}","server","--bind","0.0.0.0:8888"]

View file

@ -8,7 +8,6 @@ services:
# port and hostname for this image to use
VIRTUAL_HOST: ${VIRTUAL_HOST}
VIRTUAL_PORT: 8888
VIRTUAL_PATH: /dis/
CONFIG_PATH: ${CONFIG_PATH}