commit 784679592df71efd79ac8d716c576fad4720d4c2 Author: Aurélie Delhaie Date: Sun May 7 22:34:30 2023 +0200 first version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d33b61 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +content.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c17138 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Retro Hub page + +This project aims to list pages compatible with older browsers + +## Run the server + +You have to rename the file content.base.json to content.json or start the server with the argument `-content content.base.json` + +### Port + +You can change the listening port with the `-port` argument diff --git a/content.base.json b/content.base.json new file mode 100644 index 0000000..ffc37bb --- /dev/null +++ b/content.base.json @@ -0,0 +1,13 @@ +{ + "categories": [ + { + "title": "Cat 1", + "links": [ + { + "title": "Link 1", + "url": "https://google.fr/" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/data.go b/data/data.go new file mode 100644 index 0000000..905ede4 --- /dev/null +++ b/data/data.go @@ -0,0 +1,16 @@ +package data + +type Provider interface { + Categories() []Category +} + +type Category interface { + Title() string + Links() []Link +} + +type Link interface { + Title() string + URL() string + Description() string +} diff --git a/data/json/json.go b/data/json/json.go new file mode 100644 index 0000000..814cb18 --- /dev/null +++ b/data/json/json.go @@ -0,0 +1,80 @@ +package json + +import ( + "encoding/json" + "os" + "retroHub/data" +) + +type FileProvider struct { + categories []category +} + +type fileContent struct { + Categories []category `json:"categories"` +} + +type category struct { + T string `json:"title"` + Ls []link `json:"links"` +} + +func (c category) Title() string { + return c.T +} + +func (c category) Links() []data.Link { + var r []data.Link + for _, l := range c.Ls { + r = append(r, data.Link(l)) + } + return r +} + +type link struct { + T string `json:"title"` + U string `json:"url"` + D string `json:"description"` +} + +func (l link) Title() string { + return l.T +} + +func (l link) URL() string { + return l.U +} + +func (l link) Description() string { + return l.D +} + +func (jfp *FileProvider) Categories() []data.Category { + var r []data.Category + for _, c := range jfp.categories { + r = append(r, data.Category(c)) + } + return r +} + +func New(fp string) (*FileProvider, error) { + if _, err := os.Stat(fp); err != nil { + return nil, err + } + + content, err := os.ReadFile(fp) + if err != nil { + return nil, err + } + + var c fileContent + err = json.Unmarshal(content, &c) + if err != nil { + return nil, err + } + + jfp := new(FileProvider) + jfp.categories = c.Categories + + return jfp, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2776a29 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module retroHub + +go 1.20 + +require ( + github.com/go-chi/chi/v5 v5.0.8 + github.com/mileusna/useragent v1.3.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f008911 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/mileusna/useragent v1.3.2 h1:yGBQVNkyrlnSe4l0rlaQoH8XlG9xDkc6a7ygwPxALoU= +github.com/mileusna/useragent v1.3.2/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= diff --git a/main.go b/main.go new file mode 100644 index 0000000..8465a98 --- /dev/null +++ b/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "flag" + "retroHub/data/json" + "retroHub/server" +) + +func main() { + contentPath := flag.String("content", "content.json", "Set the content configuration path, this file is encoded in JSON") + serverPort := flag.Int("port", 8080, "Set the HTTP Server port") + + if contentPath == nil { + contentPath = new(string) + *contentPath = "content.json" + } + if serverPort == nil { + serverPort = new(int) + *serverPort = 8080 + } + + provider, err := json.New(*contentPath) + if err != nil { + panic(err) + } + err = server.Serve(provider, uint(*serverPort)) + if err != nil { + panic(err) + } +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..1fdc88f --- /dev/null +++ b/server/server.go @@ -0,0 +1,148 @@ +package server + +import ( + "embed" + _ "embed" + "errors" + "fmt" + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "github.com/mileusna/useragent" + "html/template" + "log" + "net/http" + "os" + "retroHub/data" + "strings" +) + +type injector struct { + Version string +} + +type userAgentInjector struct { + injector + UserAgent useragent.UserAgent +} + +type providerInjector struct { + injector + Provider data.Provider +} + +type errorInjector struct { + injector + StatusCode int + StatusText string +} + +const version string = "0.1" + +//go:embed templates +var templates embed.FS + +var ( + provider data.Provider + indexTemplate *template.Template + uaTemplate *template.Template + errTemplate *template.Template + + errLog *log.Logger +) + +func init() { + indexTemplate = template.Must(template.ParseFS(templates, "templates/base.html", "templates/index.html")) + uaTemplate = template.Must(template.ParseFS(templates, "templates/base.html", "templates/ua.html")) + errTemplate = template.Must(template.ParseFS(templates, "templates/base.html", "templates/error.html")) + + errLog = log.New(os.Stderr, "", log.LstdFlags) +} + +func Serve(contentProvider data.Provider, port uint) error { + if contentProvider == nil { + return errors.New("content provider cannot be nil") + } + provider = contentProvider + + r := chi.NewRouter() + r.Use(middleware.Logger) + r.Use(panicHandler) + + r.NotFound(notFoundHandler) + r.Get("/", indexHandler) + r.Get("/ua", userAgentHandler) + + log.Printf("the server is up and running on %d", port) + err := http.ListenAndServe(fmt.Sprintf(":%d", port), r) + if err != nil { + return err + } + return nil +} + +func panicHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer func() { + if r := recover(); r != nil { + errLog.Println("ERROR", r) + injectedData := errorInjector{ + injector: injector{ + Version: version, + }, + StatusCode: http.StatusInternalServerError, + StatusText: "Internal Server Error", + } + w.WriteHeader(http.StatusInternalServerError) + err := errTemplate.ExecuteTemplate(w, "base", injectedData) + if err != nil { + errLog.Println("ERROR", err) + } + } + }() + next.ServeHTTP(w, r) + }) +} + +func indexHandler(w http.ResponseWriter, r *http.Request) { + injectedData := providerInjector{ + injector: injector{ + Version: version, + }, + Provider: provider, + } + err := indexTemplate.ExecuteTemplate(w, "base", injectedData) + if err != nil { + errLog.Println("ERROR", err) + } +} + +func userAgentHandler(w http.ResponseWriter, r *http.Request) { + injectedData := userAgentInjector{ + injector: injector{ + Version: version, + }, + } + userAgent := strings.TrimSpace(r.UserAgent()) + if len(userAgent) > 0 { + injectedData.UserAgent = useragent.Parse(userAgent) + } + err := uaTemplate.ExecuteTemplate(w, "base", injectedData) + if err != nil { + errLog.Println("ERROR", err) + } +} + +func notFoundHandler(w http.ResponseWriter, _ *http.Request) { + injectedData := errorInjector{ + injector: injector{ + Version: version, + }, + StatusCode: http.StatusNotFound, + StatusText: "Not Found", + } + w.WriteHeader(http.StatusNotFound) + err := errTemplate.ExecuteTemplate(w, "base", injectedData) + if err != nil { + errLog.Println("ERROR", err) + } +} diff --git a/server/templates/base.html b/server/templates/base.html new file mode 100644 index 0000000..f166a79 --- /dev/null +++ b/server/templates/base.html @@ -0,0 +1,19 @@ +{{ define "base" }} + + + + + Retro Hub + + +

