first version
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.idea/
|
||||||
|
content.json
|
||||||
11
README.md
Normal file
11
README.md
Normal file
@@ -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
|
||||||
13
content.base.json
Normal file
13
content.base.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"categories": [
|
||||||
|
{
|
||||||
|
"title": "Cat 1",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"title": "Link 1",
|
||||||
|
"url": "https://google.fr/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
16
data/data.go
Normal file
16
data/data.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
80
data/json/json.go
Normal file
80
data/json/json.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
8
go.mod
Normal file
8
go.mod
Normal file
@@ -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
|
||||||
|
)
|
||||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -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=
|
||||||
30
main.go
Normal file
30
main.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
148
server/server.go
Normal file
148
server/server.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
19
server/templates/base.html
Normal file
19
server/templates/base.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{ define "base" }}
|
||||||
|
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" content="text/html">
|
||||||
|
<title>Retro Hub</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Retro Hub</h1>
|
||||||
|
<p>Welcome to retro hub, this website reference some tools for older browsers</p>
|
||||||
|
<hr/>
|
||||||
|
<a href="/">List of websites</a> <a href="/ua">User-Agent information</a>
|
||||||
|
<hr/>
|
||||||
|
{{ template "content" . }}
|
||||||
|
<hr/>
|
||||||
|
<p>Retro Hub v{{ .Version }} powered by Go, made by thelilfrog (<a href="https://github.com/mojitaurelie">Github</a>, not available for old systems)</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
||||||
3
server/templates/error.html
Normal file
3
server/templates/error.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{{ define "content" }}
|
||||||
|
<h1>{{ .StatusCode }} {{ .StatusText }}</h1>
|
||||||
|
{{ end }}
|
||||||
34
server/templates/index.html
Normal file
34
server/templates/index.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{{ define "content" }}
|
||||||
|
{{ range $category := .Provider.Categories }}
|
||||||
|
{{ $links := $category.Links }}
|
||||||
|
<h2>{{ $category.Title }}</h2>
|
||||||
|
{{ $length := len $links }} {{ if eq $length 0 }}
|
||||||
|
<p>This category is empty for now :(</p>
|
||||||
|
{{ else }}
|
||||||
|
<table cellpadding="3" border="2" bgcolor="silver">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th bgcolor="white">Title</th>
|
||||||
|
<th bgcolor="white">Information</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range $link := $links }}
|
||||||
|
<tr>
|
||||||
|
<td bgcolor="white">
|
||||||
|
<a href="{{$link.URL}}">{{ $link.Title }}</a>
|
||||||
|
</td>
|
||||||
|
<td bgcolor="white">
|
||||||
|
{{ $length := len $link.Description }} {{ if eq $length 0 }}
|
||||||
|
<i>No description</i>
|
||||||
|
{{ else }}
|
||||||
|
{{$link.Description}}
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
26
server/templates/ua.html
Normal file
26
server/templates/ua.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{{ define "content" }}
|
||||||
|
{{ $length := len .UserAgent.String }} {{ if eq $length 0 }}
|
||||||
|
<i>No User-Agent provided by the browser</i>
|
||||||
|
{{ else }}
|
||||||
|
<p>Your User-Agent is <b>{{ .UserAgent.String }}</b></p>
|
||||||
|
<h3>Detailled information:</h3>
|
||||||
|
<ul>
|
||||||
|
{{ $length := len .UserAgent.Name }} {{ if not (eq $length 0) }}
|
||||||
|
<li>Browser name: <b>{{ .UserAgent.Name }}</b></li>
|
||||||
|
{{ end }}
|
||||||
|
{{ $length := len .UserAgent.Version }} {{ if not (eq $length 0) }}
|
||||||
|
<li>Browser version: <b>{{ .UserAgent.Version }}</b></li>
|
||||||
|
{{ end }}
|
||||||
|
{{ $length := len .UserAgent.OS }} {{ if not (eq $length 0) }}
|
||||||
|
<li>Browser OS: <b>{{ .UserAgent.OS }}</b></li>
|
||||||
|
{{ end }}
|
||||||
|
{{ $length := len .UserAgent.OSVersion }} {{ if not (eq $length 0) }}
|
||||||
|
<li>Browser OS Version: <b>{{ .UserAgent.OSVersion }}</b></li>
|
||||||
|
{{ end }}
|
||||||
|
{{ $length := len .UserAgent.Device }} {{ if not (eq $length 0) }}
|
||||||
|
<li>Device: <b>{{ .UserAgent.Device }}</b></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
<small>This detailled information is provided by <a href="https://github.com/mileusna/useragent/">github.com/mileusna/useragent</a></small>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
Reference in New Issue
Block a user