add a bunch of frontend stuff

This commit is contained in:
sam 2023-09-03 04:11:56 +02:00
parent 2586161abd
commit bc85b7c340
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
30 changed files with 1459 additions and 136 deletions

View file

@ -0,0 +1,5 @@
{
"useTabs": true,
"trailingComma": "all",
"singleQuote": false
}

View file

@ -0,0 +1,19 @@
:root {
--dark-background: #2B303A;
--light-background: #F5F5F5;
--dark-text: #FFFFFF;
--light-text: #000000;
--dark-primary: #087E8B;
--light-primary: #67AEB5;
--dark-secondary: #7C7F85;
--light-secondary: #A4A6AA;
--dark-danger: #D64933;
--light-danger: #FF5A5F;
--dark-success: #4DA167;
--light-success: #77B68B;
}

26
assets/scss/style.scss Normal file
View file

@ -0,0 +1,26 @@
@use "node_modules/normalize.css/normalize";
@use "_variables";
body {
font-family: sans-serif;
background-color: var(--light-background);
color: var(--light-text);
&:dark {
background-color: var(--dark-background);
color: var(--dark-text);
}
}
.auth {
width: 100%;
@media (min-width: 640px) {
max-width: 520px;
}
margin: 1rem auto;
p {
display: flex;
flex-direction: column;
}
}

View file

@ -30,7 +30,10 @@ func run(c *cli.Context) error {
return errors.Wrap(err, "creating postgres database") return errors.Wrap(err, "creating postgres database")
} }
a := app.NewApp(cfg, db) a, err := app.NewApp(cfg, db)
if err != nil {
return errors.Wrap(err, "creating app")
}
log.Debug().Msg("Creating account") log.Debug().Msg("Creating account")
acct, err := a.Account().CreateLocal(c.Context, "testington", "no@mercury.example", []byte("password")) acct, err := a.Account().CreateLocal(c.Context, "testington", "no@mercury.example", []byte("password"))

View file

@ -39,7 +39,10 @@ func run(c *cli.Context) error {
return errors.Wrap(err, "creating postgres database") return errors.Wrap(err, "creating postgres database")
} }
a := app.NewApp(cfg, db) a, err := app.NewApp(cfg, db)
if err != nil {
return errors.Wrap(err, "creating app")
}
log.Debug().Msg("Mounting routes") log.Debug().Msg("Mounting routes")
web.Routes(a) web.Routes(a)

View file

@ -13,10 +13,14 @@
"@sveltejs/vite-plugin-svelte": "^2.4.2", "@sveltejs/vite-plugin-svelte": "^2.4.2",
"@tsconfig/svelte": "^5.0.0", "@tsconfig/svelte": "^5.0.0",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"sass": "^1.66.1",
"svelte": "^4.0.5", "svelte": "^4.0.5",
"svelte-check": "^3.4.6", "svelte-check": "^3.4.6",
"tslib": "^2.6.0", "tslib": "^2.6.0",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.4.5" "vite": "^4.4.5"
},
"dependencies": {
"normalize.css": "^8.0.1"
} }
} }

View file

