feat: new admin and web apps
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,4 +2,4 @@ node_modules/
|
|||||||
.dist/
|
.dist/
|
||||||
*_templ.go
|
*_templ.go
|
||||||
*.env
|
*.env
|
||||||
./assets/css/uno.css
|
uno.css
|
||||||
|
|||||||
123
app/app.go
Normal file
123
app/app.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"forge.capytal.company/capytalcode/project-comicverse/lib/middleware"
|
||||||
|
"forge.capytal.company/capytalcode/project-comicverse/lib/router"
|
||||||
|
"forge.capytal.company/capytalcode/project-comicverse/lib/router/rerrors"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"keikos.work/configs"
|
||||||
|
"keikos.work/handlers/pages"
|
||||||
|
)
|
||||||
|
|
||||||
|
type App interface {
|
||||||
|
Start() error
|
||||||
|
Stop() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
Port uint
|
||||||
|
Development bool
|
||||||
|
Log *slog.Logger
|
||||||
|
|
||||||
|
S3 *minio.Client
|
||||||
|
|
||||||
|
Assets http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
type app struct {
|
||||||
|
log *slog.Logger
|
||||||
|
|
||||||
|
s3 *minio.Client
|
||||||
|
assets http.Handler
|
||||||
|
|
||||||
|
server *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *app) Start() error {
|
||||||
|
if err := a.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *app) Stop() error {
|
||||||
|
if err := a.server.Shutdown(context.Background()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWeb(opts Options) App {
|
||||||
|
r := router.NewRouter()
|
||||||
|
|
||||||
|
r.Use(middleware.NewLoggerMiddleware(opts.Log))
|
||||||
|
|
||||||
|
if opts.Development {
|
||||||
|
configs.DEVELOPMENT = true
|
||||||
|
r.Use(middleware.DevMiddleware)
|
||||||
|
} else {
|
||||||
|
r.Use(middleware.CacheMiddleware)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Use(rerrors.NewErrorMiddleware(pages.ErrorPage{}.Component, opts.Log))
|
||||||
|
|
||||||
|
r.Handle("/", &pages.Home{})
|
||||||
|
|
||||||
|
imgs := &pages.Images{S3: opts.S3}
|
||||||
|
r.HandleFunc("GET /images", imgs.List)
|
||||||
|
r.HandleFunc("GET /images/{name}", imgs.Get)
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", opts.Port),
|
||||||
|
Handler: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &app{
|
||||||
|
log: opts.Log,
|
||||||
|
s3: opts.S3,
|
||||||
|
assets: opts.Assets,
|
||||||
|
server: &srv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdmin(opts Options) App {
|
||||||
|
r := router.NewRouter()
|
||||||
|
|
||||||
|
r.Use(middleware.NewLoggerMiddleware(opts.Log))
|
||||||
|
|
||||||
|
if opts.Development {
|
||||||
|
configs.DEVELOPMENT = true
|
||||||
|
r.Use(middleware.DevMiddleware)
|
||||||
|
} else {
|
||||||
|
r.Use(middleware.CacheMiddleware)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Use(rerrors.NewErrorMiddleware(pages.ErrorPage{}.Component, opts.Log))
|
||||||
|
|
||||||
|
r.Handle("/", &pages.Dashboard{})
|
||||||
|
|
||||||
|
imgs := &pages.Images{S3: opts.S3}
|
||||||
|
r.HandleFunc("GET /images", imgs.List)
|
||||||
|
r.HandleFunc("GET /images/{name}", imgs.Get)
|
||||||
|
r.HandleFunc("POST /images", imgs.Create)
|
||||||
|
r.HandleFunc("PUT /images/{name}", imgs.Update)
|
||||||
|
r.HandleFunc("DELETE /images/{name}", imgs.Delete)
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", opts.Port),
|
||||||
|
Handler: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &app{
|
||||||
|
log: opts.Log,
|
||||||
|
s3: opts.S3,
|
||||||
|
assets: opts.Assets,
|
||||||
|
server: &srv,
|
||||||
|
}
|
||||||
|
}
|
||||||
78
cmd/main.go
78
cmd/main.go
@@ -1,78 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"forge.capytal.company/capytalcode/project-comicverse/lib/middleware"
|
|
||||||
"forge.capytal.company/capytalcode/project-comicverse/lib/router"
|
|
||||||
"keikos.work/assets"
|
|
||||||
"keikos.work/configs"
|
|
||||||
"keikos.work/handlers/pages"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
port int64 = 8080
|
|
||||||
dev bool = false
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.Int64Var(&port, "port", port, "The website port")
|
|
||||||
flag.BoolVar(&dev, "dev", dev, "Run the website in development mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
l := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
|
||||||
Level: slog.LevelDebug,
|
|
||||||
}))
|
|
||||||
|
|
||||||
if dev {
|
|
||||||
l.Info("RUNNING IN DEVELOPMENT MODE")
|
|
||||||
configs.DEVELOPMENT = true
|
|
||||||
}
|
|
||||||
|
|
||||||
r := router.NewRouter()
|
|
||||||
|
|
||||||
r.Use(middleware.NewLoggerMiddleware(l).Wrap)
|
|
||||||
|
|
||||||
if configs.DEVELOPMENT {
|
|
||||||
r.Use(middleware.DevMiddleware)
|
|
||||||
r.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))
|
|
||||||
} else {
|
|
||||||
r.Use(middleware.CacheMiddleware)
|
|
||||||
r.Handle("/assets/", http.StripPrefix("/assets/", http.FileServerFS(assets.ASSETS)))
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Handle("/", pages.Routes())
|
|
||||||
|
|
||||||
srv := http.Server{
|
|
||||||
Addr: fmt.Sprintf(":%d", port),
|
|
||||||
Handler: r,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
defer stop()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
l.Error("Listen and serve returned error", slog.String("error", err.Error()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-ctx.Done()
|
|
||||||
l.Info("Shutting down server...")
|
|
||||||
if err := srv.Shutdown(context.Background()); err != nil {
|
|
||||||
l.Error("Server shutdown returned error", slog.String("error", err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Info("FINAL")
|
|
||||||
}
|
|
||||||
24
go.mod
24
go.mod
@@ -2,6 +2,26 @@ module keikos.work
|
|||||||
|
|
||||||
go 1.23.3
|
go 1.23.3
|
||||||
|
|
||||||
require forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241030133525-b14b0be66b7a
|
require (
|
||||||
|
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241213195940-67230ba75d8a
|
||||||
|
github.com/a-h/templ v0.2.793
|
||||||
|
github.com/minio/minio-go/v7 v7.0.81
|
||||||
|
)
|
||||||
|
|
||||||
require github.com/a-h/templ v0.2.793 // indirect
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.4 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/rs/xid v1.6.0 // indirect
|
||||||
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
|
golang.org/x/net v0.32.0 // indirect
|
||||||
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|||||||
44
go.sum
44
go.sum
@@ -1,4 +1,44 @@
|
|||||||
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241030133525-b14b0be66b7a h1:lDGMhHL+B5zmrxiSEVPLNXglPApksQ7HPEAEGOuW7sI=
|
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241213192147-c2bbd80dcef8 h1:Jp0qWzdYZesWkcsH19sdsiiODKxsICY26oTwy2e+rmg=
|
||||||
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241030133525-b14b0be66b7a/go.mod h1:COqB9i9nyQrQUz/ZqMvN98V7rh0t7vdXmBV36G8Q+7Q=
|
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241213192147-c2bbd80dcef8/go.mod h1:COqB9i9nyQrQUz/ZqMvN98V7rh0t7vdXmBV36G8Q+7Q=
|
||||||
|
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241213195940-67230ba75d8a h1:PDmA2uCzFdn+CdS3C+Pg1YGhJERgMjwgCjUEUaIDZTM=
|
||||||
|
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241213195940-67230ba75d8a/go.mod h1:COqB9i9nyQrQUz/ZqMvN98V7rh0t7vdXmBV36G8Q+7Q=
|
||||||
github.com/a-h/templ v0.2.793 h1:Io+/ocnfGWYO4VHdR0zBbf39PQlnzVCVVD+wEEs6/qY=
|
github.com/a-h/templ v0.2.793 h1:Io+/ocnfGWYO4VHdR0zBbf39PQlnzVCVVD+wEEs6/qY=
|
||||||
github.com/a-h/templ v0.2.793/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
|
github.com/a-h/templ v0.2.793/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||||
|
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
||||||
|
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||||
|
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
|
github.com/minio/minio-go/v7 v7.0.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA=
|
||||||
|
github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
|
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||||
|
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||||
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
25
handlers/pages/dashboard.templ
Normal file
25
handlers/pages/dashboard.templ
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"forge.capytal.company/capytalcode/project-comicverse/lib/router/rerrors"
|
||||||
|
|
||||||
|
"keikos.work/templates/layouts"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dashboard struct{}
|
||||||
|
|
||||||
|
templ (p *Dashboard) Component() {
|
||||||
|
@layouts.Page() {
|
||||||
|
<h1>Dashboard</h1>
|
||||||
|
<h2>Images</h2>
|
||||||
|
<form action="/images" method="post"></form>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Dashboard) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := p.Component().Render(r.Context(), w); err != nil {
|
||||||
|
rerrors.InternalError(err).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
handlers/pages/error.templ
Normal file
26
handlers/pages/error.templ
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"keikos.work/templates/layouts"
|
||||||
|
|
||||||
|
"forge.capytal.company/capytalcode/project-comicverse/lib/router/rerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorPage struct{}
|
||||||
|
|
||||||
|
templ (p ErrorPage) Component(err rerrors.RouteError) {
|
||||||
|
@layouts.Page() {
|
||||||
|
<main>
|
||||||
|
<h1>Error</h1>
|
||||||
|
<p>{ fmt.Sprintf("%#v", err) }</p>
|
||||||
|
for k, v := range err.Info {
|
||||||
|
<p>{ k } { fmt.Sprint(v) } </p>
|
||||||
|
}
|
||||||
|
if err.Endpoint != "" {
|
||||||
|
<a href={ templ.SafeURL(err.Endpoint) }>Retry</a>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,20 +7,29 @@ import (
|
|||||||
"keikos.work/templates/layouts"
|
"keikos.work/templates/layouts"
|
||||||
)
|
)
|
||||||
|
|
||||||
type home struct{}
|
type Home struct{}
|
||||||
|
|
||||||
func (p *home) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (p *Home) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := p.page().Render(r.Context(), w); err != nil {
|
if err := p.page().Render(r.Context(), w); err != nil {
|
||||||
rerrors.InternalError(err).ServeHTTP(w, r)
|
rerrors.InternalError(err).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templ (p *home) page() {
|
templ (p *Home) page() {
|
||||||
@layouts.Page() {
|
@layouts.Page() {
|
||||||
<section class="fixed z-1 w-screen h-100lvh top-0 left-0 flex justify-center items-center">
|
<section
|
||||||
<div class="overflow-x-hidden relative">
|
class="fixed z-1 w-screen h-100lvh top-0 left-0 flex justify-center items-center"
|
||||||
<span class="-translate-x-20% block">
|
>
|
||||||
<img src="/assets/img/2024-09-03.jpg" class="h-100vh animate-fade-in-right animate-ease-out"/>
|
<div
|
||||||
|
class="overflow-x-hidden relative"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="-translate-x-20% block"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/assets/img/2024-09-03.jpg"
|
||||||
|
class="h-100vh animate-fade-in-right animate-ease-out"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class={ "absolute top-0 left-0 w-100% h-100%",
|
class={ "absolute top-0 left-0 w-100% h-100%",
|
||||||
|
|||||||
51
handlers/pages/images.templ
Normal file
51
handlers/pages/images.templ
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"keikos.work/templates/layouts"
|
||||||
|
|
||||||
|
// "forge.capytal.company/capytalcode/project-comicverse/lib/router/rerrors"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Images struct {
|
||||||
|
S3 *minio.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Images) List(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Images) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Images) Create(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Images) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Images) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
templ (p *Images) heading() {
|
||||||
|
<script type="module" src="/assets/js/Images_form.js"></script>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ (p *Images) Component() {
|
||||||
|
@layouts.Page(layouts.PageInfo{
|
||||||
|
Heading: p.heading(),
|
||||||
|
}) {
|
||||||
|
<img id="file-preview" src="" class="bg-gray min-h-20rem min-w-20rem max-w-full"/>
|
||||||
|
<form action="/Images" method="post" enctype="multipart/form-data">
|
||||||
|
<label for="file">File to upload</label>
|
||||||
|
<input type="file" id="file" name="file"/>
|
||||||
|
<button type="submit">Upload</button>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package pages
|
|
||||||
|
|
||||||
import "forge.capytal.company/capytalcode/project-comicverse/lib/router"
|
|
||||||
|
|
||||||
func Routes() router.Router {
|
|
||||||
r := router.NewRouter()
|
|
||||||
|
|
||||||
r.Handle("/", &home{})
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
161
main.go
Normal file
161
main.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
|
"keikos.work/app"
|
||||||
|
"keikos.work/assets"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
port uint = 8080
|
||||||
|
dev bool = false
|
||||||
|
|
||||||
|
webFlags *flag.FlagSet = nil
|
||||||
|
adminFlags *flag.FlagSet = nil
|
||||||
|
|
||||||
|
minioAccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID")
|
||||||
|
minioSecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||||
|
minioEndpoint = os.Getenv("AWS_ENDPOINT_URL")
|
||||||
|
minioSSL = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
webFlags = flag.NewFlagSet("web", flag.ExitOnError)
|
||||||
|
|
||||||
|
webFlags.UintVar(&port, "port", 8080, "The port to be used")
|
||||||
|
webFlags.BoolVar(&dev, "dev", false, "Run the server in development mode")
|
||||||
|
|
||||||
|
webFlags.StringVar(&minioAccessKeyID, "aws-access-key-id", os.Getenv("AWS_ACCESS_KEY_ID"), "")
|
||||||
|
webFlags.StringVar(
|
||||||
|
&minioSecretAccessKey,
|
||||||
|
"aws-access-key-secret",
|
||||||
|
os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
webFlags.StringVar(&minioEndpoint, "aws-enpoint", os.Getenv("AWS_ENDPOINT_URL"), "")
|
||||||
|
webFlags.BoolVar(&minioSSL, "aws-ssl", false, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
adminFlags = flag.NewFlagSet("admin", flag.ExitOnError)
|
||||||
|
|
||||||
|
adminFlags.UintVar(&port, "port", 8080, "The port to be used")
|
||||||
|
adminFlags.BoolVar(&dev, "dev", false, "Run the server in development mode")
|
||||||
|
|
||||||
|
adminFlags.StringVar(&minioAccessKeyID, "aws-access-key-id", os.Getenv("AWS_ACCESS_KEY_ID"), "")
|
||||||
|
adminFlags.StringVar(
|
||||||
|
&minioSecretAccessKey,
|
||||||
|
"aws-access-key-secret",
|
||||||
|
os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
adminFlags.StringVar(&minioEndpoint, "aws-enpoint", os.Getenv("AWS_ENDPOINT_URL"), "")
|
||||||
|
adminFlags.BoolVar(&minioSSL, "aws-ssl", false, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
log.Fatalf("Expected 'web' or 'admin' subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
s3, err := minio.New(minioEndpoint, &minio.Options{
|
||||||
|
Creds: credentials.NewStaticV4(minioAccessKeyID, minioSecretAccessKey, ""),
|
||||||
|
Secure: minioSSL,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var a app.App
|
||||||
|
var l *slog.Logger
|
||||||
|
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "web":
|
||||||
|
webFlags.Parse(os.Args[2:])
|
||||||
|
|
||||||
|
var level slog.Leveler
|
||||||
|
if dev {
|
||||||
|
level = slog.LevelDebug
|
||||||
|
} else {
|
||||||
|
level = slog.LevelInfo
|
||||||
|
}
|
||||||
|
l = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level}))
|
||||||
|
|
||||||
|
var assetsHandler http.Handler
|
||||||
|
if dev {
|
||||||
|
assetsHandler = http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets")))
|
||||||
|
} else {
|
||||||
|
assetsHandler = http.StripPrefix("/assets/", http.FileServerFS(assets.ASSETS))
|
||||||
|
}
|
||||||
|
|
||||||
|
a = app.NewWeb(app.Options{
|
||||||
|
Port: port,
|
||||||
|
Development: dev,
|
||||||
|
Log: l,
|
||||||
|
|
||||||
|
S3: s3,
|
||||||
|
|
||||||
|
Assets: assetsHandler,
|
||||||
|
})
|
||||||
|
case "admin":
|
||||||
|
adminFlags.Parse(os.Args[2:])
|
||||||
|
|
||||||
|
var level slog.Leveler
|
||||||
|
if dev {
|
||||||
|
level = slog.LevelDebug
|
||||||
|
} else {
|
||||||
|
level = slog.LevelInfo
|
||||||
|
}
|
||||||
|
l = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level}))
|
||||||
|
|
||||||
|
var assetsHandler http.Handler
|
||||||
|
if dev {
|
||||||
|
assetsHandler = http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets")))
|
||||||
|
} else {
|
||||||
|
assetsHandler = http.StripPrefix("/assets/", http.FileServerFS(assets.ASSETS))
|
||||||
|
}
|
||||||
|
|
||||||
|
a = app.NewAdmin(app.Options{
|
||||||
|
Port: port,
|
||||||
|
Development: dev,
|
||||||
|
Log: l,
|
||||||
|
|
||||||
|
S3: s3,
|
||||||
|
|
||||||
|
Assets: assetsHandler,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
log.Fatalf("Command %q is not available", os.Args[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
if dev {
|
||||||
|
l.Info("RUNNING IN DEVELOPMENT MODE")
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := a.Start(); err != nil {
|
||||||
|
l.Error("Failed to start server", slog.String("error", err.Error()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
l.Info("Shutting down")
|
||||||
|
if err := a.Stop(); err != nil {
|
||||||
|
l.Error("Failed to shut down", slog.String("error", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Info("FINAL")
|
||||||
|
}
|
||||||
36
makefile
36
makefile
@@ -21,15 +21,25 @@ dev/templ:
|
|||||||
--proxybind="0.0.0.0" \
|
--proxybind="0.0.0.0" \
|
||||||
--open-browser=false
|
--open-browser=false
|
||||||
|
|
||||||
dev/server:
|
dev/server/web:
|
||||||
go run github.com/air-verse/air@v1.52.2 \
|
go run github.com/air-verse/air@v1.52.2 \
|
||||||
--build.cmd "go build -o tmp/bin/main ./cmd" \
|
--build.cmd "go build -o tmp/bin/main ." \
|
||||||
--build.bin "tmp/bin/main" \
|
--build.bin "tmp/bin/main" \
|
||||||
--build.exclude_dir "node_modules" \
|
--build.exclude_dir "node_modules" \
|
||||||
--build.include_ext "go" \
|
--build.include_ext "go" \
|
||||||
--build.stop_on_error "false" \
|
--build.stop_on_error "false" \
|
||||||
--misc.clean_on_exit true \
|
--misc.clean_on_exit true \
|
||||||
-- -dev -port $(PORT)
|
-- web -dev -port $(PORT)
|
||||||
|
|
||||||
|
dev/server/admin:
|
||||||
|
go run github.com/air-verse/air@v1.52.2 \
|
||||||
|
--build.cmd "go build -o tmp/bin/main ." \
|
||||||
|
--build.bin "tmp/bin/main" \
|
||||||
|
--build.exclude_dir "node_modules" \
|
||||||
|
--build.include_ext "go" \
|
||||||
|
--build.stop_on_error "false" \
|
||||||
|
--misc.clean_on_exit true \
|
||||||
|
-- admin -dev -port $(PORT)
|
||||||
|
|
||||||
dev/sync_assets:
|
dev/sync_assets:
|
||||||
go run github.com/air-verse/air@v1.52.2 \
|
go run github.com/air-verse/air@v1.52.2 \
|
||||||
@@ -43,23 +53,33 @@ dev/sync_assets:
|
|||||||
dev/assets/css:
|
dev/assets/css:
|
||||||
bun x unocss --watch
|
bun x unocss --watch
|
||||||
|
|
||||||
dev:
|
dev/web:
|
||||||
go run github.com/joho/godotenv/cmd/godotenv@v1.5.1 \
|
go run github.com/joho/godotenv/cmd/godotenv@v1.5.1 \
|
||||||
make -j4 dev/templ dev/server dev/sync_assets dev/assets/css
|
make -j4 dev/templ dev/server/web dev/sync_assets dev/assets/css
|
||||||
|
|
||||||
|
dev/admin:
|
||||||
|
go run github.com/joho/godotenv/cmd/godotenv@v1.5.1 \
|
||||||
|
make -j4 dev/templ dev/server/admin dev/sync_assets dev/assets/css
|
||||||
|
|
||||||
build/templ:
|
build/templ:
|
||||||
go run github.com/a-h/templ/cmd/templ@v0.2.707 generate
|
go run github.com/a-h/templ/cmd/templ@v0.2.707 generate
|
||||||
|
|
||||||
build/bin:
|
build/bin:
|
||||||
go build -o ./.dist/bin ./cmd
|
go build -o ./.dist/bin .
|
||||||
|
|
||||||
build/assets:
|
build/assets:
|
||||||
npx unocss
|
npx unocss
|
||||||
|
|
||||||
build: build/templ build/assets build/bin
|
build: build/templ build/assets build/bin
|
||||||
|
|
||||||
run: build
|
run/web: build
|
||||||
./.dist/bin
|
go run github.com/joho/godotenv/cmd/godotenv@v1.5.1 \
|
||||||
|
./.dist/bin web -port $(PORT)
|
||||||
|
|
||||||
|
run/admin: build
|
||||||
|
go run github.com/joho/godotenv/cmd/godotenv@v1.5.1 \
|
||||||
|
./.dist/bin admin -port $(PORT)
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
# Remove templ generated files
|
# Remove templ generated files
|
||||||
|
|||||||
142
uno.config.js
142
uno.config.js
@@ -1,76 +1,76 @@
|
|||||||
import {
|
import {
|
||||||
defineConfig,
|
defineConfig,
|
||||||
presetIcons,
|
presetIcons,
|
||||||
presetTypography,
|
presetTypography,
|
||||||
presetUno,
|
presetUno,
|
||||||
presetWebFonts,
|
presetWebFonts,
|
||||||
transformerDirectives,
|
transformerDirectives,
|
||||||
transformerVariantGroup,
|
transformerVariantGroup,
|
||||||
} from "unocss";
|
} from 'unocss';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
cli: {
|
cli: {
|
||||||
entry: {
|
entry: {
|
||||||
outFile: "./assets/css/uno.css",
|
outFile: './assets/css/uno.css',
|
||||||
patterns: [
|
patterns: [
|
||||||
"./{templates,handlers}/**/*.templ",
|
'./{templates,handlers}/**/*.templ',
|
||||||
"./assets/**/*.{js,css,html}",
|
'./assets/**/*.{js,css,html}',
|
||||||
"!./assets/css/uno.css",
|
'!./assets/css/uno.css',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
presets: [
|
presets: [
|
||||||
presetIcons(),
|
presetIcons(),
|
||||||
presetTypography(),
|
presetTypography(),
|
||||||
presetUno({
|
presetUno({
|
||||||
dark: "media",
|
dark: 'media',
|
||||||
}),
|
}),
|
||||||
presetWebFonts({
|
presetWebFonts({
|
||||||
fonts: {
|
fonts: {
|
||||||
japanese: {
|
japanese: {
|
||||||
name: "Kaisei Decol",
|
name: 'Kaisei Decol',
|
||||||
},
|
},
|
||||||
sans: {
|
sans: {
|
||||||
name: "Quattrocento Sans",
|
name: 'Quattrocento Sans',
|
||||||
},
|
},
|
||||||
serif: {
|
serif: {
|
||||||
name: "Quattrocento",
|
name: 'Quattrocento',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
provider: "none",
|
provider: 'none',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
rules: [
|
rules: [
|
||||||
["w-screen", [["width", "100vw"], ["width", "100dvw"]]],
|
['w-screen', [['width', '100vw'], ['width', '100dvw']]],
|
||||||
[/^w-(\d+)dvw$/, ([_, d]) => {
|
[/^w-(\d+)dvw$/, ([, d]) => {
|
||||||
return [
|
return [
|
||||||
["width", `${d}vw`],
|
['width', `${d}vw`],
|
||||||
["width", `${d}dvw`],
|
['width', `${d}dvw`],
|
||||||
];
|
];
|
||||||
}],
|
}],
|
||||||
["min-w-screen", [["width", "100vw"], ["width", "100dvw"]]],
|
['min-w-screen', [['width', '100vw'], ['width', '100dvw']]],
|
||||||
[/^w-(\d+)dvw$/, ([_, d]) => {
|
[/^w-(\d+)dvw$/, ([, d]) => {
|
||||||
return [
|
return [
|
||||||
["min-width", `${d}vw`],
|
['min-width', `${d}vw`],
|
||||||
["min-width", `${d}dvw`],
|
['min-width', `${d}dvw`],
|
||||||
];
|
];
|
||||||
}],
|
}],
|
||||||
["h-screen", [["height", "100vh"], ["height", "100dvh"]]],
|
['h-screen', [['height', '100vh'], ['height', '100dvh']]],
|
||||||
[/^h-(\d+)dvh$/, ([_, d]) => {
|
[/^h-(\d+)dvh$/, ([, d]) => {
|
||||||
return [
|
return [
|
||||||
["height", `${d}vh`],
|
['height', `${d}vh`],
|
||||||
["height", `${d}dvh`],
|
['height', `${d}dvh`],
|
||||||
];
|
];
|
||||||
}],
|
}],
|
||||||
["min-h-screen", [["height", "100vh"], ["height", "100dvh"]]],
|
['min-h-screen', [['height', '100vh'], ['height', '100dvh']]],
|
||||||
[/^h-(\d+)dvh$/, ([_, d]) => {
|
[/^h-(\d+)dvh$/, ([, d]) => {
|
||||||
return [
|
return [
|
||||||
["min-height", `${d}vh`],
|
['min-height', `${d}vh`],
|
||||||
["min-height", `${d}dvh`],
|
['min-height', `${d}dvh`],
|
||||||
];
|
];
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
/* theme: {
|
/* theme: {
|
||||||
colors: {
|
colors: {
|
||||||
white: 'var(--white)',
|
white: 'var(--white)',
|
||||||
black: 'var(--black)',
|
black: 'var(--black)',
|
||||||
@@ -87,5 +87,5 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, */
|
}, */
|
||||||
transformers: [transformerDirectives(), transformerVariantGroup()],
|
transformers: [transformerDirectives(), transformerVariantGroup()],
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user