feat: new admin and web apps
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,4 +2,4 @@ node_modules/
|
||||
.dist/
|
||||
*_templ.go
|
||||
*.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
|
||||
|
||||
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-20241030133525-b14b0be66b7a/go.mod h1:COqB9i9nyQrQUz/ZqMvN98V7rh0t7vdXmBV36G8Q+7Q=
|
||||
forge.capytal.company/capytalcode/project-comicverse v0.0.0-20241213192147-c2bbd80dcef8 h1:Jp0qWzdYZesWkcsH19sdsiiODKxsICY26oTwy2e+rmg=
|
||||
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/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"
|
||||
)
|
||||
|
||||
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 {
|
||||
rerrors.InternalError(err).ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
templ (p *home) page() {
|
||||
templ (p *Home) page() {
|
||||
@layouts.Page() {
|
||||
<section class="fixed z-1 w-screen h-100lvh top-0 left-0 flex justify-center items-center">
|
||||
<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"/>
|
||||
<section
|
||||
class="fixed z-1 w-screen h-100lvh top-0 left-0 flex justify-center items-center"
|
||||
>
|
||||
<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
|
||||
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" \
|
||||
--open-browser=false
|
||||
|
||||
dev/server:
|
||||
dev/server/web:
|
||||
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.exclude_dir "node_modules" \
|
||||
--build.include_ext "go" \
|
||||
--build.stop_on_error "false" \
|
||||
--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:
|
||||
go run github.com/air-verse/air@v1.52.2 \
|
||||
@@ -43,23 +53,33 @@ dev/sync_assets:
|
||||
dev/assets/css:
|
||||
bun x unocss --watch
|
||||
|
||||
dev:
|
||||
dev/web:
|
||||
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:
|
||||
go run github.com/a-h/templ/cmd/templ@v0.2.707 generate
|
||||
|
||||
build/bin:
|
||||
go build -o ./.dist/bin ./cmd
|
||||
go build -o ./.dist/bin .
|
||||
|
||||
build/assets:
|
||||
npx unocss
|
||||
|
||||
build: build/templ build/assets build/bin
|
||||
|
||||
run: build
|
||||
./.dist/bin
|
||||
run/web: build
|
||||
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:
|
||||
# Remove templ generated files
|
||||
|
||||
142
uno.config.js
142
uno.config.js
@@ -1,76 +1,76 @@
|
||||
import {
|
||||
defineConfig,
|
||||
presetIcons,
|
||||
presetTypography,
|
||||
presetUno,
|
||||
presetWebFonts,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from "unocss";
|
||||
defineConfig,
|
||||
presetIcons,
|
||||
presetTypography,
|
||||
presetUno,
|
||||
presetWebFonts,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss';
|
||||
|
||||
export default defineConfig({
|
||||
cli: {
|
||||
entry: {
|
||||
outFile: "./assets/css/uno.css",
|
||||
patterns: [
|
||||
"./{templates,handlers}/**/*.templ",
|
||||
"./assets/**/*.{js,css,html}",
|
||||
"!./assets/css/uno.css",
|
||||
],
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
presetIcons(),
|
||||
presetTypography(),
|
||||
presetUno({
|
||||
dark: "media",
|
||||
}),
|
||||
presetWebFonts({
|
||||
fonts: {
|
||||
japanese: {
|
||||
name: "Kaisei Decol",
|
||||
},
|
||||
sans: {
|
||||
name: "Quattrocento Sans",
|
||||
},
|
||||
serif: {
|
||||
name: "Quattrocento",
|
||||
},
|
||||
},
|
||||
provider: "none",
|
||||
}),
|
||||
],
|
||||
rules: [
|
||||
["w-screen", [["width", "100vw"], ["width", "100dvw"]]],
|
||||
[/^w-(\d+)dvw$/, ([_, d]) => {
|
||||
return [
|
||||
["width", `${d}vw`],
|
||||
["width", `${d}dvw`],
|
||||
];
|
||||
}],
|
||||
["min-w-screen", [["width", "100vw"], ["width", "100dvw"]]],
|
||||
[/^w-(\d+)dvw$/, ([_, d]) => {
|
||||
return [
|
||||
["min-width", `${d}vw`],
|
||||
["min-width", `${d}dvw`],
|
||||
];
|
||||
}],
|
||||
["h-screen", [["height", "100vh"], ["height", "100dvh"]]],
|
||||
[/^h-(\d+)dvh$/, ([_, d]) => {
|
||||
return [
|
||||
["height", `${d}vh`],
|
||||
["height", `${d}dvh`],
|
||||
];
|
||||
}],
|
||||
["min-h-screen", [["height", "100vh"], ["height", "100dvh"]]],
|
||||
[/^h-(\d+)dvh$/, ([_, d]) => {
|
||||
return [
|
||||
["min-height", `${d}vh`],
|
||||
["min-height", `${d}dvh`],
|
||||
];
|
||||
}],
|
||||
],
|
||||
/* theme: {
|
||||
cli: {
|
||||
entry: {
|
||||
outFile: './assets/css/uno.css',
|
||||
patterns: [
|
||||
'./{templates,handlers}/**/*.templ',
|
||||
'./assets/**/*.{js,css,html}',
|
||||
'!./assets/css/uno.css',
|
||||
],
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
presetIcons(),
|
||||
presetTypography(),
|
||||
presetUno({
|
||||
dark: 'media',
|
||||
}),
|
||||
presetWebFonts({
|
||||
fonts: {
|
||||
japanese: {
|
||||
name: 'Kaisei Decol',
|
||||
},
|
||||
sans: {
|
||||
name: 'Quattrocento Sans',
|
||||
},
|
||||
serif: {
|
||||
name: 'Quattrocento',
|
||||
},
|
||||
},
|
||||
provider: 'none',
|
||||
}),
|
||||
],
|
||||
rules: [
|
||||
['w-screen', [['width', '100vw'], ['width', '100dvw']]],
|
||||
[/^w-(\d+)dvw$/, ([, d]) => {
|
||||
return [
|
||||
['width', `${d}vw`],
|
||||
['width', `${d}dvw`],
|
||||
];
|
||||
}],
|
||||
['min-w-screen', [['width', '100vw'], ['width', '100dvw']]],
|
||||
[/^w-(\d+)dvw$/, ([, d]) => {
|
||||
return [
|
||||
['min-width', `${d}vw`],
|
||||
['min-width', `${d}dvw`],
|
||||
];
|
||||
}],
|
||||
['h-screen', [['height', '100vh'], ['height', '100dvh']]],
|
||||
[/^h-(\d+)dvh$/, ([, d]) => {
|
||||
return [
|
||||
['height', `${d}vh`],
|
||||
['height', `${d}dvh`],
|
||||
];
|
||||
}],
|
||||
['min-h-screen', [['height', '100vh'], ['height', '100dvh']]],
|
||||
[/^h-(\d+)dvh$/, ([, d]) => {
|
||||
return [
|
||||
['min-height', `${d}vh`],
|
||||
['min-height', `${d}dvh`],
|
||||
];
|
||||
}],
|
||||
],
|
||||
/* theme: {
|
||||
colors: {
|
||||
white: 'var(--white)',
|
||||
black: 'var(--black)',
|
||||
@@ -87,5 +87,5 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
}, */
|
||||
transformers: [transformerDirectives(), transformerVariantGroup()],
|
||||
transformers: [transformerDirectives(), transformerVariantGroup()],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user