瀏覽代碼

Web UI: working GET(short) happy/sad with template. No tests yet.

Frederic G. MARAND 5 年之前
父節點
當前提交
3d9674d19c
共有 9 個文件被更改,包括 130 次插入65 次删除
  1. 9 3
      cmd/kurzd/dist.config.yml
  2. 2 2
      cmd/kurzd/serve.go
  3. 39 55
      web/get_short.go
  4. 2 0
      web/templates/201.gohtml
  5. 25 0
      web/templates/403.gohtml
  6. 5 3
      web/templates/404.gohtml
  7. 25 0
      web/templates/451.gohtml
  8. 2 0
      web/templates/home.gohtml
  9. 21 2
      web/web.go

+ 9 - 3
cmd/kurzd/dist.config.yml

@@ -1,12 +1,18 @@
+api:
+  address: ":8000"
 database:
   # Code likely not to work on another engine because it adds ?parseTime=true
   driver: mysql
   dsn: <user>:<pass>@[<server>]/<database>
   test_dsn: <user>:<pass>@[<server>]/<database>
-api:
-  address: ":8000"
 web:
   address: ":3000"
   # May be absolute or relative to the "kurzd" binary.
   assetsPath: ../../web/public
-  siteName: "Kurz"
+  # The initial version cache buster on Kurz start. Will probably not remain in config.
+  assetsVersion: 1
+  fullyQualifiedSiteBaseURL: &fqsu http://localhost:3000
+  fullyQualifiedAssetsBaseURL: *fqsu
+  # Delay is in seconds
+  refreshDelay: 5
+  siteName: "Kurz"

+ 2 - 2
cmd/kurzd/serve.go