@ -1,5 +1,10 @@
lockfileVersion: "6.0" lockfileVersion: "6.0"
dependencies:
normalize.css:
specifier: ^8.0.1
version: 8.0.1
devDependencies: devDependencies:
"@sveltejs/vite-plugin-svelte": "@sveltejs/vite-plugin-svelte":
specifier: ^2.4.2 specifier: ^2.4.2
@ -10,12 +15,15 @@ devDependencies:
prettier: prettier:
specifier: ^3.0.3 specifier: ^3.0.3
version: 3.0.3 version: 3.0.3
sass:
specifier: ^1.66.1
version: 1.66.1
svelte: svelte:
specifier: ^4.0.5 specifier: ^4.0.5
version: 4.0.5 version: 4.0.5
svelte-check: svelte-check:
specifier: ^3.4.6 specifier: ^3.4.6
version: 3.4.6(svelte@4.0.5) version: 3.4.6(sass@1.66.1)(svelte@4.0.5)
tslib: tslib:
specifier: ^2.6.0 specifier: ^2.6.0
version: 2.6.0 version: 2.6.0
@ -24,7 +32,7 @@ devDependencies:
version: 5.0.2 version: 5.0.2
vite: vite:
specifier: ^4.4.5 specifier: ^4.4.5
version: 4.4.5 version: 4.4.5(sass@1.66.1)
packages: packages:
/@ampproject/remapping@2.2.1: /@ampproject/remapping@2.2.1:
@ -391,7 +399,7 @@ packages:
"@sveltejs/vite-plugin-svelte": 2.4.2(svelte@4.0.5)(vite@4.4.5) "@sveltejs/vite-plugin-svelte": 2.4.2(svelte@4.0.5)(vite@4.4.5)
debug: 4.3.4 debug: 4.3.4
svelte: 4.0.5 svelte: 4.0.5
vite: 4.4.5 vite: 4.4.5(sass@1.66.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -413,7 +421,7 @@ packages:
magic-string: 0.30.3 magic-string: 0.30.3
svelte: 4.0.5 svelte: 4.0.5
svelte-hmr: 0.15.3(svelte@4.0.5) svelte-hmr: 0.15.3(svelte@4.0.5)
vite: 4.4.5 vite: 4.4.5(sass@1.66.1)
vitefu: 0.2.4(vite@4.4.5) vitefu: 0.2.4(vite@4.4.5)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -747,6 +755,13 @@ packages:
} }
dev: true dev: true
/immutable@4.3.4:
resolution:
{
integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==,
}
dev: true
/import-fresh@3.3.0: /import-fresh@3.3.0:
resolution: resolution:
{ {
@ -947,6 +962,13 @@ packages:
engines: { node: ">=0.10.0" } engines: { node: ">=0.10.0" }
dev: true dev: true
/normalize.css@8.0.1:
resolution:
{
integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==,
}
dev: false
/once@1.4.0: /once@1.4.0:
resolution: resolution:
{ {
@ -1106,6 +1128,19 @@ packages:
rimraf: 2.7.1 rimraf: 2.7.1
dev: true dev: true
/sass@1.66.1:
resolution:
{
integrity: sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==,
}
engines: { node: ">=14.0.0" }
hasBin: true
dependencies:
chokidar: 3.5.3
immutable: 4.3.4
source-map-js: 1.0.2
dev: true
/sorcery@0.11.0: /sorcery@0.11.0:
resolution: resolution:
{ {
@ -1137,7 +1172,7 @@ packages:
min-indent: 1.0.1 min-indent: 1.0.1
dev: true dev: true
/svelte-check@3.4.6(svelte@4.0.5): /svelte-check@3.4.6(sass@1.66.1)(svelte@4.0.5):
resolution: resolution:
{ {
integrity: sha512-OBlY8866Zh1zHQTkBMPS6psPi7o2umTUyj6JWm4SacnIHXpWFm658pG32m3dKvKFL49V4ntAkfFHKo4ztH07og==, integrity: sha512-OBlY8866Zh1zHQTkBMPS6psPi7o2umTUyj6JWm4SacnIHXpWFm658pG32m3dKvKFL49V4ntAkfFHKo4ztH07og==,
@ -1153,7 +1188,7 @@ packages:
picocolors: 1.0.0 picocolors: 1.0.0
sade: 1.8.1 sade: 1.8.1
svelte: 4.0.5 svelte: 4.0.5
svelte-preprocess: 5.0.4(svelte@4.0.5)(typescript@5.2.2) svelte-preprocess: 5.0.4(sass@1.66.1)(svelte@4.0.5)(typescript@5.2.2)
typescript: 5.2.2 typescript: 5.2.2
transitivePeerDependencies: transitivePeerDependencies:
- "@babel/core" - "@babel/core"
@ -1179,7 +1214,7 @@ packages:
svelte: 4.0.5 svelte: 4.0.5
dev: true dev: true
/svelte-preprocess@5.0.4(svelte@4.0.5)(typescript@5.2.2): /svelte-preprocess@5.0.4(sass@1.66.1)(svelte@4.0.5)(typescript@5.2.2):
resolution: resolution:
{ {
integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==, integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==,
@ -1223,6 +1258,7 @@ packages:
"@types/pug": 2.0.6 "@types/pug": 2.0.6
detect-indent: 6.1.0 detect-indent: 6.1.0
magic-string: 0.27.0 magic-string: 0.27.0
sass: 1.66.1
sorcery: 0.11.0 sorcery: 0.11.0
strip-indent: 3.0.0 strip-indent: 3.0.0
svelte: 4.0.5 svelte: 4.0.5
@ -1286,7 +1322,7 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/vite@4.4.5: /vite@4.4.5(sass@1.66.1):
resolution: resolution:
{ {
integrity: sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==, integrity: sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==,
@ -1320,6 +1356,7 @@ packages:
esbuild: 0.18.20 esbuild: 0.18.20
postcss: 8.4.29 postcss: 8.4.29
rollup: 3.28.1 rollup: 3.28.1
sass: 1.66.1
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true dev: true
@ -1335,7 +1372,7 @@ packages:
vite: vite:
optional: true optional: true
dependencies: dependencies:
vite: 4.4.5 vite: 4.4.5(sass@1.66.1)
dev: true dev: true
/wrappy@1.0.2: /wrappy@1.0.2:

1
go.mod
View file

@ -5,6 +5,7 @@ go 1.19
require ( require (
emperror.dev/errors v0.8.1 emperror.dev/errors v0.8.1
github.com/BurntSushi/toml v1.2.1 github.com/BurntSushi/toml v1.2.1
github.com/flosch/pongo2/v6 v6.0.0
github.com/georgysavva/scany/v2 v2.0.0 github.com/georgysavva/scany/v2 v2.0.0
github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/render v1.0.2 github.com/go-chi/render v1.0.2

2
go.sum
View file

@ -102,6 +102,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU=
github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=

16
package.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "mercury",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch:style": "sass -I . -w assets/scss/:web/frontend/assets/css/ --style compressed"
},
"keywords": [],
"author": "sam <sam@sleepycat.moe>",
"license": "APGL-3.0-only",
"devDependencies": {
"normalize.css": "^8.0.1",
"sass": "^1.66.1"
}
}

1028
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

3
pnpm-workspace.yaml Normal file
View file

@ -0,0 +1,3 @@
packages:
- .
- frontend

View file

@ -1,8 +1,11 @@
package app package app
import ( import (
"emperror.dev/errors"
"git.sleepycat.moe/sam/mercury/config" "git.sleepycat.moe/sam/mercury/config"
"git.sleepycat.moe/sam/mercury/internal/database/sql" "git.sleepycat.moe/sam/mercury/internal/database/sql"
"git.sleepycat.moe/sam/mercury/web/templates"
"github.com/flosch/pongo2/v6"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
) )
@ -12,19 +15,27 @@ type App struct {
Config config.Config Config config.Config
Database *sql.Base Database *sql.Base
tmpl *pongo2.TemplateSet
} }
func NewApp(cfg config.Config, db *sql.Base) *App { func NewApp(cfg config.Config, db *sql.Base) (*App, error) {
app := &App{ app := &App{
Router: chi.NewRouter(), Router: chi.NewRouter(),
Config: cfg, Config: cfg,
Database: db, Database: db,
} }
tmpl, err := templates.New(cfg.Core.Dev)
if err != nil {
return nil, errors.Wrap(err, "creating templates")
}
app.tmpl = tmpl
app.Router.Use(app.Logger) app.Router.Use(app.Logger)
app.Router.Use(middleware.Recoverer) app.Router.Use(middleware.Recoverer)
return app return app, nil
} }
func (a *App) Account(q ...sql.Querier) *sql.AccountStore { func (a *App) Account(q ...sql.Querier) *sql.AccountStore {

52
web/app/template.go Normal file
View file

@ -0,0 +1,52 @@
package app
import (
"net/http"
"time"
"github.com/flosch/pongo2/v6"
)
func (app *App) Template(w http.ResponseWriter, r *http.Request, tmplName string, ctx pongo2.Context) error {
tmpl, err := app.tmpl.FromCache(tmplName)
if err != nil {
return err
}
tctx := pongo2.Context{
"flash_message": app.getFlash(w, r),
}
tctx.Update(ctx)
w.Header().Set("Content-Type", "text/html")
return tmpl.ExecuteWriter(tctx, w)
}
const flashCookieName = "mercury-flash-message"
func (app *App) Flash(w http.ResponseWriter, msg string) {
http.SetCookie(w, &http.Cookie{
Name: flashCookieName,
Value: msg,
Path: "/",
HttpOnly: true,
Expires: time.Now().Add(time.Minute),
})
}
func (app *App) getFlash(w http.ResponseWriter, r *http.Request) string {
cookie, err := r.Cookie(flashCookieName)
if err != nil {
return ""
}
defer http.SetCookie(w, &http.Cookie{
Name: flashCookieName,
Value: "",
Path: "/",
HttpOnly: true,
Expires: time.Now(),
})
return cookie.Value
}

15
web/auth/auth.go Normal file
View file

@ -0,0 +1,15 @@
package auth
import "git.sleepycat.moe/sam/mercury/web/app"
type Auth struct {
*app.App
}
func New(app *app.App) *Auth {
auth := &Auth{
App: app,
}
return auth
}

11
web/auth/login.go Normal file
View file

@ -0,0 +1,11 @@
package auth
import (
"net/http"
"github.com/flosch/pongo2/v6"
)
func (app *Auth) GetLogin(w http.ResponseWriter, r *http.Request) {
app.Template(w, r, "auth/login.tpl", pongo2.Context{})
}

1
web/frontend/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
assets

View file

@ -1,8 +1,8 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{.Config.Name}}</title> <title>{{.Config.Name}}</title>
{{.Vue.RenderTags}} {{.Vue.RenderTags}}

View file

@ -1,22 +1,26 @@
package frontend package frontend
import ( import (
"embed"
"html/template" "html/template"
"net/http" "net/http"
"os" "os"
"path/filepath"
"git.sleepycat.moe/sam/mercury/frontend" "git.sleepycat.moe/sam/mercury/frontend"
"git.sleepycat.moe/sam/mercury/internal/database" "git.sleepycat.moe/sam/mercury/internal/database"
"git.sleepycat.moe/sam/mercury/web/app" "git.sleepycat.moe/sam/mercury/web/app"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
vueglue "github.com/torenware/vite-go" vueglue "github.com/torenware/vite-go"
_ "embed"
) )
//go:embed app.html //go:embed app.html
var htmlTemplate string var htmlTemplate string
//go:embed assets
var assets embed.FS
type Frontend struct { type Frontend struct {
*app.App *app.App
glue *vueglue.VueGlue glue *vueglue.VueGlue
@ -94,3 +98,14 @@ func (app *Frontend) ServeFrontend(w http.ResponseWriter, r *http.Request) {
log.Err(err).Msg("executing frontend template") log.Err(err).Msg("executing frontend template")
} }
} }
func (app *Frontend) ServeStaticAssets(w http.ResponseWriter, r *http.Request) {
if app.Config.Core.Dev {
// TODO: this is unsafe
path := filepath.Join("web/frontend/assets/", chi.URLParam(r, "*"))
http.ServeFile(w, r, path)
return
}
_ = assets
}

View file

@ -2,12 +2,23 @@ package web
import ( import (
"git.sleepycat.moe/sam/mercury/web/app" "git.sleepycat.moe/sam/mercury/web/app"
"git.sleepycat.moe/sam/mercury/web/auth"
"git.sleepycat.moe/sam/mercury/web/frontend" "git.sleepycat.moe/sam/mercury/web/frontend"
"github.com/go-chi/chi/v5"
) )
func Routes(app *app.App) { func Routes(app *app.App) {
// auth
app.Router.Route("/auth", func(r chi.Router) {
auth := auth.New(app)
r.Get("/login", auth.GetLogin)
})
// web app handlers
// also assets
frontend := frontend.New(app) frontend := frontend.New(app)
app.Router.HandleFunc(frontend.AssetsPath(), frontend.ServeAssets) app.Router.HandleFunc(frontend.AssetsPath(), frontend.ServeAssets)
app.Router.HandleFunc("/static/*", frontend.ServeStaticAssets)
app.Router.HandleFunc("/web", frontend.ServeFrontend) app.Router.HandleFunc("/web", frontend.ServeFrontend)
app.Router.HandleFunc("/web/*", frontend.ServeFrontend) app.Router.HandleFunc("/web/*", frontend.ServeFrontend)
} }

View file

@ -0,0 +1,19 @@
{% extends 'base.tpl' %}
{% block title %}
Log in
{% endblock %}
{% block content %}
<div class="auth">
<form method="post">
<p>
<label for="username">Username</label>
<input type="text" name="username" />
</p>
<p>
<label for="username">Password</label>
<input type="password" name="password" />
</p>
<input type="submit" value="Log in" />
</form>
</div>
{% endblock %}

13
web/templates/base.tpl Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<title>{% block title %}Mercury{% endblock %}</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>

View file

@ -0,0 +1,28 @@
package templates
import (
"embed"
"emperror.dev/errors"
"github.com/flosch/pongo2/v6"
)
//go:embed *
var fs embed.FS
func New(dev bool) (*pongo2.TemplateSet, error) {
if dev {
loader, err := pongo2.NewLocalFileSystemLoader("web/templates")
if err != nil {
return nil, errors.Wrap(err, "creating filesystem loader")
}
ts := pongo2.NewSet("web", loader)
ts.Debug = true
return ts, nil
}
loader := pongo2.NewFSLoader(fs)
ts := pongo2.NewSet("web", loader)
return ts, nil
}