Retro Hub

+

Welcome to retro hub, this website reference some tools for older browsers

+
+ List of websites User-Agent information +
+ {{ template "content" . }} +
+

Retro Hub v{{ .Version }} powered by Go, made by thelilfrog (Github, not available for old systems)

+ + +{{ end }} \ No newline at end of file diff --git a/server/templates/error.html b/server/templates/error.html new file mode 100644 index 0000000..7a9dd3b --- /dev/null +++ b/server/templates/error.html @@ -0,0 +1,3 @@ +{{ define "content" }} +

{{ .StatusCode }} {{ .StatusText }}

+{{ end }} \ No newline at end of file diff --git a/server/templates/index.html b/server/templates/index.html new file mode 100644 index 0000000..f1d6644 --- /dev/null +++ b/server/templates/index.html @@ -0,0 +1,34 @@ +{{ define "content" }} + {{ range $category := .Provider.Categories }} + {{ $links := $category.Links }} +

{{ $category.Title }}

+ {{ $length := len $links }} {{ if eq $length 0 }} +

This category is empty for now :(

+ {{ else }} + + + + + + + + + {{ range $link := $links }} + + + + + {{ end }} + +
TitleInformation
+ {{ $link.Title }} + + {{ $length := len $link.Description }} {{ if eq $length 0 }} + No description + {{ else }} + {{$link.Description}} + {{ end }} +
+ {{ end }} + {{ end }} +{{ end }} \ No newline at end of file diff --git a/server/templates/ua.html b/server/templates/ua.html new file mode 100644 index 0000000..81a0e5c --- /dev/null +++ b/server/templates/ua.html @@ -0,0 +1,26 @@ +{{ define "content" }} + {{ $length := len .UserAgent.String }} {{ if eq $length 0 }} + No User-Agent provided by the browser + {{ else }} +

Your User-Agent is {{ .UserAgent.String }}

+

Detailled information:

+ + This detailled information is provided by github.com/mileusna/useragent + {{ end }} +{{ end }} \ No newline at end of file