@@ -4,7 +4,6 @@ import (
 	"code.osinet.fr/fgm/kurz/web"
 	"context"
 	"database/sql"
-	"github.com/davecgh/go-spew/spew"
 	"github.com/spf13/viper"
 	"log"
 	"net/http"
@@ -69,10 +68,11 @@ func serveHandler(_ *cobra.Command, args []string) {
 	viper.SetDefault("web.assetsPath", "../../web/public")
 	assetsPath := viper.Get("web.assetsPath").(string)
 
+	webConfig := viper.Get("web").(map[string]interface{})
 	router := mux.NewRouter()
 	api.SetupRoutes(router)
-	spew.Dump(viper.Get("web"))
 	web.SetupRoutes(router, assetsPath)
+	web.BuildGlobals(webConfig)
 	http.Handle("/", router)
 
 	address := viper.Get("web.address").(string)

+ 39 - 55
web/get_short.go

@@ -1,9 +1,9 @@
 package web
 
 import (
-	"fmt"
+	"io"
 	"net/http"
-	"time"
+	"strings"
 
 	"code.osinet.fr/fgm/kurz/domain"
 	"github.com/gorilla/mux"
@@ -22,41 +22,49 @@ Template variables:
 
 type bytes = []byte
 
-func build403(w http.ResponseWriter, short string) (bytes, error) {
-	type Short403Data struct {
-		FullyQualifiedAssetsBaseURL string
-		FullyQualifiedShortURL      string
-		FullyQualifiedSiteURL       string
-		RefreshDelay                time.Duration
-		SiteName                    string
+func build403(w http.ResponseWriter, short string) error {
+	data := struct {
+		FullyQualifiedShortURL string
+		Globals
+	}{
+		short,
+		globals,
 	}
 
-	data := Short403Data{}
-	tmpl.Execute(w, data)
-	return bytes(""), nil
+	sw := &strings.Builder{}
+	err := tmpl.ExecuteTemplate(sw, "403", data)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+	} else {
+		w.WriteHeader(http.StatusForbidden)
+		io.Copy(w, strings.NewReader(sw.String()))
+	}
+
+	return err
 }
 
-func build404(w http.ResponseWriter, short string) (bytes, error) {
-	type Short404Data struct {
-		AssetsVersion               int
-		FullyQualifiedAssetsBaseURL string
-		FullyQualifiedShortURL      string
-		FullyQualifiedSiteURL       string
-		FullyQualifiedTargetURL     string
-		RefreshDelay                time.Duration
-		SiteName                    string
+func build404(w http.ResponseWriter, short string) error {
+	data := struct {
+		FullyQualifiedShortURL string
+		Globals
+	}{
+		short,
+		globals,
 	}
 
-	data := Short404Data{}
-	err := tmpl.Execute(w, data)
+	sw := &strings.Builder{}
+	err := tmpl.ExecuteTemplate(sw, "404", data)
 	if err != nil {
-		fmt.Println(err)
+		w.WriteHeader(http.StatusInternalServerError)
+	} else {
+		w.WriteHeader(http.StatusNotFound)
+		io.Copy(w, strings.NewReader(sw.String()))
 	}
-	return bytes(""), nil
+	return err
 }
 
-func build451(w http.ResponseWriter, short string) (bytes, error) {
-	return nil, nil
+func build451(w http.ResponseWriter, short string) error {
+	return nil
 }
 
 // handleGetShort handles path /<short>
@@ -84,42 +92,18 @@ func handleGetShort(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// Normal sad paths.
-	// TODO improve UX: build pages instead of just HTTP 4xx.
-	var status int
 	switch domainErr.Kind {
 	case domain.ShortNotFound:
-		status = http.StatusNotFound
-		if page, err := build404(w, short); err != nil {
-			status = http.StatusInternalServerError
-			w.WriteHeader(status)
-		} else {
-			w.WriteHeader(status)
-			w.Write(page)
-		}
+		build404(w, short)
 
 	case domain.TargetBlockedError:
-		status = http.StatusForbidden
-		if page, err := build403(w, short); err != nil {
-			status = http.StatusInternalServerError
-			w.WriteHeader(status)
-		} else {
-			w.WriteHeader(status)
-			w.Write(page)
-		}
+		build403(w, short)
 
 	case domain.TargetCensoredError:
-		status = http.StatusUnavailableForLegalReasons
-		if page, err := build451(w, short); err != nil {
-			status = http.StatusInternalServerError
-			w.WriteHeader(status)
-		} else {
-			w.WriteHeader(status)
-			w.Write(page)
-		}
+		build451(w, short)
 
 	default:
 		// TargetInvalid is not supposed to happen in this case, so it is an internal error too.
-		status = http.StatusInternalServerError
-		w.WriteHeader(status)
+		w.WriteHeader(http.StatusInternalServerError)
 	}
 }

+ 2 - 0
web/templates/201.gohtml

@@ -1,3 +1,4 @@
+{{define "201"}}
 <!DOCTYPE html>
 <html lang="fr" dir="ltr">
 <head>
@@ -23,3 +24,4 @@
     {{template "footer"}}
 </body>
 </html>
+{{end}}

+ 25 - 0
web/templates/403.gohtml

@@ -0,0 +1,25 @@
+{{define "403"}}
+<!DOCTYPE html>
+<html lang="fr" dir="ltr">
+<head>
+    <title>{{.SiteName}}: {{.FullyQualifiedShortURL}}</title>
+    <link rel="shortcut icon" href="{{.FullyQualifiedAssetsBaseURL}}/favicon.ico?v=1">
+    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/plusvite.css?v={{.AssetsVersion}}" />
+    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/riff.css?v={{.AssetsVersion}}" />
+    {{template "analytics" .}}
+    {{template "inlinecss"}}
+    <meta http-equiv="Refresh" content="{{.RefreshDelay}};{{.FullyQualifiedSiteURL}}" />
+</head>
+
+<body>
+    <div id="box">
+        <h1>{{.FullyQualifiedShortURL}}</h1>
+        <p>L'URL de destination est valide mais a été bloqué par son titulaire.</p>
+        <p>Dans quelques secondes, vous serez redirigé vers la page d'accueil du site.</p>
+        <p><a href="{{.FullyQualifiedSiteURL}}">Retour</a></p>
+    </div>
+
+    {{template "footer"}}
+</body>
+</html>
+{{end}}

+ 5 - 3
web/templates/404.gohtml

@@ -1,10 +1,11 @@
+{{define "404"}}
 <!DOCTYPE html>
 <html lang="fr" dir="ltr">
 <head>
     <title>{{.SiteName}}: {{.FullyQualifiedShortURL}}</title>
     <link rel="shortcut icon" href="{{.FullyQualifiedAssetsBaseURL}}/favicon.ico?v=1">
-    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/plusvite.css?v={{.AssetsVersion}}" />
-    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/riff.css?v={{.AssetsVersion}}" />
+    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/plusvite.css?v={{.Globals.AssetsVersion}}" />
+    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/riff.css?v={{.Globals.AssetsVersion}}" />
     {{template "analytics" .}}
     {{template "inlinecss"}}
     <meta http-equiv="Refresh" content="{{.RefreshDelay}};{{.FullyQualifiedSiteURL}}" />
@@ -13,7 +14,7 @@
 <body>
     <div id="box">
         <h1>{{.FullyQualifiedShortURL}}</h1>
-        <p>Ceci n'est pas un URL <a href="{{.FullyQualifiedSiteURL}}">{{.FullyQualifiedSiteURL}}</a> valide.</p>
+        <p>Ceci n'est pas un URL <a href="{{.FullyQualifiedSiteURL}}">{{.SiteName}}</a> valide.</p>
         <p>Dans quelques secondes, vous serez redirigé vers la page d'accueil du site.</p>
         <p><a href="{{.FullyQualifiedSiteURL}}">Retour</a></p>
     </div>
@@ -21,3 +22,4 @@
     {{template "footer"}}
 </body>
 </html>
+{{end}}

+ 25 - 0
web/templates/451.gohtml

@@ -0,0 +1,25 @@
+{{define "451"}}
+<!DOCTYPE html>
+<html lang="fr" dir="ltr">
+<head>
+    <title>{{.SiteName}}: {{.FullyQualifiedShortURL}}</title>
+    <link rel="shortcut icon" href="{{.FullyQualifiedAssetsBaseURL}}/favicon.ico?v=1">
+    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/plusvite.css?v={{.AssetsVersion}}" />
+    <link rel="stylesheet" type="text/css" href="{{.FullyQualifiedAssetsBaseURL}}/public/css/riff.css?v={{.AssetsVersion}}" />
+    {{template "analytics" .}}
+    {{template "inlinecss"}}
+    <meta http-equiv="Refresh" content="{{.RefreshDelay}};{{.FullyQualifiedSiteURL}}" />
+</head>
+
+<body>
+    <div id="box">
+        <h1>{{.FullyQualifiedShortURL}}</h1>
+        <p>L'URL de destination est valide mais a été bloqué par décision légale.</p>
+        <p>Dans quelques secondes, vous serez redirigé vers la page d'accueil du site.</p>
+        <p><a href="{{.FullyQualifiedSiteURL}}">Retour</a></p>
+    </div>
+
+    {{template "footer"}}
+</body>
+</html>
+{{end}}

+ 2 - 0
web/templates/home.gohtml

@@ -1,3 +1,4 @@
+{{define "home"}}
 <!DOCTYPE html>
 <html lang="fr" dir="ltr">
 <head>
@@ -48,3 +49,4 @@
     {{template "footer"}}
 </body>
 </html>
+{{end}}

+ 21 - 2
web/web.go

@@ -8,7 +8,6 @@ a complete application.
 package web
 
 import (
-	"github.com/davecgh/go-spew/spew"
 	"html/template"
 	"net/http"
 	"path/filepath"
@@ -34,6 +33,16 @@ const (
 
 var tmpl *template.Template
 
+type Globals struct {
+	AssetsVersion               int
+	FullyQualifiedAssetsBaseURL string
+	FullyQualifiedSiteBaseURL   string
+	RefreshDelay                int
+	SiteName                    string
+}
+
+var globals Globals
+
 // SetupRoutes() configures Web UI routes on the passed mux.Router.
 func SetupRoutes(router *mux.Router, configAssetsPath string) {
 	const assetsPrefix = "/public"
@@ -59,7 +68,6 @@ func SetupRoutes(router *mux.Router, configAssetsPath string) {
 		Name(RouteGetRoot)
 
 	base, _ := filepath.Abs(configAssetsPath + "/../templates/")
-	spew.Dump(base)
 	layout := base + "/layout"
 	tmpl = template.Must(template.ParseFiles(
 		base+"/201.gohtml",
@@ -70,3 +78,14 @@ func SetupRoutes(router *mux.Router, configAssetsPath string) {
 		layout+"/inlinecss.gohtml",
 	))
 }
+
+func BuildGlobals(c map[string]interface{}) {
+	// Note: keys in viper are lower-cased.
+	globals = Globals{
+		AssetsVersion:               c["assetsversion"].(int),
+		FullyQualifiedAssetsBaseURL: c["fullyqualifiedassetsbaseurl"].(string),
+		FullyQualifiedSiteBaseURL:   c["fullyqualifiedsitebaseurl"].(string),
+		RefreshDelay:                c["refreshdelay"].(int),
+		SiteName:                    c["sitename"].(string),
+	}
+}