diff --git a/.air.toml b/.air.toml
index 921489b..b268d61 100644
--- a/.air.toml
+++ b/.air.toml
@@ -5,7 +5,7 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
- cmd = "go build -o ./tmp/main ./cmd/reshoot"
+ cmd = "go build -o ./tmp/main ./cmd/projectreshoot"
delay = 1000
exclude_dir = []
exclude_file = []
diff --git a/.gitignore b/.gitignore
index 9d8ebf9..33692cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,5 +5,5 @@ server.log
bin/
tmp/
static/css/output.css
-view/**/*_templ.go
-view/**/*_templ.txt
+internal/view/**/*_templ.go
+internal/view/**/*_templ.txt
diff --git a/Makefile b/Makefile
index 78aa20a..d715556 100644
--- a/Makefile
+++ b/Makefile
@@ -8,26 +8,18 @@ build:
tailwindcss -i ./pkg/embedfs/files/css/input.css -o ./pkg/embedfs/files/css/output.css && \
go mod tidy && \
templ generate && \
- go generate ./cmd/projectreshoot && \
- go build -ldflags="-w -s" -o ./bin/${BINARY_NAME}${SUFFIX} ./cmd/projectreshoot
+ go generate ./cmd/${BINARY_NAME} && \
+ go build -ldflags="-w -s" -o ./bin/${BINARY_NAME}${SUFFIX} ./cmd/${BINARY_NAME}
+
+run:
+ make build
+ ./bin/${BINARY_NAME}${SUFFIX}
dev:
templ generate --watch &\
air &\
tailwindcss -i ./pkg/embedfs/files/css/input.css -o ./pkg/embedfs/files/css/output.css --watch
-tester:
- go mod tidy && \
- go run . --port 3232 --tester --loglevel trace
-
-test:
- go mod tidy && \
- templ generate && \
- go generate ./cmd/projectreshoot && \
- go test ./cmd/projectreshoot
- go test ./pkg/db
- go test ./internal/middleware
-
clean:
go clean
diff --git a/cmd/projectreshoot/dbconn.go b/cmd/projectreshoot/dbconn.go
index 650f8c1..c6b800d 100644
--- a/cmd/projectreshoot/dbconn.go
+++ b/cmd/projectreshoot/dbconn.go
@@ -1,37 +1,53 @@
package main
import (
- "projectreshoot/pkg/config"
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/tests"
+ "database/sql"
+ "fmt"
"strconv"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
+
+ _ "github.com/mattn/go-sqlite3"
)
-func setupDBConn(
- args map[string]string,
- logger *zerolog.Logger,
- config *config.Config,
-) (*db.SafeConn, error) {
- if args["test"] == "true" {
- logger.Debug().Msg("Server in test mode, using test database")
- ver, err := strconv.ParseInt(config.DBName, 10, 0)
- if err != nil {
- return nil, errors.Wrap(err, "strconv.ParseInt")
- }
- wconn, rconn, err := tests.SetupTestDB(ver)
- if err != nil {
- return nil, errors.Wrap(err, "tests.SetupTestDB")
- }
- conn := db.MakeSafe(wconn, rconn, logger)
- return conn, nil
- } else {
- conn, err := db.ConnectToDatabase(config.DBName, logger)
- if err != nil {
- return nil, errors.Wrap(err, "db.ConnectToDatabase")
- }
- return conn, nil
+func setupDBConn(dbName string) (*sql.DB, error) {
+ opts := "_journal_mode=WAL&_synchronous=NORMAL&_txlock=IMMEDIATE"
+ file := fmt.Sprintf("file:%s.db?%s", dbName, opts)
+ conn, err := sql.Open("sqlite3", file)
+ if err != nil {
+ return nil, errors.Wrap(err, "sql.Open")
}
+ err = checkDBVersion(conn, dbName)
+ if err != nil {
+ return nil, errors.Wrap(err, "checkDBVersion")
+ }
+ return conn, nil
+}
+
+// Check the database version
+func checkDBVersion(db *sql.DB, dbName string) error {
+ expectVer, err := strconv.Atoi(dbName)
+ if err != nil {
+ return errors.Wrap(err, "strconv.Atoi")
+ }
+ query := `SELECT version_id FROM goose_db_version WHERE is_applied = 1
+ ORDER BY version_id DESC LIMIT 1`
+ rows, err := db.Query(query)
+ if err != nil {
+ return errors.Wrap(err, "db.Query")
+ }
+ defer rows.Close()
+ if rows.Next() {
+ var version int
+ err = rows.Scan(&version)
+ if err != nil {
+ return errors.Wrap(err, "rows.Scan")
+ }
+ if version != expectVer {
+ return errors.New("Version mismatch")
+ }
+ } else {
+ return errors.New("No version found")
+ }
+ return nil
}
diff --git a/cmd/projectreshoot/main_test.go b/cmd/projectreshoot/main_test.go
deleted file mode 100644
index a97a5ae..0000000
--- a/cmd/projectreshoot/main_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package main
-
-import (
- "bytes"
- "context"
- "fmt"
- "net/http"
- "os"
- "strings"
- "syscall"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-)
-
-func Test_main(t *testing.T) {
- ctx := context.Background()
- ctx, cancel := context.WithCancel(ctx)
- t.Cleanup(cancel)
- args := map[string]string{"test": "true"}
- var stdout bytes.Buffer
- os.Setenv("SECRET_KEY", ".")
- os.Setenv("TMDB_API_TOKEN", ".")
- os.Setenv("HOST", "127.0.0.1")
- os.Setenv("PORT", "3232")
- runSrvErr := make(chan error)
- go func() {
- if err := run(ctx, &stdout, args); err != nil {
- runSrvErr <- err
- return
- }
- }()
-
- go func() {
- err := waitForReady(ctx, 10*time.Second, "http://127.0.0.1:3232/healthz")
- if err != nil {
- runSrvErr <- err
- return
- }
- runSrvErr <- nil
- }()
- select {
- case err := <-runSrvErr:
- if err != nil {
- t.Fatalf("Error starting test server: %s", err)
- return
- }
- t.Log("Test server started")
- }
-
- t.Run("SIGUSR1 puts database into global lock", func(t *testing.T) {
- done := make(chan bool)
- go func() {
- expected := "Global database lock acquired"
- for {
- if strings.Contains(stdout.String(), expected) {
- done <- true
- return
- }
- time.Sleep(100 * time.Millisecond)
- }
- }()
-
- proc, err := os.FindProcess(os.Getpid())
- require.NoError(t, err)
- proc.Signal(syscall.SIGUSR1)
-
- select {
- case <-done:
- t.Log("found")
- case <-time.After(250 * time.Millisecond):
- t.Errorf("Not found")
- }
- })
-
- t.Run("SIGUSR2 releases database global lock", func(t *testing.T) {
- done := make(chan bool)
- go func() {
- expected := "Global database lock released"
- for {
- if strings.Contains(stdout.String(), expected) {
- done <- true
- return
- }
- time.Sleep(100 * time.Millisecond)
- }
- }()
-
- proc, err := os.FindProcess(os.Getpid())
- require.NoError(t, err)
- proc.Signal(syscall.SIGUSR2)
-
- select {
- case <-done:
- t.Log("found")
- case <-time.After(250 * time.Millisecond):
- t.Errorf("Not found")
- }
- })
-}
-
-func waitForReady(
- ctx context.Context,
- timeout time.Duration,
- endpoint string,
-) error {
- client := http.Client{}
- startTime := time.Now()
- for {
- req, err := http.NewRequestWithContext(
- ctx,
- http.MethodGet,
- endpoint,
- nil,
- )
- if err != nil {
- return fmt.Errorf("failed to create request: %w", err)
- }
-
- resp, err := client.Do(req)
- if err != nil {
- fmt.Printf("Error making request: %s\n", err.Error())
- time.Sleep(250 * time.Millisecond)
- continue
- }
- if resp.StatusCode == http.StatusOK {
- fmt.Println("Endpoint is ready!")
- resp.Body.Close()
- return nil
- }
- resp.Body.Close()
-
- select {
- case <-ctx.Done():
- return ctx.Err()
- default:
- if time.Since(startTime) >= timeout {
- return fmt.Errorf("timeout reached while waiting for endpoint")
- }
- // wait a little while between checks
- time.Sleep(250 * time.Millisecond)
- }
- }
-}
diff --git a/cmd/projectreshoot/run.go b/cmd/projectreshoot/run.go
index ec530ac..9b9841c 100644
--- a/cmd/projectreshoot/run.go
+++ b/cmd/projectreshoot/run.go
@@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
- "git.haelnorr.com/haelnorr/golibh/logger"
"io"
"net/http"
"os"
@@ -14,6 +13,9 @@ import (
"sync"
"time"
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
+
"github.com/pkg/errors"
)
@@ -38,7 +40,7 @@ func run(ctx context.Context, w io.Writer, args map[string]string) error {
// Setup the logfile
var logfile *os.File = nil
if config.LogOutput == "both" || config.LogOutput == "file" {
- logfile, err = logging.GetLogFile(config.LogDir)
+ logfile, err = hlog.NewLogFile(config.LogDir)
if err != nil {
return errors.Wrap(err, "logging.GetLogFile")
}
@@ -52,7 +54,7 @@ func run(ctx context.Context, w io.Writer, args map[string]string) error {
}
// Setup the logger
- logger, err := logging.GetLogger(
+ logger, err := hlog.NewLogger(
config.LogLevel,
consoleWriter,
logfile,
@@ -65,7 +67,7 @@ func run(ctx context.Context, w io.Writer, args map[string]string) error {
// Setup the database connection
logger.Debug().Msg("Config loaded and logger started")
logger.Debug().Msg("Connecting to database")
- conn, err := setupDBConn(args, logger, config)
+ conn, err := setupDBConn(config.DBName)
if err != nil {
return errors.Wrap(err, "setupDBConn")
}
@@ -78,18 +80,22 @@ func run(ctx context.Context, w io.Writer, args map[string]string) error {
return errors.Wrap(err, "getStaticFiles")
}
- logger.Debug().Msg("Setting up HTTP server")
- httpServer := httpserver.NewServer(config, logger, conn, &staticFS, &maint)
+ // Setup TokenGenerator
+ logger.Debug().Msg("Creating TokenGenerator")
+ tokenGen, err := jwt.CreateGenerator(
+ config.AccessTokenExpiry,
+ config.RefreshTokenExpiry,
+ config.TokenFreshTime,
+ config.TrustedHost,
+ config.SecretKey,
+ conn,
+ )
- // Runs function for testing in dev if --tester flag true
- if args["tester"] == "true" {
- logger.Debug().Msg("Running tester function")
- test(config, logger, conn, httpServer)
- return nil
- }
+ logger.Debug().Msg("Setting up HTTP server")
+ httpServer := httpserver.NewServer(config, logger, conn, tokenGen, &staticFS, &maint)
// Setups a channel to listen for os.Signal
- handleMaintSignals(conn, config, httpServer, logger)
+ handleMaintSignals(httpServer, logger)
// Runs the http server
logger.Debug().Msg("Starting up the HTTP server")
@@ -102,9 +108,7 @@ func run(ctx context.Context, w io.Writer, args map[string]string) error {
// Handles graceful shutdown
var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
<-ctx.Done()
shutdownCtx := context.Background()
shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second)
@@ -112,7 +116,7 @@ func run(ctx context.Context, w io.Writer, args map[string]string) error {
if err := httpServer.Shutdown(shutdownCtx); err != nil {
logger.Error().Err(err).Msg("Error shutting down server")
}
- }()
+ })
wg.Wait()
logger.Info().Msg("Shutting down")
return nil
diff --git a/cmd/projectreshoot/signals.go b/cmd/projectreshoot/signals.go
index e608cc3..10566b0 100644
--- a/cmd/projectreshoot/signals.go
+++ b/cmd/projectreshoot/signals.go
@@ -4,21 +4,16 @@ import (
"net/http"
"os"
"os/signal"
- "projectreshoot/pkg/config"
- "projectreshoot/pkg/db"
"sync/atomic"
"syscall"
- "time"
- "github.com/rs/zerolog"
+ "git.haelnorr.com/h/golib/hlog"
)
// Handle SIGUSR1 and SIGUSR2 syscalls to toggle maintenance mode
func handleMaintSignals(
- conn *db.SafeConn,
- config *config.Config,
srv *http.Server,
- logger *zerolog.Logger,
+ logger *hlog.Logger,
) {
logger.Debug().Msg("Starting signal listener")
ch := make(chan os.Signal, 1)
@@ -33,14 +28,10 @@ func handleMaintSignals(
if atomic.LoadUint32(&maint) != 1 {
atomic.StoreUint32(&maint, 1)
logger.Info().Msg("Signal received: Starting maintenance")
- logger.Info().Msg("Attempting to acquire database lock")
- conn.Pause(config.DBLockTimeout * time.Second)
}
case syscall.SIGUSR2:
if atomic.LoadUint32(&maint) != 0 {
logger.Info().Msg("Signal received: Maintenance over")
- logger.Info().Msg("Releasing database lock")
- conn.Resume()
atomic.StoreUint32(&maint, 0)
}
}
diff --git a/cmd/projectreshoot/tester.go b/cmd/projectreshoot/tester.go
deleted file mode 100644
index 3932bf7..0000000
--- a/cmd/projectreshoot/tester.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package main
-
-import (
- "net/http"
-
- "projectreshoot/pkg/config"
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/tmdb"
-
- "github.com/rs/zerolog"
-)
-
-// This function will only be called if the --test commandline flag is set.
-// After the function finishes the application will close.
-// Running command `make tester` will run the test using port 3232 to avoid
-// conflicts on the default 3333. Useful for testing things out during dev.
-// If you add code here, remember to run:
-// `git update-index --assume-unchanged tester.go` to avoid tracking changes
-func test(
- config *config.Config,
- logger *zerolog.Logger,
- conn *db.SafeConn,
- srv *http.Server,
-) {
- query := "a few good men"
- search, err := tmdb.SearchMovies(config.TMDBToken, query, false, 1)
- if err != nil {
- logger.Error().Err(err).Msg("error occured")
- return
- }
- logger.Info().Interface("results", search).Msg("search results received")
-}
diff --git a/go.mod b/go.mod
index 55987f6..b010702 100644
--- a/go.mod
+++ b/go.mod
@@ -3,36 +3,35 @@ module projectreshoot
go 1.25.5
require (
- github.com/a-h/templ v0.3.833
- github.com/golang-jwt/jwt v3.2.2+incompatible
- github.com/google/uuid v1.6.0
+ git.haelnorr.com/h/golib/hlog v0.9.0
+ git.haelnorr.com/h/golib/jwt v0.9.0
+ git.haelnorr.com/h/golib/tmdb v0.8.0
+ github.com/a-h/templ v0.3.977
github.com/joho/godotenv v1.5.1
github.com/mattn/go-sqlite3 v1.14.24
github.com/pkg/errors v0.9.1
github.com/pressly/goose/v3 v3.24.1
- github.com/rs/zerolog v1.34.0
- github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.33.0
modernc.org/sqlite v1.35.0
)
+replace git.haelnorr.com/h/golib/jwt v0.9.0 => /home/haelnorr/projects/golib/jwt
+
require (
- git.haelnorr.com/haelnorr/golibh/logger v0.0.0-20251231092003-201af4af7a28 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
+ github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+ github.com/rs/zerolog v1.34.0 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
- golang.org/x/sync v0.11.0 // indirect
- golang.org/x/sys v0.30.0 // indirect
- gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/sys v0.34.0 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
diff --git a/go.sum b/go.sum
index e93d55f..90de7ef 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,11 @@
-git.haelnorr.com/haelnorr/golibh/logger v0.0.0-20251231092003-201af4af7a28 h1:9Kja2VpN9TU+7NWefoSW40jdBQaLEC2Gklo3uWoWGSk=
-git.haelnorr.com/haelnorr/golibh/logger v0.0.0-20251231092003-201af4af7a28/go.mod h1:rn805r6IrjzM/90X8pr9ulkBKqCg01YueivQZNMb/IY=
-github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU=
-github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk=
+git.haelnorr.com/h/golib/hlog v0.9.0 h1:ib8n2MdmiRK2TF067p220kXmhDe9aAnlcsgpuv+QpvE=
+git.haelnorr.com/h/golib/hlog v0.9.0/go.mod h1:oOlzb8UVHUYP1k7dN5PSJXVskAB2z8EYgRN85jAi0Zk=
+git.haelnorr.com/h/golib/tmdb v0.8.0 h1:OQ6M2TB8FHm8fJD7/ebfWm63Duzfp0kmFX9genEig34=
+git.haelnorr.com/h/golib/tmdb v0.8.0/go.mod h1:mGKYa3o3z0IsQ5EO3MPmnL2Bwl2sSMsUHXVgaIGR7Z0=
+github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
+github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
+github.com/a-h/templ v0.3.977 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg=
+github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
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=
@@ -18,11 +22,6 @@ 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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
@@ -44,36 +43,30 @@ github.com/pressly/goose/v3 v3.24.1 h1:bZmxRco2uy5uu5Ng1MMVEfYsFlrMJI+e/VMXHQ3C4
github.com/pressly/goose/v3 v3.24.1/go.mod h1:rEWreU9uVtt0DHCyLzF9gRcWiiTF/V+528DV+4DORug=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
-github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
-github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
-golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
-golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
-golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
+golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
-golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
+golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
+golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
diff --git a/internal/handler/account.go b/internal/handler/account.go
index fcb97e8..ee8e622 100644
--- a/internal/handler/account.go
+++ b/internal/handler/account.go
@@ -2,6 +2,7 @@ package handler
import (
"context"
+ "database/sql"
"net/http"
"time"
@@ -10,10 +11,10 @@ import (
"projectreshoot/internal/view/page"
"projectreshoot/pkg/contexts"
"projectreshoot/pkg/cookies"
- "projectreshoot/pkg/db"
+
+ "git.haelnorr.com/h/golib/hlog"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
)
// Renders the account page on the 'General' subpage
@@ -44,8 +45,8 @@ func AccountSubpage() http.Handler {
// Handles a request to change the users username
func ChangeUsername(
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ logger *hlog.Logger,
+ conn *sql.DB,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
@@ -53,7 +54,7 @@ func ChangeUsername(
defer cancel()
// Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
logger.Warn().Err(err).Msg("Error updating username")
w.WriteHeader(http.StatusServiceUnavailable)
@@ -61,7 +62,7 @@ func ChangeUsername(
}
r.ParseForm()
newUsername := r.FormValue("username")
- unique, err := models.CheckUsernameUnique(ctx, tx, newUsername)
+ unique, err := models.CheckUsernameUnique(tx, newUsername)
if err != nil {
tx.Rollback()
logger.Error().Err(err).Msg("Error updating username")
@@ -75,7 +76,7 @@ func ChangeUsername(
return
}
user := contexts.GetUser(r.Context())
- err = user.ChangeUsername(ctx, tx, newUsername)
+ err = user.ChangeUsername(tx, newUsername)
if err != nil {
tx.Rollback()
logger.Error().Err(err).Msg("Error updating username")
@@ -90,8 +91,8 @@ func ChangeUsername(
// Handles a request to change the users bio
func ChangeBio(
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ logger *hlog.Logger,
+ conn *sql.DB,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
@@ -99,7 +100,7 @@ func ChangeBio(
defer cancel()
// Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
logger.Warn().Err(err).Msg("Error updating bio")
w.WriteHeader(http.StatusServiceUnavailable)
@@ -115,7 +116,7 @@ func ChangeBio(
return
}
user := contexts.GetUser(r.Context())
- err = user.ChangeBio(ctx, tx, newBio)
+ err = user.ChangeBio(tx, newBio)
if err != nil {
tx.Rollback()
logger.Error().Err(err).Msg("Error updating bio")
@@ -144,8 +145,8 @@ func validateChangePassword(
// Handles a request to change the users password
func ChangePassword(
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ logger *hlog.Logger,
+ conn *sql.DB,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
@@ -153,7 +154,7 @@ func ChangePassword(
defer cancel()
// Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
logger.Warn().Err(err).Msg("Error updating password")
w.WriteHeader(http.StatusServiceUnavailable)
@@ -166,7 +167,7 @@ func ChangePassword(
return
}
user := contexts.GetUser(r.Context())
- err = user.SetPassword(ctx, tx, newPass)
+ err = user.SetPassword(tx, newPass)
if err != nil {
tx.Rollback()
logger.Error().Err(err).Msg("Error updating password")
diff --git a/internal/handler/login.go b/internal/handler/login.go
index 8157db3..fda9698 100644
--- a/internal/handler/login.go
+++ b/internal/handler/login.go
@@ -2,6 +2,7 @@ package handler
import (
"context"
+ "database/sql"
"net/http"
"time"
@@ -10,27 +11,26 @@ import (
"projectreshoot/internal/view/page"
"projectreshoot/pkg/config"
"projectreshoot/pkg/cookies"
- "projectreshoot/pkg/db"
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
)
// Validates the username matches a user in the database and the password
// is correct. Returns the corresponding user
func validateLogin(
- ctx context.Context,
- tx db.SafeTX,
+ tx *sql.Tx,
r *http.Request,
) (*models.User, error) {
formUsername := r.FormValue("username")
formPassword := r.FormValue("password")
- user, err := models.GetUserFromUsername(ctx, tx, formUsername)
+ user, err := models.GetUserFromUsername(tx, formUsername)
if err != nil {
return nil, errors.Wrap(err, "db.GetUserFromUsername")
}
- err = user.CheckPassword(formPassword)
+ err = user.CheckPassword(tx, formPassword)
if err != nil {
return nil, errors.New("Username or password incorrect")
}
@@ -52,8 +52,9 @@ func checkRememberMe(r *http.Request) bool {
// template for user feedback
func LoginRequest(
config *config.Config,
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ logger *hlog.Logger,
+ conn *sql.DB,
+ tokenGen *jwt.TokenGenerator,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
@@ -61,14 +62,14 @@ func LoginRequest(
defer cancel()
// Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
logger.Warn().Err(err).Msg("Failed to set token cookies")
w.WriteHeader(http.StatusServiceUnavailable)
return
}
r.ParseForm()
- user, err := validateLogin(ctx, tx, r)
+ user, err := validateLogin(tx, r)
if err != nil {
tx.Rollback()
if err.Error() != "Username or password incorrect" {
@@ -81,7 +82,7 @@ func LoginRequest(
}
rememberMe := checkRememberMe(r)
- err = cookies.SetTokenCookies(w, r, config, user, true, rememberMe)
+ err = cookies.SetTokenCookies(w, r, config, tokenGen, user, true, rememberMe)
if err != nil {
tx.Rollback()
w.WriteHeader(http.StatusInternalServerError)
diff --git a/internal/handler/logout.go b/internal/handler/logout.go
index 715c81f..3180dfa 100644
--- a/internal/handler/logout.go
+++ b/internal/handler/logout.go
@@ -2,26 +2,25 @@ package handler
import (
"context"
+ "database/sql"
"net/http"
"strings"
"time"
- "projectreshoot/pkg/config"
"projectreshoot/pkg/cookies"
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/jwt"
+
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
)
func revokeAccess(
- config *config.Config,
- ctx context.Context,
- tx *db.SafeWTX,
+ tokenGen *jwt.TokenGenerator,
+ tx *sql.Tx,
atStr string,
) error {
- aT, err := jwt.ParseAccessToken(config, ctx, tx, atStr)
+ aT, err := tokenGen.ValidateAccess(tx, atStr)
if err != nil {
if strings.Contains(err.Error(), "Token is expired") ||
strings.Contains(err.Error(), "Token has been revoked") {
@@ -29,7 +28,7 @@ func revokeAccess(
}
return errors.Wrap(err, "jwt.ParseAccessToken")
}
- err = jwt.RevokeToken(ctx, tx, aT)
+ err = aT.Revoke(tx)
if err != nil {
return errors.Wrap(err, "jwt.RevokeToken")
}
@@ -37,12 +36,11 @@ func revokeAccess(
}
func revokeRefresh(
- config *config.Config,
- ctx context.Context,
- tx *db.SafeWTX,
+ tokenGen *jwt.TokenGenerator,
+ tx *sql.Tx,
rtStr string,
) error {
- rT, err := jwt.ParseRefreshToken(config, ctx, tx, rtStr)
+ rT, err := tokenGen.ValidateRefresh(tx, rtStr)
if err != nil {
if strings.Contains(err.Error(), "Token is expired") ||
strings.Contains(err.Error(), "Token has been revoked") {
@@ -50,7 +48,7 @@ func revokeRefresh(
}
return errors.Wrap(err, "jwt.ParseRefreshToken")
}
- err = jwt.RevokeToken(ctx, tx, rT)
+ err = rT.Revoke(tx)
if err != nil {
return errors.Wrap(err, "jwt.RevokeToken")
}
@@ -59,20 +57,19 @@ func revokeRefresh(
// Retrieve and revoke the user's tokens
func revokeTokens(
- config *config.Config,
- ctx context.Context,
- tx *db.SafeWTX,
+ tokenGen *jwt.TokenGenerator,
+ tx *sql.Tx,
r *http.Request,
) error {
// get the tokens from the cookies
atStr, rtStr := cookies.GetTokenStrings(r)
// revoke the refresh token first as the access token expires quicker
// only matters if there is an error revoking the tokens
- err := revokeRefresh(config, ctx, tx, rtStr)
+ err := revokeRefresh(tokenGen, tx, rtStr)
if err != nil {
return errors.Wrap(err, "revokeRefresh")
}
- err = revokeAccess(config, ctx, tx, atStr)
+ err = revokeAccess(tokenGen, tx, atStr)
if err != nil {
return errors.Wrap(err, "revokeAccess")
}
@@ -81,25 +78,25 @@ func revokeTokens(
// Handle a logout request
func Logout(
- config *config.Config,
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ conn *sql.DB,
+ tokenGen *jwt.TokenGenerator,
+ logger *hlog.Logger,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
defer cancel()
- // Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
- logger.Warn().Err(err).Msg("Error occured on user logout")
- w.WriteHeader(http.StatusServiceUnavailable)
+ logger.Error().Err(err).Msg("Failed to start database transaction")
+ w.WriteHeader(http.StatusInternalServerError)
return
}
- err = revokeTokens(config, ctx, tx, r)
+ defer tx.Rollback()
+
+ err = revokeTokens(tokenGen, tx, r)
if err != nil {
- tx.Rollback()
logger.Error().Err(err).Msg("Error occured on user logout")
w.WriteHeader(http.StatusInternalServerError)
return
diff --git a/internal/handler/movie.go b/internal/handler/movie.go
index c4b286e..ca352fa 100644
--- a/internal/handler/movie.go
+++ b/internal/handler/movie.go
@@ -4,14 +4,14 @@ import (
"net/http"
"projectreshoot/internal/view/page"
"projectreshoot/pkg/config"
- "projectreshoot/pkg/tmdb"
"strconv"
- "github.com/rs/zerolog"
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/tmdb"
)
func Movie(
- logger *zerolog.Logger,
+ logger *hlog.Logger,
config *config.Config,
) http.Handler {
return http.HandlerFunc(
diff --git a/internal/handler/movie_search.go b/internal/handler/movie_search.go
index aaa425a..dfa7b98 100644
--- a/internal/handler/movie_search.go
+++ b/internal/handler/movie_search.go
@@ -5,13 +5,13 @@ import (
"projectreshoot/internal/view/component/search"
"projectreshoot/internal/view/page"
"projectreshoot/pkg/config"
- "projectreshoot/pkg/tmdb"
- "github.com/rs/zerolog"
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/tmdb"
)
func SearchMovies(
- logger *zerolog.Logger,
+ logger *hlog.Logger,
config *config.Config,
) http.Handler {
return http.HandlerFunc(
diff --git a/internal/handler/reauthenticatate.go b/internal/handler/reauthenticatate.go
index b3e8189..aa7b0b4 100644
--- a/internal/handler/reauthenticatate.go
+++ b/internal/handler/reauthenticatate.go
@@ -2,6 +2,7 @@ package handler
import (
"context"
+ "database/sql"
"net/http"
"time"
@@ -9,47 +10,45 @@ import (
"projectreshoot/pkg/config"
"projectreshoot/pkg/contexts"
"projectreshoot/pkg/cookies"
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/jwt"
+
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
)
// Get the tokens from the request
func getTokens(
- config *config.Config,
- ctx context.Context,
- tx db.SafeTX,
+ tokenGen *jwt.TokenGenerator,
+ tx *sql.Tx,
r *http.Request,
) (*jwt.AccessToken, *jwt.RefreshToken, error) {
// get the existing tokens from the cookies
atStr, rtStr := cookies.GetTokenStrings(r)
- aT, err := jwt.ParseAccessToken(config, ctx, tx, atStr)
+ aT, err := tokenGen.ValidateAccess(tx, atStr)
if err != nil {
- return nil, nil, errors.Wrap(err, "jwt.ParseAccessToken")
+ return nil, nil, errors.Wrap(err, "tokenGen.ValidateAccess")
}
- rT, err := jwt.ParseRefreshToken(config, ctx, tx, rtStr)
+ rT, err := tokenGen.ValidateRefresh(tx, rtStr)
if err != nil {
- return nil, nil, errors.Wrap(err, "jwt.ParseRefreshToken")
+ return nil, nil, errors.Wrap(err, "tokenGen.ValidateRefresh")
}
return aT, rT, nil
}
// Revoke the given token pair
func revokeTokenPair(
- ctx context.Context,
- tx *db.SafeWTX,
+ tx *sql.Tx,
aT *jwt.AccessToken,
rT *jwt.RefreshToken,
) error {
- err := jwt.RevokeToken(ctx, tx, aT)
+ err := aT.Revoke(tx)
if err != nil {
- return errors.Wrap(err, "jwt.RevokeToken")
+ return errors.Wrap(err, "aT.Revoke")
}
- err = jwt.RevokeToken(ctx, tx, rT)
+ err = rT.Revoke(tx)
if err != nil {
- return errors.Wrap(err, "jwt.RevokeToken")
+ return errors.Wrap(err, "rT.Revoke")
}
return nil
}
@@ -57,12 +56,12 @@ func revokeTokenPair(
// Issue new tokens for the user, invalidating the old ones
func refreshTokens(
config *config.Config,
- ctx context.Context,
- tx *db.SafeWTX,
+ tokenGen *jwt.TokenGenerator,
+ tx *sql.Tx,
w http.ResponseWriter,
r *http.Request,
) error {
- aT, rT, err := getTokens(config, ctx, tx, r)
+ aT, rT, err := getTokens(tokenGen, tx, r)
if err != nil {
return errors.Wrap(err, "getTokens")
}
@@ -72,11 +71,11 @@ func refreshTokens(
}[aT.TTL]
// issue new tokens for the user
user := contexts.GetUser(r.Context())
- err = cookies.SetTokenCookies(w, r, config, user.User, true, rememberMe)
+ err = cookies.SetTokenCookies(w, r, config, tokenGen, user.User, true, rememberMe)
if err != nil {
return errors.Wrap(err, "cookies.SetTokenCookies")
}
- err = revokeTokenPair(ctx, tx, aT, rT)
+ err = revokeTokenPair(tx, aT, rT)
if err != nil {
return errors.Wrap(err, "revokeTokenPair")
}
@@ -86,12 +85,13 @@ func refreshTokens(
// Validate the provided password
func validatePassword(
+ tx *sql.Tx,
r *http.Request,
) error {
r.ParseForm()
password := r.FormValue("password")
user := contexts.GetUser(r.Context())
- err := user.CheckPassword(password)
+ err := user.CheckPassword(tx, password)
if err != nil {
return errors.Wrap(err, "user.CheckPassword")
}
@@ -100,9 +100,10 @@ func validatePassword(
// Handle request to reauthenticate (i.e. make token fresh again)
func Reauthenticate(
- logger *zerolog.Logger,
+ logger *hlog.Logger,
config *config.Config,
- conn *db.SafeConn,
+ conn *sql.DB,
+ tokenGen *jwt.TokenGenerator,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
@@ -110,22 +111,21 @@ func Reauthenticate(
defer cancel()
// Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
- logger.Warn().Err(err).Msg("Failed to refresh user tokens")
- w.WriteHeader(http.StatusServiceUnavailable)
+ logger.Error().Err(err).Msg("Failed to start transaction")
+ w.WriteHeader(http.StatusInternalServerError)
return
}
- err = validatePassword(r)
+ defer tx.Rollback()
+ err = validatePassword(tx, r)
if err != nil {
- tx.Rollback()
w.WriteHeader(445)
form.ConfirmPassword("Incorrect password").Render(r.Context(), w)
return
}
- err = refreshTokens(config, ctx, tx, w, r)
+ err = refreshTokens(config, tokenGen, tx, w, r)
if err != nil {
- tx.Rollback()
logger.Error().Err(err).Msg("Failed to refresh user tokens")
w.WriteHeader(http.StatusInternalServerError)
return
diff --git a/internal/handler/register.go b/internal/handler/register.go
index 226b3b7..5202667 100644
--- a/internal/handler/register.go
+++ b/internal/handler/register.go
@@ -2,6 +2,7 @@ package handler
import (
"context"
+ "database/sql"
"net/http"
"time"
@@ -10,23 +11,23 @@ import (
"projectreshoot/internal/view/page"
"projectreshoot/pkg/config"
"projectreshoot/pkg/cookies"
- "projectreshoot/pkg/db"
+
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
)
func validateRegistration(
- ctx context.Context,
- tx *db.SafeWTX,
+ tx *sql.Tx,
r *http.Request,
) (*models.User, error) {
formUsername := r.FormValue("username")
formPassword := r.FormValue("password")
formConfirmPassword := r.FormValue("confirm-password")
- unique, err := models.CheckUsernameUnique(ctx, tx, formUsername)
+ unique, err := models.CheckUsernameUnique(tx, formUsername)
if err != nil {
- return nil, errors.Wrap(err, "db.CheckUsernameUnique")
+ return nil, errors.Wrap(err, "models.CheckUsernameUnique")
}
if !unique {
return nil, errors.New("Username is taken")
@@ -37,9 +38,9 @@ func validateRegistration(
if len(formPassword) > 72 {
return nil, errors.New("Password exceeds maximum length of 72 bytes")
}
- user, err := models.CreateNewUser(ctx, tx, formUsername, formPassword)
+ user, err := models.CreateNewUser(tx, formUsername, formPassword)
if err != nil {
- return nil, errors.Wrap(err, "db.CreateNewUser")
+ return nil, errors.Wrap(err, "models.CreateNewUser")
}
return user, nil
@@ -47,8 +48,9 @@ func validateRegistration(
func RegisterRequest(
config *config.Config,
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ tokenGen *jwt.TokenGenerator,
+ logger *hlog.Logger,
+ conn *sql.DB,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
@@ -56,14 +58,14 @@ func RegisterRequest(
defer cancel()
// Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
logger.Warn().Err(err).Msg("Failed to set token cookies")
w.WriteHeader(http.StatusServiceUnavailable)
return
}
r.ParseForm()
- user, err := validateRegistration(ctx, tx, r)
+ user, err := validateRegistration(tx, r)
if err != nil {
tx.Rollback()
if err.Error() != "Username is taken" &&
@@ -78,7 +80,7 @@ func RegisterRequest(
}
rememberMe := checkRememberMe(r)
- err = cookies.SetTokenCookies(w, r, config, user, true, rememberMe)
+ err = cookies.SetTokenCookies(w, r, config, tokenGen, user, true, rememberMe)
if err != nil {
tx.Rollback()
w.WriteHeader(http.StatusInternalServerError)
diff --git a/internal/httpserver/routes.go b/internal/httpserver/routes.go
index e31bc7a..e96c04b 100644
--- a/internal/httpserver/routes.go
+++ b/internal/httpserver/routes.go
@@ -1,23 +1,25 @@
package httpserver
import (
+ "database/sql"
"net/http"
"projectreshoot/internal/handler"
"projectreshoot/internal/middleware"
"projectreshoot/internal/view/page"
"projectreshoot/pkg/config"
- "projectreshoot/pkg/db"
- "github.com/rs/zerolog"
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
)
// Add all the handled routes to the mux
func addRoutes(
mux *http.ServeMux,
- logger *zerolog.Logger,
+ logger *hlog.Logger,
config *config.Config,
- conn *db.SafeConn,
+ tokenGen *jwt.TokenGenerator,
+ conn *sql.DB,
staticFS *http.FileSystem,
) {
route := mux.Handle
@@ -39,17 +41,17 @@ func addRoutes(
// Login page and handlers
route("GET /login", loggedOut(handler.LoginPage(config.TrustedHost)))
- route("POST /login", loggedOut(handler.LoginRequest(config, logger, conn)))
+ route("POST /login", loggedOut(handler.LoginRequest(config, logger, conn, tokenGen)))
// Register page and handlers
route("GET /register", loggedOut(handler.RegisterPage(config.TrustedHost)))
- route("POST /register", loggedOut(handler.RegisterRequest(config, logger, conn)))
+ route("POST /register", loggedOut(handler.RegisterRequest(config, tokenGen, logger, conn)))
// Logout
- route("POST /logout", handler.Logout(config, logger, conn))
+ route("POST /logout", handler.Logout(conn, tokenGen, logger))
// Reauthentication request
- route("POST /reauthenticate", loggedIn(handler.Reauthenticate(logger, config, conn)))
+ route("POST /reauthenticate", loggedIn(handler.Reauthenticate(logger, config, conn, tokenGen)))
// Profile page
route("GET /profile", loggedIn(handler.ProfilePage()))
diff --git a/internal/httpserver/server.go b/internal/httpserver/server.go
index f634505..5d73bc4 100644
--- a/internal/httpserver/server.go
+++ b/internal/httpserver/server.go
@@ -1,6 +1,7 @@
package httpserver
import (
+ "database/sql"
"io/fs"
"net"
"net/http"
@@ -8,20 +9,21 @@ import (
"projectreshoot/internal/middleware"
"projectreshoot/pkg/config"
- "projectreshoot/pkg/db"
- "github.com/rs/zerolog"
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
)
func NewServer(
config *config.Config,
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ logger *hlog.Logger,
+ conn *sql.DB,
+ tokenGen *jwt.TokenGenerator,
staticFS *fs.FS,
maint *uint32,
) *http.Server {
fs := http.FS(*staticFS)
- srv := createServer(config, logger, conn, &fs, maint)
+ srv := createServer(config, logger, conn, tokenGen, &fs, maint)
httpServer := &http.Server{
Addr: net.JoinHostPort(config.Host, config.Port),
Handler: srv,
@@ -35,8 +37,9 @@ func NewServer(
// Returns a new http.Handler with all the routes and middleware added
func createServer(
config *config.Config,
- logger *zerolog.Logger,
- conn *db.SafeConn,
+ logger *hlog.Logger,
+ conn *sql.DB,
+ tokenGen *jwt.TokenGenerator,
staticFS *http.FileSystem,
maint *uint32,
) http.Handler {
@@ -45,6 +48,7 @@ func createServer(
mux,
logger,
config,
+ tokenGen,
conn,
staticFS,
)
@@ -52,7 +56,7 @@ func createServer(
// Add middleware here, must be added in reverse order of execution
// i.e. First in list will get executed last during the request handling
handler = middleware.Logging(logger, handler)
- handler = middleware.Authentication(logger, config, conn, handler, maint)
+ handler = middleware.Authentication(logger, config, conn, tokenGen, handler, maint)
// Gzip
handler = middleware.Gzip(handler, config.GZIP)
diff --git a/internal/middleware/authentication.go b/internal/middleware/authentication.go
index 4393eff..302dd35 100644
--- a/internal/middleware/authentication.go
+++ b/internal/middleware/authentication.go
@@ -2,6 +2,7 @@ package middleware
import (
"context"
+ "database/sql"
"net/http"
"sync/atomic"
"time"
@@ -11,25 +12,24 @@ import (
"projectreshoot/pkg/config"
"projectreshoot/pkg/contexts"
"projectreshoot/pkg/cookies"
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/jwt"
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/jwt"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
)
// Attempt to use a valid refresh token to generate a new token pair
func refreshAuthTokens(
config *config.Config,
- ctx context.Context,
- tx *db.SafeWTX,
+ tokenGen *jwt.TokenGenerator,
+ tx *sql.Tx,
w http.ResponseWriter,
req *http.Request,
ref *jwt.RefreshToken,
) (*models.User, error) {
- user, err := ref.GetUser(ctx, tx)
+ user, err := models.GetUserFromID(tx, ref.SUB)
if err != nil {
- return nil, errors.Wrap(err, "ref.GetUser")
+ return nil, errors.Wrap(err, "models.GetUser")
}
rememberMe := map[string]bool{
@@ -38,14 +38,14 @@ func refreshAuthTokens(
}[ref.TTL]
// Set fresh to true because new tokens coming from refresh request
- err = cookies.SetTokenCookies(w, req, config, user, false, rememberMe)
+ err = cookies.SetTokenCookies(w, req, config, tokenGen, user, false, rememberMe)
if err != nil {
return nil, errors.Wrap(err, "cookies.SetTokenCookies")
}
// New tokens sent, revoke the used refresh token
- err = jwt.RevokeToken(ctx, tx, ref)
+ err = ref.Revoke(tx)
if err != nil {
- return nil, errors.Wrap(err, "jwt.RevokeToken")
+ return nil, errors.Wrap(err, "ref.Revoke")
}
// Return the authorized user
return user, nil
@@ -54,23 +54,26 @@ func refreshAuthTokens(
// Check the cookies for token strings and attempt to authenticate them
func getAuthenticatedUser(
config *config.Config,
- ctx context.Context,
- tx *db.SafeWTX,
+ tokenGen *jwt.TokenGenerator,
+ tx *sql.Tx,
w http.ResponseWriter,
r *http.Request,
) (*contexts.AuthenticatedUser, error) {
// Get token strings from cookies
atStr, rtStr := cookies.GetTokenStrings(r)
+ if atStr == "" && rtStr == "" {
+ return nil, errors.New("No token strings provided")
+ }
// Attempt to parse the access token
- aT, err := jwt.ParseAccessToken(config, ctx, tx, atStr)
+ aT, err := tokenGen.ValidateAccess(tx, atStr)
if err != nil {
// Access token invalid, attempt to parse refresh token
- rT, err := jwt.ParseRefreshToken(config, ctx, tx, rtStr)
+ rT, err := tokenGen.ValidateRefresh(tx, rtStr)
if err != nil {
- return nil, errors.Wrap(err, "jwt.ParseRefreshToken")
+ return nil, errors.Wrap(err, "tokenGen.ValidateRefresh")
}
// Refresh token valid, attempt to get a new token pair
- user, err := refreshAuthTokens(config, ctx, tx, w, r, rT)
+ user, err := refreshAuthTokens(config, tokenGen, tx, w, r, rT)
if err != nil {
return nil, errors.Wrap(err, "refreshAuthTokens")
}
@@ -82,9 +85,9 @@ func getAuthenticatedUser(
return &authUser, nil
}
// Access token valid
- user, err := aT.GetUser(ctx, tx)
+ user, err := models.GetUserFromID(tx, aT.SUB)
if err != nil {
- return nil, errors.Wrap(err, "aT.GetUser")
+ return nil, errors.Wrap(err, "models.GetUser")
}
authUser := contexts.AuthenticatedUser{
User: user,
@@ -96,9 +99,10 @@ func getAuthenticatedUser(
// Attempt to authenticate the user and add their account details
// to the request context
func Authentication(
- logger *zerolog.Logger,
+ logger *hlog.Logger,
config *config.Config,
- conn *db.SafeConn,
+ conn *sql.DB,
+ tokenGen *jwt.TokenGenerator,
next http.Handler,
maint *uint32,
) http.Handler {
@@ -115,7 +119,7 @@ func Authentication(
}
// Start the transaction
- tx, err := conn.Begin(ctx)
+ tx, err := conn.BeginTx(ctx, nil)
if err != nil {
// Failed to start transaction, skip auth
logger.Warn().Err(err).
@@ -123,7 +127,7 @@ func Authentication(
handler.ErrorPage(http.StatusServiceUnavailable, w, r)
return
}
- user, err := getAuthenticatedUser(config, ctx, tx, w, r)
+ user, err := getAuthenticatedUser(config, tokenGen, tx, w, r)
if err != nil {
tx.Rollback()
// User auth failed, delete the cookies to avoid repeat requests
diff --git a/internal/middleware/authentication_test.go b/internal/middleware/authentication_test.go
deleted file mode 100644
index df68ad0..0000000
--- a/internal/middleware/authentication_test.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package middleware
-
-import (
- "io"
- "net/http"
- "net/http/httptest"
- "strconv"
- "sync/atomic"
- "testing"
-
- "projectreshoot/pkg/contexts"
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/tests"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestAuthenticationMiddleware(t *testing.T) {
- cfg, err := tests.TestConfig()
- require.NoError(t, err)
- logger := tests.NilLogger()
- ver, err := strconv.ParseInt(cfg.DBName, 10, 0)
- require.NoError(t, err)
- wconn, rconn, err := tests.SetupTestDB(ver)
- require.NoError(t, err)
- sconn := db.MakeSafe(wconn, rconn, logger)
- defer sconn.Close()
-
- // Handler to check outcome of Authentication middleware
- testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- user := contexts.GetUser(r.Context())
- if user == nil {
- w.WriteHeader(http.StatusUnauthorized)
- w.Write([]byte(strconv.Itoa(0)))
- return
- } else {
- w.WriteHeader(http.StatusOK)
- w.Write([]byte(strconv.Itoa(user.ID)))
- }
- })
- var maint uint32
- atomic.StoreUint32(&maint, 0)
- // Add the middleware and create the server
- authHandler := Authentication(logger, cfg, sconn, testHandler, &maint)
- require.NoError(t, err)
- server := httptest.NewServer(authHandler)
- defer server.Close()
-
- tokens := getTokens()
-
- tests := []struct {
- name string
- id int
- accessToken string
- refreshToken string
- expectedCode int
- }{
- {
- name: "Valid Access Token (Fresh)",
- id: 1,
- accessToken: tokens["accessFresh"],
- refreshToken: "",
- expectedCode: http.StatusOK,
- },
- {
- name: "Valid Access Token (Unfresh)",
- id: 1,
- accessToken: tokens["accessUnfresh"],
- refreshToken: tokens["refreshExpired"],
- expectedCode: http.StatusOK,
- },
- {
- name: "Valid Refresh Token (Triggers Refresh)",
- id: 1,
- accessToken: tokens["accessExpired"],
- refreshToken: tokens["refreshValid"],
- expectedCode: http.StatusOK,
- },
- {
- name: "Both tokens expired",
- accessToken: tokens["accessExpired"],
- refreshToken: tokens["refreshExpired"],
- expectedCode: http.StatusUnauthorized,
- },
- {
- name: "Access token revoked",
- accessToken: tokens["accessRevoked"],
- refreshToken: "",
- expectedCode: http.StatusUnauthorized,
- },
- {
- name: "Refresh token revoked",
- accessToken: "",
- refreshToken: tokens["refreshRevoked"],
- expectedCode: http.StatusUnauthorized,
- },
- {
- name: "Invalid Tokens",
- accessToken: tokens["invalid"],
- refreshToken: tokens["invalid"],
- expectedCode: http.StatusUnauthorized,
- },
- {
- name: "No Tokens",
- accessToken: "",
- refreshToken: "",
- expectedCode: http.StatusUnauthorized,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- client := &http.Client{}
-
- req, _ := http.NewRequest(http.MethodGet, server.URL, nil)
-
- // Add cookies if provided
- if tt.accessToken != "" {
- req.AddCookie(&http.Cookie{Name: "access", Value: tt.accessToken})
- }
- if tt.refreshToken != "" {
- req.AddCookie(&http.Cookie{Name: "refresh", Value: tt.refreshToken})
- }
-
- resp, err := client.Do(req)
- assert.NoError(t, err)
- assert.Equal(t, tt.expectedCode, resp.StatusCode)
- body, err := io.ReadAll(resp.Body)
- require.NoError(t, err)
- assert.Equal(t, strconv.Itoa(tt.id), string(body))
- })
- }
-}
-
-// get the tokens to test with
-func getTokens() map[string]string {
- tokens := map[string]string{
- "accessFresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ4OTU2NzIyMTAsImZyZXNoIjo0ODk1NjcyMjEwLCJpYXQiOjE3Mzk2NzIyMTAsImlzcyI6IjEyNy4wLjAuMSIsImp0aSI6ImE4Njk2YWM4LTg3OWMtNDdkNC1iZWM2LTRlY2Y4MTRiZThiZiIsInNjb3BlIjoiYWNjZXNzIiwic3ViIjoxLCJ0dGwiOiJzZXNzaW9uIn0.6nAquDY0JBLPdaJ9q_sMpKj1ISG4Vt2U05J57aoPue8",
- "accessUnfresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjMzMjk5Njc1NjcxLCJmcmVzaCI6MTczOTY3NTY3MSwiaWF0IjoxNzM5Njc1NjcxLCJpc3MiOiIxMjcuMC4wLjEiLCJqdGkiOiJjOGNhZmFjNy0yODkzLTQzNzMtOTI4ZS03MGUwODJkYmM2MGIiLCJzY29wZSI6ImFjY2VzcyIsInN1YiI6MSwidHRsIjoic2Vzc2lvbiJ9.plWQVFwHlhXUYI5utS7ny1JfXjJSFrigkq-PnTHD5VY",
- "accessExpired": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3Mzk2NzIyNDgsImZyZXNoIjoxNzM5NjcyMjQ4LCJpYXQiOjE3Mzk2NzIyNDgsImlzcyI6IjEyNy4wLjAuMSIsImp0aSI6IjgxYzA1YzBjLTJhOGItNGQ2MC04Yzc4LWY2ZTQxODYxZDFmNCIsInNjb3BlIjoiYWNjZXNzIiwic3ViIjoxLCJ0dGwiOiJzZXNzaW9uIn0.iI1f17kKTuFDEMEYltJRIwRYgYQ-_nF9Wsn0KR6x77Q",
- "refreshValid": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ4OTU2NzE5MjIsImlhdCI6MTczOTY3MTkyMiwiaXNzIjoiMTI3LjAuMC4xIiwianRpIjoiZTUxMTY3ZWEtNDA3OS00ZTczLTkzZDQtNTgwZDMzODRjZDU4Iiwic2NvcGUiOiJyZWZyZXNoIiwic3ViIjoxLCJ0dGwiOiJzZXNzaW9uIn0.tvtqQ8Z4WrYWHHb0MaEPdsU2FT2KLRE1zHOv3ipoFyc",
- "refreshExpired": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3Mzk2NzIyNDgsImlhdCI6MTczOTY3MjI0OCwiaXNzIjoiMTI3LjAuMC4xIiwianRpIjoiZTg5YTc5MTYtZGEzYi00YmJhLWI3ZDMtOWI1N2ViNjRhMmU0Iiwic2NvcGUiOiJyZWZyZXNoIiwic3ViIjoxLCJ0dGwiOiJzZXNzaW9uIn0.rH_fytC7Duxo598xacu820pQKF9ELbG8674h_bK_c4I",
- "accessRevoked": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ4OTU2NzE5MjIsImZyZXNoIjoxNzM5NjcxOTIyLCJpYXQiOjE3Mzk2NzE5MjIsImlzcyI6IjEyNy4wLjAuMSIsImp0aSI6IjBhNmIzMzhlLTkzMGEtNDNmZS04ZjcwLTFhNmRhZWQyNTZmYSIsInNjb3BlIjoiYWNjZXNzIiwic3ViIjoxLCJ0dGwiOiJzZXNzaW9uIn0.mZLuCp9amcm2_CqYvbHPlk86nfiuy_Or8TlntUCw4Qs",
- "refreshRevoked": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjMzMjk5Njc1NjcxLCJpYXQiOjE3Mzk2NzU2NzEsImlzcyI6IjEyNy4wLjAuMSIsImp0aSI6ImI3ZmE1MWRjLTg1MzItNDJlMS04NzU2LTVkMjViZmIyMDAzYSIsInNjb3BlIjoicmVmcmVzaCIsInN1YiI6MSwidHRsIjoic2Vzc2lvbiJ9.5Q9yDZN5FubfCWHclUUZEkJPOUHcOEpVpgcUK-ameHo",
- "invalid": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
- }
- return tokens
-}
diff --git a/internal/middleware/logging.go b/internal/middleware/logging.go
index e890152..82bbffd 100644
--- a/internal/middleware/logging.go
+++ b/internal/middleware/logging.go
@@ -6,7 +6,7 @@ import (
"projectreshoot/pkg/contexts"
"time"
- "github.com/rs/zerolog"
+ "git.haelnorr.com/h/golib/hlog"
)
// Wraps the http.ResponseWriter, adding a statusCode field
@@ -22,7 +22,7 @@ func (w *wrappedWriter) WriteHeader(statusCode int) {
}
// Middleware to add logs to console with details of the request
-func Logging(logger *zerolog.Logger, next http.Handler) http.Handler {
+func Logging(logger *hlog.Logger, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/static/css/output.css" ||
r.URL.Path == "/static/favicon.ico" {
diff --git a/internal/middleware/pageprotection_test.go b/internal/middleware/pageprotection_test.go
deleted file mode 100644
index 8cbc81b..0000000
--- a/internal/middleware/pageprotection_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package middleware
-
-import (
- "net/http"
- "net/http/httptest"
- "strconv"
- "sync/atomic"
- "testing"
-
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/tests"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestPageLoginRequired(t *testing.T) {
- cfg, err := tests.TestConfig()
- require.NoError(t, err)
- logger := tests.NilLogger()
- ver, err := strconv.ParseInt(cfg.DBName, 10, 0)
- require.NoError(t, err)
- wconn, rconn, err := tests.SetupTestDB(ver)
- require.NoError(t, err)
- sconn := db.MakeSafe(wconn, rconn, logger)
- defer sconn.Close()
-
- // Handler to check outcome of Authentication middleware
- testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusOK)
- })
-
- var maint uint32
- atomic.StoreUint32(&maint, 0)
- // Add the middleware and create the server
- loginRequiredHandler := LoginReq(testHandler)
- authHandler := Authentication(logger, cfg, sconn, loginRequiredHandler, &maint)
- server := httptest.NewServer(authHandler)
- defer server.Close()
-
- tokens := getTokens()
-
- tests := []struct {
- name string
- accessToken string
- refreshToken string
- expectedCode int
- }{
- {
- name: "Valid Login",
- accessToken: tokens["accessFresh"],
- refreshToken: "",
- expectedCode: http.StatusOK,
- },
- {
- name: "Expired login",
- accessToken: tokens["accessExpired"],
- refreshToken: tokens["refreshExpired"],
- expectedCode: http.StatusUnauthorized,
- },
- {
- name: "No login",
- accessToken: "",
- refreshToken: "",
- expectedCode: http.StatusUnauthorized,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- client := &http.Client{}
-
- req, _ := http.NewRequest(http.MethodGet, server.URL, nil)
-
- // Add cookies if provided
- if tt.accessToken != "" {
- req.AddCookie(&http.Cookie{Name: "access", Value: tt.accessToken})
- }
- if tt.refreshToken != "" {
- req.AddCookie(&http.Cookie{Name: "refresh", Value: tt.refreshToken})
- }
-
- resp, err := client.Do(req)
- assert.NoError(t, err)
- assert.Equal(t, tt.expectedCode, resp.StatusCode)
- })
- }
-}
diff --git a/internal/middleware/reauthentication_test.go b/internal/middleware/reauthentication_test.go
deleted file mode 100644
index de407e0..0000000
--- a/internal/middleware/reauthentication_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package middleware
-
-import (
- "net/http"
- "net/http/httptest"
- "strconv"
- "sync/atomic"
- "testing"
-
- "projectreshoot/pkg/db"
- "projectreshoot/pkg/tests"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestReauthRequired(t *testing.T) {
- cfg, err := tests.TestConfig()
- require.NoError(t, err)
- logger := tests.NilLogger()
- ver, err := strconv.ParseInt(cfg.DBName, 10, 0)
- require.NoError(t, err)
- wconn, rconn, err := tests.SetupTestDB(ver)
- require.NoError(t, err)
- sconn := db.MakeSafe(wconn, rconn, logger)
- defer sconn.Close()
-
- // Handler to check outcome of Authentication middleware
- testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusOK)
- })
-
- var maint uint32
- atomic.StoreUint32(&maint, 0)
- // Add the middleware and create the server
- reauthRequiredHandler := FreshReq(testHandler)
- loginRequiredHandler := LoginReq(reauthRequiredHandler)
- authHandler := Authentication(logger, cfg, sconn, loginRequiredHandler, &maint)
- server := httptest.NewServer(authHandler)
- defer server.Close()
-
- tokens := getTokens()
-
- tests := []struct {
- name string
- accessToken string
- refreshToken string
- expectedCode int
- }{
- {
- name: "Fresh Login",
- accessToken: tokens["accessFresh"],
- refreshToken: "",
- expectedCode: http.StatusOK,
- },
- {
- name: "Unfresh Login",
- accessToken: tokens["accessUnfresh"],
- refreshToken: "",
- expectedCode: 444,
- },
- {
- name: "Expired login",
- accessToken: tokens["accessExpired"],
- refreshToken: tokens["refreshExpired"],
- expectedCode: http.StatusUnauthorized,
- },
- {
- name: "No login",
- accessToken: "",
- refreshToken: "",
- expectedCode: http.StatusUnauthorized,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- client := &http.Client{}
-
- req, _ := http.NewRequest(http.MethodGet, server.URL, nil)
-
- // Add cookies if provided
- if tt.accessToken != "" {
- req.AddCookie(&http.Cookie{Name: "access", Value: tt.accessToken})
- }
- if tt.refreshToken != "" {
- req.AddCookie(&http.Cookie{Name: "refresh", Value: tt.refreshToken})
- }
-
- resp, err := client.Do(req)
- assert.NoError(t, err)
- assert.Equal(t, tt.expectedCode, resp.StatusCode)
- })
- }
-}
diff --git a/internal/models/user.go b/internal/models/user.go
index 47a2341..3f93ea2 100644
--- a/internal/models/user.go
+++ b/internal/models/user.go
@@ -1,30 +1,31 @@
package models
import (
- "context"
- "projectreshoot/pkg/db"
+ "database/sql"
"github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
)
type User struct {
- ID int // Integer ID (index primary key)
- Username string // Username (unique)
- Password_hash string // Bcrypt password hash
- Created_at int64 // Epoch timestamp when the user was added to the database
- Bio string // Short byline set by the user
+ ID int // Integer ID (index primary key)
+ Username string // Username (unique)
+ Created_at int64 // Epoch timestamp when the user was added to the database
+ Bio string // Short byline set by the user
}
// Uses bcrypt to set the users Password_hash from the given password
-func (user *User) SetPassword(ctx context.Context, tx *db.SafeWTX, password string) error {
+func (user *User) SetPassword(
+ tx *sql.Tx,
+ password string,
+) error {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return errors.Wrap(err, "bcrypt.GenerateFromPassword")
}
- user.Password_hash = string(hashedPassword)
+ newPassword := string(hashedPassword)
query := `UPDATE users SET password_hash = ? WHERE id = ?`
- _, err = tx.Exec(ctx, query, user.Password_hash, user.ID)
+ _, err = tx.Exec(query, newPassword, user.ID)
if err != nil {
return errors.Wrap(err, "tx.Exec")
}
@@ -32,8 +33,15 @@ func (user *User) SetPassword(ctx context.Context, tx *db.SafeWTX, password stri
}
// Uses bcrypt to check if the given password matches the users Password_hash
-func (user *User) CheckPassword(password string) error {
- err := bcrypt.CompareHashAndPassword([]byte(user.Password_hash), []byte(password))
+func (user *User) CheckPassword(tx *sql.Tx, password string) error {
+ query := `SELECT password_hash FROM users WHERE id = ? LIMIT 1`
+ row := tx.QueryRow(query, user.ID)
+ hashedPassword := ""
+ err := row.Scan(&hashedPassword)
+ if err != nil {
+ return errors.Wrap(err, "row.Scan")
+ }
+ err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
if err != nil {
return errors.Wrap(err, "bcrypt.CompareHashAndPassword")
}
@@ -41,9 +49,9 @@ func (user *User) CheckPassword(password string) error {
}
// Change the user's username
-func (user *User) ChangeUsername(ctx context.Context, tx *db.SafeWTX, newUsername string) error {
+func (user *User) ChangeUsername(tx *sql.Tx, newUsername string) error {
query := `UPDATE users SET username = ? WHERE id = ?`
- _, err := tx.Exec(ctx, query, newUsername, user.ID)
+ _, err := tx.Exec(query, newUsername, user.ID)
if err != nil {
return errors.Wrap(err, "tx.Exec")
}
@@ -51,9 +59,9 @@ func (user *User) ChangeUsername(ctx context.Context, tx *db.SafeWTX, newUsernam
}
// Change the user's bio
-func (user *User) ChangeBio(ctx context.Context, tx *db.SafeWTX, newBio string) error {
+func (user *User) ChangeBio(tx *sql.Tx, newBio string) error {
query := `UPDATE users SET bio = ? WHERE id = ?`
- _, err := tx.Exec(ctx, query, newBio, user.ID)
+ _, err := tx.Exec(query, newBio, user.ID)
if err != nil {
return errors.Wrap(err, "tx.Exec")
}
diff --git a/internal/models/user_functions.go b/internal/models/user_functions.go
index 2bfea8a..da4e5e0 100644
--- a/internal/models/user_functions.go
+++ b/internal/models/user_functions.go
@@ -1,31 +1,28 @@
package models
import (
- "context"
"database/sql"
"fmt"
- "projectreshoot/pkg/db"
"github.com/pkg/errors"
)
// Creates a new user in the database and returns a pointer
func CreateNewUser(
- ctx context.Context,
- tx *db.SafeWTX,
+ tx *sql.Tx,
username string,
password string,
) (*User, error) {
query := `INSERT INTO users (username) VALUES (?)`
- _, err := tx.Exec(ctx, query, username)
+ _, err := tx.Exec(query, username)
if err != nil {
return nil, errors.Wrap(err, "tx.Exec")
}
- user, err := GetUserFromUsername(ctx, tx, username)
+ user, err := GetUserFromUsername(tx, username)
if err != nil {
return nil, errors.Wrap(err, "GetUserFromUsername")
}
- err = user.SetPassword(ctx, tx, password)
+ err = user.SetPassword(tx, password)
if err != nil {
return nil, errors.Wrap(err, "user.SetPassword")
}
@@ -34,23 +31,21 @@ func CreateNewUser(
// Fetches data from the users table using "WHERE column = 'value'"
func fetchUserData(
- ctx context.Context,
- tx db.SafeTX,
+ tx *sql.Tx,
column string,
- value interface{},
+ value any,
) (*sql.Rows, error) {
query := fmt.Sprintf(
`SELECT
id,
username,
- password_hash,
created_at,
bio
FROM users
WHERE %s = ? COLLATE NOCASE LIMIT 1`,
column,
)
- rows, err := tx.Query(ctx, query, value)
+ rows, err := tx.Query(query, value)
if err != nil {
return nil, errors.Wrap(err, "tx.Query")
}
@@ -66,7 +61,6 @@ func scanUserRow(user *User, rows *sql.Rows) error {
err := rows.Scan(
&user.ID,
&user.Username,
- &user.Password_hash,
&user.Created_at,
&user.Bio,
)
@@ -78,8 +72,8 @@ func scanUserRow(user *User, rows *sql.Rows) error {
// Queries the database for a user matching the given username.
// Query is case insensitive
-func GetUserFromUsername(ctx context.Context, tx db.SafeTX, username string) (*User, error) {
- rows, err := fetchUserData(ctx, tx, "username", username)
+func GetUserFromUsername(tx *sql.Tx, username string) (*User, error) {
+ rows, err := fetchUserData(tx, "username", username)
if err != nil {
return nil, errors.Wrap(err, "fetchUserData")
}
@@ -93,8 +87,8 @@ func GetUserFromUsername(ctx context.Context, tx db.SafeTX, username string) (*U
}
// Queries the database for a user matching the given ID.
-func GetUserFromID(ctx context.Context, tx db.SafeTX, id int) (*User, error) {
- rows, err := fetchUserData(ctx, tx, "id", id)
+func GetUserFromID(tx *sql.Tx, id int) (*User, error) {
+ rows, err := fetchUserData(tx, "id", id)
if err != nil {
return nil, errors.Wrap(err, "fetchUserData")
}
@@ -108,9 +102,9 @@ func GetUserFromID(ctx context.Context, tx db.SafeTX, id int) (*User, error) {
}
// Checks if the given username is unique. Returns true if not taken
-func CheckUsernameUnique(ctx context.Context, tx db.SafeTX, username string) (bool, error) {
+func CheckUsernameUnique(tx *sql.Tx, username string) (bool, error) {
query := `SELECT 1 FROM users WHERE username = ? COLLATE NOCASE LIMIT 1`
- rows, err := tx.Query(ctx, query, username)
+ rows, err := tx.Query(query, username)
if err != nil {
return false, errors.Wrap(err, "tx.Query")
}
diff --git a/internal/view/component/account/changebio_templ.go b/internal/view/component/account/changebio_templ.go
deleted file mode 100644
index 935782e..0000000
--- a/internal/view/component/account/changebio_templ.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package account
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/pkg/contexts"
-
-func ChangeBio(err string, bio string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
-
- user := contexts.GetUser(ctx)
- if bio == "" {
- bio = user.Bio
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/account/changepassword_templ.go b/internal/view/component/account/changepassword_templ.go
deleted file mode 100644
index 4808626..0000000
--- a/internal/view/component/account/changepassword_templ.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package account
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-func ChangePassword(err string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/account/changeusername_templ.go b/internal/view/component/account/changeusername_templ.go
deleted file mode 100644
index 22f647b..0000000
--- a/internal/view/component/account/changeusername_templ.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package account
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/pkg/contexts"
-
-func ChangeUsername(err string, username string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
-
- user := contexts.GetUser(ctx)
- if username == "" {
- username = user.Username
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/account/container_templ.go b/internal/view/component/account/container_templ.go
deleted file mode 100644
index 5cb89da..0000000
--- a/internal/view/component/account/container_templ.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package account
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-func AccountContainer(subpage string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = SelectMenu(subpage).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var2 string
- templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(subpage)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/account/container.templ`, Line: 16, Col: 13}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- switch subpage {
- case "General":
- templ_7745c5c3_Err = AccountGeneral().Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- case "Security":
- templ_7745c5c3_Err = AccountSecurity().Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/account/general_templ.go b/internal/view/component/account/general_templ.go
deleted file mode 100644
index 39d9a7a..0000000
--- a/internal/view/component/account/general_templ.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package account
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-func AccountGeneral() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = ChangeUsername("", "").Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = ChangeBio("", "").Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/account/security_templ.go b/internal/view/component/account/security_templ.go
deleted file mode 100644
index a0b15e5..0000000
--- a/internal/view/component/account/security_templ.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package account
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-func AccountSecurity() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = ChangePassword("").Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/account/selectmenu_templ.go b/internal/view/component/account/selectmenu_templ.go
deleted file mode 100644
index 86021b5..0000000
--- a/internal/view/component/account/selectmenu_templ.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package account
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "fmt"
-
-type MenuItem struct {
- name string
- href string
-}
-
-func getMenuItems() []MenuItem {
- return []MenuItem{
- {
- name: "General",
- href: "general",
- },
- {
- name: "Security",
- href: "security",
- },
- {
- name: "Preferences",
- href: "preferences",
- },
- }
-}
-
-func SelectMenu(activePage string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
-
- menuItems := getMenuItems()
- page := fmt.Sprintf("{page:'%s'}", activePage)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, item := range menuItems {
-
- activebind := fmt.Sprintf("page === '%s' && 'bg-mantle'", item.name)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(item.name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/account/selectmenu.templ`, Line: 83, Col: 19}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/footer/footer_templ.go b/internal/view/component/footer/footer_templ.go
deleted file mode 100644
index 359e1ff..0000000
--- a/internal/view/component/footer/footer_templ.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package footer
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-type FooterItem struct {
- name string
- href string
-}
-
-// Specify the links to show in the footer
-func getFooterItems() []FooterItem {
- return []FooterItem{
- {
- name: "About",
- href: "/about",
- },
- {
- name: "Github",
- href: "https://github.com/haelnorr/projectreshoot",
- },
- }
-}
-
-// Returns the template fragment for the Footer
-func Footer() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Project Reshoot
A better way to discover and rate films
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, item := range getFooterItems() {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/footer/footer.templ`, Line: 71, Col: 19}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/form/confirmpass_templ.go b/internal/view/component/form/confirmpass_templ.go
deleted file mode 100644
index ceed0b2..0000000
--- a/internal/view/component/form/confirmpass_templ.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package form
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-func ConfirmPassword(err string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/form/loginform_templ.go b/internal/view/component/form/loginform_templ.go
deleted file mode 100644
index e8bc085..0000000
--- a/internal/view/component/form/loginform_templ.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package form
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-// Login Form. If loginError is not an empty string, it will display the
-// contents of loginError to the user.
-// If loginError is "Username or password incorrect" it will also show
-// error icons on the username and password field
-func LoginForm(loginError string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- credErr := "Username or password incorrect"
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/form/registerform_templ.go b/internal/view/component/form/registerform_templ.go
deleted file mode 100644
index d332b3f..0000000
--- a/internal/view/component/form/registerform_templ.go
+++ /dev/null
@@ -1,63 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package form
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-// Login Form. If loginError is not an empty string, it will display the
-// contents of loginError to the user.
-func RegisterForm(registerError string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
-
- usernameErr := "Username is taken"
- passErrs := []string{
- "Password exceeds maximum length of 72 bytes",
- "Passwords do not match",
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/nav/navbar_templ.go b/internal/view/component/nav/navbar_templ.go
deleted file mode 100644
index 7e7b2f8..0000000
--- a/internal/view/component/nav/navbar_templ.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package nav
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-type NavItem struct {
- name string // Label to display
- href string // Link reference
-}
-
-// Return the list of navbar links
-func getNavItems() []NavItem {
- return []NavItem{
- {
- name: "Movies",
- href: "/movies",
- },
- }
-}
-
-// Returns the navbar template fragment
-func Navbar() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- navItems := getNavItems()
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = sideNav(navItems).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/nav/navbarleft_templ.go b/internal/view/component/nav/navbarleft_templ.go
deleted file mode 100644
index 119bb65..0000000
--- a/internal/view/component/nav/navbarleft_templ.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package nav
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-// Returns the left portion of the navbar
-func navLeft(navItems []NavItem) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, item := range navItems {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/nav/navbarleft.templ`, Line: 13, Col: 17}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/nav/navbarright_templ.go b/internal/view/component/nav/navbarright_templ.go
deleted file mode 100644
index 98515bc..0000000
--- a/internal/view/component/nav/navbarright_templ.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package nav
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/pkg/contexts"
-
-type ProfileItem struct {
- name string // Label to display
- href string // Link reference
-}
-
-// Return the list of profile links
-func getProfileItems() []ProfileItem {
- return []ProfileItem{
- {
- name: "Profile",
- href: "/profile",
- },
- {
- name: "Account",
- href: "/account",
- },
- }
-}
-
-// Returns the right portion of the navbar
-func navRight() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- user := contexts.GetUser(ctx)
- items := getProfileItems()
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if user != nil {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
Profile ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var2 string
- templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/nav/navbarright.templ`, Line: 41, Col: 22}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, item := range items {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(item.name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/nav/navbarright.templ`, Line: 63, Col: 20}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
Logout ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
Login Register ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
Toggle menu ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/nav/sidenav_templ.go b/internal/view/component/nav/sidenav_templ.go
deleted file mode 100644
index 3fb57d6..0000000
--- a/internal/view/component/nav/sidenav_templ.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package nav
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/pkg/contexts"
-
-// Returns the mobile version of the navbar thats only visible when activated
-func sideNav(navItems []NavItem) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- user := contexts.GetUser(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, item := range navItems {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/nav/sidenav.templ`, Line: 22, Col: 18}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if user == nil {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/popup/confirmPasswordModal_templ.go b/internal/view/component/popup/confirmPasswordModal_templ.go
deleted file mode 100644
index eaee3cd..0000000
--- a/internal/view/component/popup/confirmPasswordModal_templ.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-
-package popup
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/component/form"
-
-func ConfirmPasswordModal() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "To complete this action you need to confirm your password
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = form.ConfirmPassword("").Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/popup/error500Popup_templ.go b/internal/view/component/popup/error500Popup_templ.go
deleted file mode 100644
index cd21eef..0000000
--- a/internal/view/component/popup/error500Popup_templ.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package popup
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-func Error500Popup() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "An error occured on the server. Please try again later, or contact an administrator
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/popup/error503Popup_templ.go b/internal/view/component/popup/error503Popup_templ.go
deleted file mode 100644
index e542023..0000000
--- a/internal/view/component/popup/error503Popup_templ.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package popup
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-func Error503Popup() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "The service is currently available. It could be down for maintenance. Please try again later.
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/component/search/movies_results.templ b/internal/view/component/search/movies_results.templ
index d8d8e7a..3dce75c 100644
--- a/internal/view/component/search/movies_results.templ
+++ b/internal/view/component/search/movies_results.templ
@@ -1,7 +1,7 @@
package search
-import "projectreshoot/pkg/tmdb"
import "fmt"
+import "git.haelnorr.com/h/golib/tmdb"
templ MovieResults(movies *tmdb.ResultMovies, image *tmdb.Image) {
for _, movie := range movies.Results {
@@ -18,23 +18,23 @@ templ MovieResults(movies *tmdb.ResultMovies, image *tmdb.Image) {
onerror="this.onerror=null; setFallbackColor(this);"
/>
+ function setFallbackColor(img) {
+ const baseColor = getComputedStyle(document.documentElement).
+ getPropertyValue('--base').trim();
+ img.src = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='144'%3E%3Crect width='100%' height='100%' fill='${baseColor}'/%3E%3C/svg%3E`;
+ }
+
{ movie.Title } { movie.ReleaseYear() }
- Released:
+ Released:
{ movie.ReleaseDate }
- Original Title:
+ Original Title:
{ movie.OriginalTitle }
{ movie.Overview }
diff --git a/internal/view/component/search/movies_results_templ.go b/internal/view/component/search/movies_results_templ.go
deleted file mode 100644
index 70ca412..0000000
--- a/internal/view/component/search/movies_results_templ.go
+++ /dev/null
@@ -1,132 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package search
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/pkg/tmdb"
-import "fmt"
-
-func MovieResults(movies *tmdb.ResultMovies, image *tmdb.Image) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- for _, movie := range movies.Results {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(movie.Title)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/search/movies_results.templ`, Line: 31, Col: 18}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(movie.ReleaseYear())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/search/movies_results.templ`, Line: 31, Col: 42}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " Released: ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var6 string
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(movie.ReleaseDate)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/search/movies_results.templ`, Line: 34, Col: 50}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
Original Title: ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var7 string
- templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(movie.OriginalTitle)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/search/movies_results.templ`, Line: 38, Col: 52}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var8 string
- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(movie.Overview)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/component/search/movies_results.templ`, Line: 40, Col: 45}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/layout/global_templ.go b/internal/view/layout/global_templ.go
deleted file mode 100644
index cfca892..0000000
--- a/internal/view/layout/global_templ.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package layout
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/component/nav"
-import "projectreshoot/internal/view/component/footer"
-import "projectreshoot/internal/view/component/popup"
-
-// Global page layout. Includes HTML document settings, header tags
-// navbar and footer
-func Global(title string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var2 string
- templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/layout/global.templ`, Line: 36, Col: 17}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = popup.Error500Popup().Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = popup.Error503Popup().Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = popup.ConfirmPasswordModal().Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = nav.Navbar().Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = footer.Footer().Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/about_templ.go b/internal/view/page/about_templ.go
deleted file mode 100644
index f449951..0000000
--- a/internal/view/page/about_templ.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-
-// Returns the about page content
-func About() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
About
What is Project Reshoot?
Project Reshoot is a movie review site that aims to provide a better experience for the users. Instead of a single number that shows the average, or a spread of star ratings, Project Reshoot asks you to rate movies with a vibe. These ratings are shown as an easy to see pie chart showing how everyone felt.
The other major feature is the ability for you to customize what details you see about movies, hiding details you don't want to see until after you've watched it. This gives you peace of mind when searching for new movies to watch.
Why the name?
The name came partially from the premise of wanting to deliver a new take on movie reviews, and partially from it being rewritten from scratch in a new technology stack (Goodbye NextJS, Hello GOTH).
Who's behind it?
Currently Project Reshoot is being built by a team of 1. It is somewhat of a passion project and a way to practice my development skills. For the time being, it will likely stay that way, but if you want to contribute, you can check out the
Github repo here .
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global("About").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/account_templ.go b/internal/view/page/account_templ.go
deleted file mode 100644
index 3f8c7fa..0000000
--- a/internal/view/page/account_templ.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-import "projectreshoot/internal/view/component/account"
-
-func Account(subpage string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = account.AccountContainer(subpage).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global("Account - "+subpage).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/error_templ.go b/internal/view/page/error_templ.go
deleted file mode 100644
index 9a3f240..0000000
--- a/internal/view/page/error_templ.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-import "strconv"
-
-// Page template for Error pages. Error code should be a HTTP status code as
-// a string, and err should be the corresponding response title.
-// Message is a custom error message displayed below the code and error.
-func Error(code int, err string, message string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(code))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/error.templ`, Line: 18, Col: 25}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(err)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/error.templ`, Line: 22, Col: 10}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(message)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/error.templ`, Line: 25, Col: 14}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
Go to homepage ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global(err).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/index_templ.go b/internal/view/page/index_templ.go
deleted file mode 100644
index 1e53b57..0000000
--- a/internal/view/page/index_templ.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-
-// Page content for the index page
-func Index() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
Project Reshoot
A better way to discover and rate films
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global("Project Reshoot").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/login_templ.go b/internal/view/page/login_templ.go
deleted file mode 100644
index 923ea97..0000000
--- a/internal/view/page/login_templ.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-import "projectreshoot/internal/view/component/form"
-
-// Returns the login page
-func Login() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
Login Don't have an account yet? Sign up here
Or
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = form.LoginForm("").Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global("Login").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/movie.templ b/internal/view/page/movie.templ
index 3bc38ae..3851d1e 100644
--- a/internal/view/page/movie.templ
+++ b/internal/view/page/movie.templ
@@ -1,15 +1,12 @@
package page
-import "projectreshoot/pkg/tmdb"
+import "git.haelnorr.com/h/golib/tmdb"
import "projectreshoot/internal/view/layout"
templ Movie(movie *tmdb.Movie, credits *tmdb.Credits, image *tmdb.Image) {
@layout.Global(movie.Title) {
-
+
for _, billedcrew := range credits.BilledCrew() {
{ billedcrew.Name }
@@ -20,7 +17,7 @@ templ Movie(movie *tmdb.Movie, credits *tmdb.Credits, image *tmdb.Image) {
+ const resizeObs = new ResizeObserver(() => {
+ moveBilledCrew();
+ });
+ resizeObs.observe(document.body);
+
diff --git a/internal/view/page/movie_search_templ.go b/internal/view/page/movie_search_templ.go
deleted file mode 100644
index 5d82b0e..0000000
--- a/internal/view/page/movie_search_templ.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-
-func Movies() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global("Search movies").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/movie_templ.go b/internal/view/page/movie_templ.go
deleted file mode 100644
index f3adedd..0000000
--- a/internal/view/page/movie_templ.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/pkg/tmdb"
-import "projectreshoot/internal/view/layout"
-
-func Movie(movie *tmdb.Movie, credits *tmdb.Credits, image *tmdb.Image) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, billedcrew := range credits.BilledCrew() {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(billedcrew.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 15, Col: 47}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(billedcrew.FRoles())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 16, Col: 55}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var6 string
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(movie.Title)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 58, Col: 19}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var7 string
- templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(movie.FGenres())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 61, Col: 23}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " • ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var8 string
- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(movie.FRuntime())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 62, Col: 33}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " • ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var9 string
- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(movie.ReleaseYear())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 63, Col: 36}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var10 string
- templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(movie.Tagline)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 77, Col: 22}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
Overview ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var11 string
- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(movie.Overview)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/movie.templ`, Line: 86, Col: 23}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global(movie.Title).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/profile_templ.go b/internal/view/page/profile_templ.go
deleted file mode 100644
index 1a3b4ed..0000000
--- a/internal/view/page/profile_templ.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-import "projectreshoot/pkg/contexts"
-
-func Profile() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- user := contexts.GetUser(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Hello, ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/page/profile.templ`, Line: 10, Col: 25}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global("Profile - "+user.Username).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/internal/view/page/register_templ.go b/internal/view/page/register_templ.go
deleted file mode 100644
index d43a1e6..0000000
--- a/internal/view/page/register_templ.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.833
-package page
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import "projectreshoot/internal/view/layout"
-import "projectreshoot/internal/view/component/form"
-
-// Returns the login page
-func Register() templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Register Already have an account? Login here
Or
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = form.RegisterForm("").Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = layout.Global("Register").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-var _ = templruntime.GeneratedTemplate
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 7110e9d..534a05e 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -5,12 +5,10 @@ import (
"os"
"time"
- "projectreshoot/pkg/logging"
- "projectreshoot/pkg/tmdb"
-
+ "git.haelnorr.com/h/golib/hlog"
+ "git.haelnorr.com/h/golib/tmdb"
"github.com/joho/godotenv"
"github.com/pkg/errors"
- "github.com/rs/zerolog"
)
type Config struct {
@@ -28,7 +26,7 @@ type Config struct {
AccessTokenExpiry int64 // Access token expiry in minutes
RefreshTokenExpiry int64 // Refresh token expiry in minutes
TokenFreshTime int64 // Time for tokens to stay fresh in minutes
- LogLevel zerolog.Level // Log level for global logging. Defaults to info
+ LogLevel hlog.Level // Log level for global logging. Defaults to info
LogOutput string // "file", "console", or "both". Defaults to console
LogDir string // Path to create log files
TMDBToken string // Read access token for TMDB API
@@ -41,7 +39,7 @@ func GetConfig(args map[string]string) (*Config, error) {
var (
host string
port string
- logLevel zerolog.Level
+ logLevel hlog.Level
logOutput string
valid bool
)
@@ -57,9 +55,9 @@ func GetConfig(args map[string]string) (*Config, error) {
port = GetEnvDefault("PORT", "3010")
}
if args["loglevel"] != "" {
- logLevel = logging.GetLogLevel(args["loglevel"])
+ logLevel = hlog.LogLevel(args["loglevel"])
} else {
- logLevel = logging.GetLogLevel(GetEnvDefault("LOG_LEVEL", "info"))
+ logLevel = hlog.LogLevel(GetEnvDefault("LOG_LEVEL", "info"))
}
if args["logoutput"] != "" {
opts := map[string]string{
diff --git a/pkg/cookies/tokens.go b/pkg/cookies/tokens.go
index 696758c..bd7079c 100644
--- a/pkg/cookies/tokens.go
+++ b/pkg/cookies/tokens.go
@@ -6,8 +6,8 @@ import (
"projectreshoot/internal/models"
"projectreshoot/pkg/config"
- "projectreshoot/pkg/jwt"
+ "git.haelnorr.com/h/golib/jwt"
"github.com/pkg/errors"
)
@@ -58,15 +58,16 @@ func SetTokenCookies(
w http.ResponseWriter,
r *http.Request,
config *config.Config,
+ tokenGen *jwt.TokenGenerator,
user *models.User,
fresh bool,
rememberMe bool,
) error {
- at, atexp, err := jwt.GenerateAccessToken(config, user, fresh, rememberMe)
+ at, atexp, err := tokenGen.NewAccess(user.ID, fresh, rememberMe)
if err != nil {
return errors.Wrap(err, "jwt.GenerateAccessToken")
}
- rt, rtexp, err := jwt.GenerateRefreshToken(config, user, rememberMe)
+ rt, rtexp, err := tokenGen.NewRefresh(user.ID, rememberMe)
if err != nil {
return errors.Wrap(err, "jwt.GenerateRefreshToken")
}
diff --git a/pkg/db/connection.go b/pkg/db/connection.go
deleted file mode 100644
index a5eb718..0000000
--- a/pkg/db/connection.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package db
-
-import (
- "database/sql"
- "fmt"
- "strconv"
-
- "github.com/pkg/errors"
- "github.com/rs/zerolog"
-
- _ "github.com/mattn/go-sqlite3"
-)
-
-// Returns a database connection handle for the DB
-func ConnectToDatabase(
- dbName string,
- logger *zerolog.Logger,
-) (*SafeConn, error) {
- opts := "_journal_mode=WAL&_synchronous=NORMAL&_txlock=IMMEDIATE"
- file := fmt.Sprintf("file:%s.db?%s", dbName, opts)
- wconn, err := sql.Open("sqlite3", file)
- if err != nil {
- return nil, errors.Wrap(err, "sql.Open (rw)")
- }
- wconn.SetMaxOpenConns(1)
- opts = "_synchronous=NORMAL&mode=ro"
- file = fmt.Sprintf("file:%s.db?%s", dbName, opts)
-
- rconn, err := sql.Open("sqlite3", file)
- if err != nil {
- return nil, errors.Wrap(err, "sql.Open (ro)")
- }
-
- version, err := strconv.Atoi(dbName)
- if err != nil {
- return nil, errors.Wrap(err, "strconv.Atoi")
- }
- err = checkDBVersion(rconn, version)
- if err != nil {
- return nil, errors.Wrap(err, "checkDBVersion")
- }
- conn := MakeSafe(wconn, rconn, logger)
- return conn, nil
-}
-
-// Check the database version
-func checkDBVersion(db *sql.DB, expectVer int) error {
- query := `SELECT version_id FROM goose_db_version WHERE is_applied = 1
- ORDER BY version_id DESC LIMIT 1`
- rows, err := db.Query(query)
- if err != nil {
- return errors.Wrap(err, "db.Query")
- }
- defer rows.Close()
- if rows.Next() {
- var version int
- err = rows.Scan(&version)
- if err != nil {
- return errors.Wrap(err, "rows.Scan")
- }
- if version != expectVer {
- return errors.New("Version mismatch")
- }
- } else {
- return errors.New("No version found")
- }
- return nil
-}
diff --git a/pkg/db/safeconn.go b/pkg/db/safeconn.go
deleted file mode 100644
index 0fa1560..0000000
--- a/pkg/db/safeconn.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package db
-
-import (
- "context"
- "database/sql"
- "time"
-
- "github.com/pkg/errors"
- "github.com/rs/zerolog"
-)
-
-type SafeConn struct {
- wconn *sql.DB
- rconn *sql.DB
- readLockCount uint32
- globalLockStatus uint32
- globalLockRequested uint32
- logger *zerolog.Logger
-}
-
-// Make the provided db handle safe and attach a logger to it
-func MakeSafe(wconn *sql.DB, rconn *sql.DB, logger *zerolog.Logger) *SafeConn {
- return &SafeConn{wconn: wconn, rconn: rconn, logger: logger}
-}
-
-// Attempts to acquire a global lock on the database connection
-func (conn *SafeConn) acquireGlobalLock() bool {
- if conn.readLockCount > 0 || conn.globalLockStatus == 1 {
- return false
- }
- conn.globalLockStatus = 1
- conn.logger.Debug().Uint32("global_lock_status", conn.globalLockStatus).
- Msg("Global lock acquired")
- return true
-}
-
-// Releases a global lock on the database connection
-func (conn *SafeConn) releaseGlobalLock() {
- conn.globalLockStatus = 0
- conn.logger.Debug().Uint32("global_lock_status", conn.globalLockStatus).
- Msg("Global lock released")
-}
-
-// Acquire a read lock on the connection. Multiple read locks can be acquired
-// at the same time
-func (conn *SafeConn) acquireReadLock() bool {
- if conn.globalLockStatus == 1 || conn.globalLockRequested == 1 {
- return false
- }
- conn.readLockCount += 1
- conn.logger.Debug().Uint32("read_lock_count", conn.readLockCount).
- Msg("Read lock acquired")
- return true
-}
-
-// Release a read lock. Decrements read lock count by 1
-func (conn *SafeConn) releaseReadLock() {
- conn.readLockCount -= 1
- conn.logger.Debug().Uint32("read_lock_count", conn.readLockCount).
- Msg("Read lock released")
-}
-
-// Starts a new transaction based on the current context. Will cancel if
-// the context is closed/cancelled/done
-func (conn *SafeConn) Begin(ctx context.Context) (*SafeWTX, error) {
- lockAcquired := make(chan struct{})
- lockCtx, cancel := context.WithCancel(ctx)
- defer cancel()
-
- go func() {
- select {
- case <-lockCtx.Done():
- return
- default:
- if conn.acquireReadLock() {
- close(lockAcquired)
- }
- }
- }()
-
- select {
- case <-lockAcquired:
- tx, err := conn.wconn.BeginTx(ctx, nil)
- if err != nil {
- conn.releaseReadLock()
- return nil, err
- }
- return &SafeWTX{tx: tx, sc: conn}, nil
- case <-ctx.Done():
- cancel()
- return nil, errors.New("Transaction time out due to database lock")
- }
-}
-
-// Starts a new READONLY transaction based on the current context. Will cancel if
-// the context is closed/cancelled/done
-func (conn *SafeConn) RBegin(ctx context.Context) (*SafeRTX, error) {
- lockAcquired := make(chan struct{})
- lockCtx, cancel := context.WithCancel(ctx)
- defer cancel()
-
- go func() {
- select {
- case <-lockCtx.Done():
- return
- default:
- if conn.acquireReadLock() {
- close(lockAcquired)
- }
- }
- }()
-
- select {
- case <-lockAcquired:
- tx, err := conn.rconn.BeginTx(ctx, nil)
- if err != nil {
- conn.releaseReadLock()
- return nil, err
- }
- return &SafeRTX{tx: tx, sc: conn}, nil
- case <-ctx.Done():
- cancel()
- return nil, errors.New("Transaction time out due to database lock")
- }
-}
-
-// Acquire a global lock, preventing all transactions
-func (conn *SafeConn) Pause(timeoutAfter time.Duration) {
- conn.logger.Info().Msg("Attempting to acquire global database lock")
- conn.globalLockRequested = 1
- defer func() { conn.globalLockRequested = 0 }()
- timeout := time.After(timeoutAfter)
- attempt := 0
- for {
- if conn.acquireGlobalLock() {
- conn.logger.Info().Msg("Global database lock acquired")
- return
- }
- select {
- case <-timeout:
- conn.logger.Info().Msg("Timeout: Global database lock abandoned")
- return
- case <-time.After(100 * time.Millisecond):
- attempt++
- }
- }
-}
-
-// Release the global lock
-func (conn *SafeConn) Resume() {
- conn.releaseGlobalLock()
- conn.logger.Info().Msg("Global database lock released")
-}
-
-// Close the database connection
-func (conn *SafeConn) Close() error {
- conn.logger.Debug().Msg("Acquiring global lock for connection close")
- conn.acquireGlobalLock()
- defer conn.releaseGlobalLock()
- conn.logger.Debug().Msg("Closing database connection")
- return conn.wconn.Close()
-}
diff --git a/pkg/db/safeconntx_test.go b/pkg/db/safeconntx_test.go
deleted file mode 100644
index 35d483a..0000000
--- a/pkg/db/safeconntx_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package db
-
-import (
- "context"
- "projectreshoot/pkg/tests"
- "strconv"
- "sync"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestSafeConn(t *testing.T) {
- cfg, err := tests.TestConfig()
- require.NoError(t, err)
- logger := tests.NilLogger()
- ver, err := strconv.ParseInt(cfg.DBName, 10, 0)
- require.NoError(t, err)
- wconn, rconn, err := tests.SetupTestDB(ver)
- require.NoError(t, err)
- sconn := MakeSafe(wconn, rconn, logger)
- defer sconn.Close()
-
- t.Run("Global lock waits for read locks to finish", func(t *testing.T) {
- tx, err := sconn.Begin(t.Context())
- require.NoError(t, err)
- var requested sync.WaitGroup
- var engaged sync.WaitGroup
- requested.Add(1)
- engaged.Add(1)
- go func() {
- requested.Done()
- sconn.Pause(5 * time.Second)
- engaged.Done()
- }()
- requested.Wait()
- assert.Equal(t, uint32(0), sconn.globalLockStatus)
- assert.Equal(t, uint32(1), sconn.globalLockRequested)
- tx.Commit()
- engaged.Wait()
- assert.Equal(t, uint32(1), sconn.globalLockStatus)
- assert.Equal(t, uint32(0), sconn.globalLockRequested)
- sconn.Resume()
- })
- t.Run("Lock abandons after timeout", func(t *testing.T) {
- tx, err := sconn.Begin(t.Context())
- require.NoError(t, err)
- sconn.Pause(250 * time.Millisecond)
- assert.Equal(t, uint32(0), sconn.globalLockStatus)
- assert.Equal(t, uint32(0), sconn.globalLockRequested)
- tx.Commit()
- })
- t.Run("Pause blocks transactions and resume allows", func(t *testing.T) {
- tx, err := sconn.Begin(t.Context())
- require.NoError(t, err)
- var requested sync.WaitGroup
- var engaged sync.WaitGroup
- requested.Add(1)
- engaged.Add(1)
- go func() {
- requested.Done()
- sconn.Pause(5 * time.Second)
- engaged.Done()
- }()
- requested.Wait()
- assert.Equal(t, uint32(0), sconn.globalLockStatus)
- assert.Equal(t, uint32(1), sconn.globalLockRequested)
- ctx, cancel := context.WithTimeout(t.Context(), 250*time.Millisecond)
- defer cancel()
- _, err = sconn.Begin(ctx)
- require.Error(t, err)
- tx.Commit()
- engaged.Wait()
- _, err = sconn.Begin(ctx)
- require.Error(t, err)
- sconn.Resume()
- tx, err = sconn.Begin(t.Context())
- require.NoError(t, err)
- tx.Commit()
- })
-}
-func TestSafeTX(t *testing.T) {
- cfg, err := tests.TestConfig()
- require.NoError(t, err)
- logger := tests.NilLogger()
- ver, err := strconv.ParseInt(cfg.DBName, 10, 0)
- require.NoError(t, err)
- wconn, rconn, err := tests.SetupTestDB(ver)
- require.NoError(t, err)
- sconn := MakeSafe(wconn, rconn, logger)
- defer sconn.Close()
-
- t.Run("Commit releases lock", func(t *testing.T) {
- tx, err := sconn.Begin(t.Context())
- require.NoError(t, err)
- assert.Equal(t, uint32(1), sconn.readLockCount)
- tx.Commit()
- assert.Equal(t, uint32(0), sconn.readLockCount)
- })
- t.Run("Rollback releases lock", func(t *testing.T) {
- tx, err := sconn.Begin(t.Context())
- require.NoError(t, err)
- assert.Equal(t, uint32(1), sconn.readLockCount)
- tx.Rollback()
- assert.Equal(t, uint32(0), sconn.readLockCount)
- })
- t.Run("Multiple RTX can gain read lock", func(t *testing.T) {
- tx1, err := sconn.RBegin(t.Context())
- require.NoError(t, err)
- tx2, err := sconn.RBegin(t.Context())
- require.NoError(t, err)
- tx3, err := sconn.RBegin(t.Context())
- require.NoError(t, err)
- tx1.Commit()
- tx2.Commit()
- tx3.Commit()
- })
- t.Run("Lock acquiring times out after timeout", func(t *testing.T) {
- ctx, cancel := context.WithTimeout(t.Context(), 250*time.Millisecond)
- defer cancel()
- sconn.acquireGlobalLock()
- defer sconn.releaseGlobalLock()
- _, err := sconn.Begin(ctx)
- require.Error(t, err)
- })
- t.Run("Lock acquires if lock released", func(t *testing.T) {
- ctx, cancel := context.WithTimeout(t.Context(), 250*time.Millisecond)
- defer cancel()
- sconn.acquireGlobalLock()
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- tx, err := sconn.Begin(ctx)
- require.NoError(t, err)
- tx.Commit()
- wg.Done()
- }()
- sconn.releaseGlobalLock()
- wg.Wait()
- })
-}
diff --git a/pkg/db/safetx.go b/pkg/db/safetx.go
deleted file mode 100644
index 070b23f..0000000
--- a/pkg/db/safetx.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package db
-
-import (
- "context"
- "database/sql"
- "regexp"
- "strings"
-
- "github.com/pkg/errors"
-)
-
-type SafeTX interface {
- Query(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
- QueryRow(ctx context.Context, query string, args ...interface{}) (*sql.Row, error)
- Commit() error
- Rollback() error
-}
-
-// Extends sql.Tx for use with SafeConn
-type SafeWTX struct {
- tx *sql.Tx
- sc *SafeConn
-}
-type SafeRTX struct {
- tx *sql.Tx
- sc *SafeConn
-}
-
-func isWriteOperation(query string) bool {
- query = strings.TrimSpace(query)
- query = strings.ToUpper(query)
- writeOpsRegex := `^(INSERT|UPDATE|DELETE|REPLACE|MERGE|CREATE|DROP|ALTER|TRUNCATE)\s+`
- re := regexp.MustCompile(writeOpsRegex)
- return re.MatchString(query)
-}
-
-// Query the database inside the transaction
-func (stx *SafeRTX) Query(
- ctx context.Context,
- query string,
- args ...interface{},
-) (*sql.Rows, error) {
- if stx.tx == nil {
- return nil, errors.New("Cannot query without a transaction")
- }
- if isWriteOperation(query) {
- return nil, errors.New("Cannot query with a write operation")
- }
- rows, err := stx.tx.QueryContext(ctx, query, args...)
- if err != nil {
- return nil, errors.Wrap(err, "tx.QueryContext")
- }
- return rows, nil
-}
-
-// Query the database inside the transaction
-func (stx *SafeWTX) Query(
- ctx context.Context,
- query string,
- args ...interface{},
-) (*sql.Rows, error) {
- if stx.tx == nil {
- return nil, errors.New("Cannot query without a transaction")
- }
- if isWriteOperation(query) {
- return nil, errors.New("Cannot query with a write operation")
- }
- rows, err := stx.tx.QueryContext(ctx, query, args...)
- if err != nil {
- return nil, errors.Wrap(err, "tx.QueryContext")
- }
- return rows, nil
-}
-
-// Query a row from the database inside the transaction
-func (stx *SafeRTX) QueryRow(
- ctx context.Context,
- query string,
- args ...interface{},
-) (*sql.Row, error) {
- if stx.tx == nil {
- return nil, errors.New("Cannot query without a transaction")
- }
- if isWriteOperation(query) {
- return nil, errors.New("Cannot query with a write operation")
- }
- return stx.tx.QueryRowContext(ctx, query, args...), nil
-}
-
-// Query a row from the database inside the transaction
-func (stx *SafeWTX) QueryRow(
- ctx context.Context,
- query string,
- args ...interface{},
-) (*sql.Row, error) {
- if stx.tx == nil {
- return nil, errors.New("Cannot query without a transaction")
- }
- if isWriteOperation(query) {
- return nil, errors.New("Cannot query with a write operation")
- }
- return stx.tx.QueryRowContext(ctx, query, args...), nil
-}
-
-// Exec a statement on the database inside the transaction
-func (stx *SafeWTX) Exec(
- ctx context.Context,
- query string,
- args ...interface{},
-) (sql.Result, error) {
- if stx.tx == nil {
- return nil, errors.New("Cannot exec without a transaction")
- }
- res, err := stx.tx.ExecContext(ctx, query, args...)
- if err != nil {
- return nil, errors.Wrap(err, "tx.ExecContext")
- }
- return res, nil
-}
-
-// Commit the current transaction and release the read lock
-func (stx *SafeRTX) Commit() error {
- if stx.tx == nil {
- return errors.New("Cannot commit without a transaction")
- }
- err := stx.tx.Commit()
- stx.tx = nil
- stx.sc.releaseReadLock()
- return err
-}
-
-// Commit the current transaction and release the read lock
-func (stx *SafeWTX) Commit() error {
- if stx.tx == nil {
- return errors.New("Cannot commit without a transaction")
- }
- err := stx.tx.Commit()
- stx.tx = nil
- stx.sc.releaseReadLock()
- return err
-}
-
-// Abort the current transaction, releasing the read lock
-func (stx *SafeRTX) Rollback() error {
- if stx.tx == nil {
- return errors.New("Cannot rollback without a transaction")
- }
- err := stx.tx.Rollback()
- stx.tx = nil
- stx.sc.releaseReadLock()
- return err
-}
-
-// Abort the current transaction, releasing the read lock
-func (stx *SafeWTX) Rollback() error {
- if stx.tx == nil {
- return errors.New("Cannot rollback without a transaction")
- }
- err := stx.tx.Rollback()
- stx.tx = nil
- stx.sc.releaseReadLock()
- return err
-}
diff --git a/pkg/embedfs/files/css/output.css b/pkg/embedfs/files/css/output.css
index 6e4e488..c51718a 100644
--- a/pkg/embedfs/files/css/output.css
+++ b/pkg/embedfs/files/css/output.css
@@ -1,282 +1,24 @@
-/*! tailwindcss v4.0.3 | MIT License | https://tailwindcss.com */
+/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
@import url("https://fonts.googleapis.com/css2?family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap");
+@layer properties;
@layer theme, base, components, utilities;
@layer theme {
:root, :host {
- --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
- 'Segoe UI Symbol', 'Noto Color Emoji';
- --font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
- 'Courier New', monospace;
- --color-red-50: oklch(0.971 0.013 17.38);
- --color-red-100: oklch(0.936 0.032 17.717);
- --color-red-200: oklch(0.885 0.062 18.334);
- --color-red-300: oklch(0.808 0.114 19.571);
- --color-red-400: oklch(0.704 0.191 22.216);
- --color-red-500: oklch(0.637 0.237 25.331);
- --color-red-600: oklch(0.577 0.245 27.325);
- --color-red-700: oklch(0.505 0.213 27.518);
- --color-red-800: oklch(0.444 0.177 26.899);
- --color-red-900: oklch(0.396 0.141 25.723);
- --color-red-950: oklch(0.258 0.092 26.042);
- --color-orange-50: oklch(0.98 0.016 73.684);
- --color-orange-100: oklch(0.954 0.038 75.164);
- --color-orange-200: oklch(0.901 0.076 70.697);
- --color-orange-300: oklch(0.837 0.128 66.29);
- --color-orange-400: oklch(0.75 0.183 55.934);
- --color-orange-500: oklch(0.705 0.213 47.604);
- --color-orange-600: oklch(0.646 0.222 41.116);
- --color-orange-700: oklch(0.553 0.195 38.402);
- --color-orange-800: oklch(0.47 0.157 37.304);
- --color-orange-900: oklch(0.408 0.123 38.172);
- --color-orange-950: oklch(0.266 0.079 36.259);
- --color-amber-50: oklch(0.987 0.022 95.277);
- --color-amber-100: oklch(0.962 0.059 95.617);
- --color-amber-200: oklch(0.924 0.12 95.746);
- --color-amber-300: oklch(0.879 0.169 91.605);
- --color-amber-400: oklch(0.828 0.189 84.429);
- --color-amber-500: oklch(0.769 0.188 70.08);
- --color-amber-600: oklch(0.666 0.179 58.318);
- --color-amber-700: oklch(0.555 0.163 48.998);
- --color-amber-800: oklch(0.473 0.137 46.201);
- --color-amber-900: oklch(0.414 0.112 45.904);
- --color-amber-950: oklch(0.279 0.077 45.635);
- --color-yellow-50: oklch(0.987 0.026 102.212);
- --color-yellow-100: oklch(0.973 0.071 103.193);
- --color-yellow-200: oklch(0.945 0.129 101.54);
- --color-yellow-300: oklch(0.905 0.182 98.111);
- --color-yellow-400: oklch(0.852 0.199 91.936);
- --color-yellow-500: oklch(0.795 0.184 86.047);
- --color-yellow-600: oklch(0.681 0.162 75.834);
- --color-yellow-700: oklch(0.554 0.135 66.442);
- --color-yellow-800: oklch(0.476 0.114 61.907);
- --color-yellow-900: oklch(0.421 0.095 57.708);
- --color-yellow-950: oklch(0.286 0.066 53.813);
- --color-lime-50: oklch(0.986 0.031 120.757);
- --color-lime-100: oklch(0.967 0.067 122.328);
- --color-lime-200: oklch(0.938 0.127 124.321);
- --color-lime-300: oklch(0.897 0.196 126.665);
- --color-lime-400: oklch(0.841 0.238 128.85);
- --color-lime-500: oklch(0.768 0.233 130.85);
- --color-lime-600: oklch(0.648 0.2 131.684);
- --color-lime-700: oklch(0.532 0.157 131.589);
- --color-lime-800: oklch(0.453 0.124 130.933);
- --color-lime-900: oklch(0.405 0.101 131.063);
- --color-lime-950: oklch(0.274 0.072 132.109);
- --color-green-50: oklch(0.982 0.018 155.826);
- --color-green-100: oklch(0.962 0.044 156.743);
- --color-green-200: oklch(0.925 0.084 155.995);
- --color-green-300: oklch(0.871 0.15 154.449);
- --color-green-400: oklch(0.792 0.209 151.711);
- --color-green-500: oklch(0.723 0.219 149.579);
- --color-green-600: oklch(0.627 0.194 149.214);
- --color-green-700: oklch(0.527 0.154 150.069);
- --color-green-800: oklch(0.448 0.119 151.328);
- --color-green-900: oklch(0.393 0.095 152.535);
- --color-green-950: oklch(0.266 0.065 152.934);
- --color-emerald-50: oklch(0.979 0.021 166.113);
- --color-emerald-100: oklch(0.95 0.052 163.051);
- --color-emerald-200: oklch(0.905 0.093 164.15);
- --color-emerald-300: oklch(0.845 0.143 164.978);
- --color-emerald-400: oklch(0.765 0.177 163.223);
- --color-emerald-500: oklch(0.696 0.17 162.48);
- --color-emerald-600: oklch(0.596 0.145 163.225);
- --color-emerald-700: oklch(0.508 0.118 165.612);
- --color-emerald-800: oklch(0.432 0.095 166.913);
- --color-emerald-900: oklch(0.378 0.077 168.94);
- --color-emerald-950: oklch(0.262 0.051 172.552);
- --color-teal-50: oklch(0.984 0.014 180.72);
- --color-teal-100: oklch(0.953 0.051 180.801);
- --color-teal-200: oklch(0.91 0.096 180.426);
- --color-teal-300: oklch(0.855 0.138 181.071);
- --color-teal-400: oklch(0.777 0.152 181.912);
- --color-teal-500: oklch(0.704 0.14 182.503);
- --color-teal-600: oklch(0.6 0.118 184.704);
- --color-teal-700: oklch(0.511 0.096 186.391);
- --color-teal-800: oklch(0.437 0.078 188.216);
- --color-teal-900: oklch(0.386 0.063 188.416);
- --color-teal-950: oklch(0.277 0.046 192.524);
- --color-cyan-50: oklch(0.984 0.019 200.873);
- --color-cyan-100: oklch(0.956 0.045 203.388);
- --color-cyan-200: oklch(0.917 0.08 205.041);
- --color-cyan-300: oklch(0.865 0.127 207.078);
- --color-cyan-400: oklch(0.789 0.154 211.53);
- --color-cyan-500: oklch(0.715 0.143 215.221);
- --color-cyan-600: oklch(0.609 0.126 221.723);
- --color-cyan-700: oklch(0.52 0.105 223.128);
- --color-cyan-800: oklch(0.45 0.085 224.283);
- --color-cyan-900: oklch(0.398 0.07 227.392);
- --color-cyan-950: oklch(0.302 0.056 229.695);
- --color-sky-50: oklch(0.977 0.013 236.62);
- --color-sky-100: oklch(0.951 0.026 236.824);
- --color-sky-200: oklch(0.901 0.058 230.902);
- --color-sky-300: oklch(0.828 0.111 230.318);
- --color-sky-400: oklch(0.746 0.16 232.661);
- --color-sky-500: oklch(0.685 0.169 237.323);
- --color-sky-600: oklch(0.588 0.158 241.966);
- --color-sky-700: oklch(0.5 0.134 242.749);
- --color-sky-800: oklch(0.443 0.11 240.79);
- --color-sky-900: oklch(0.391 0.09 240.876);
- --color-sky-950: oklch(0.293 0.066 243.157);
- --color-blue-50: oklch(0.97 0.014 254.604);
- --color-blue-100: oklch(0.932 0.032 255.585);
- --color-blue-200: oklch(0.882 0.059 254.128);
- --color-blue-300: oklch(0.809 0.105 251.813);
- --color-blue-400: oklch(0.707 0.165 254.624);
- --color-blue-500: oklch(0.623 0.214 259.815);
- --color-blue-600: oklch(0.546 0.245 262.881);
- --color-blue-700: oklch(0.488 0.243 264.376);
- --color-blue-800: oklch(0.424 0.199 265.638);
- --color-blue-900: oklch(0.379 0.146 265.522);
- --color-blue-950: oklch(0.282 0.091 267.935);
- --color-indigo-50: oklch(0.962 0.018 272.314);
- --color-indigo-100: oklch(0.93 0.034 272.788);
- --color-indigo-200: oklch(0.87 0.065 274.039);
- --color-indigo-300: oklch(0.785 0.115 274.713);
- --color-indigo-400: oklch(0.673 0.182 276.935);
- --color-indigo-500: oklch(0.585 0.233 277.117);
- --color-indigo-600: oklch(0.511 0.262 276.966);
- --color-indigo-700: oklch(0.457 0.24 277.023);
- --color-indigo-800: oklch(0.398 0.195 277.366);
- --color-indigo-900: oklch(0.359 0.144 278.697);
- --color-indigo-950: oklch(0.257 0.09 281.288);
- --color-violet-50: oklch(0.969 0.016 293.756);
- --color-violet-100: oklch(0.943 0.029 294.588);
- --color-violet-200: oklch(0.894 0.057 293.283);
- --color-violet-300: oklch(0.811 0.111 293.571);
- --color-violet-400: oklch(0.702 0.183 293.541);
- --color-violet-500: oklch(0.606 0.25 292.717);
- --color-violet-600: oklch(0.541 0.281 293.009);
- --color-violet-700: oklch(0.491 0.27 292.581);
- --color-violet-800: oklch(0.432 0.232 292.759);
- --color-violet-900: oklch(0.38 0.189 293.745);
- --color-violet-950: oklch(0.283 0.141 291.089);
- --color-purple-50: oklch(0.977 0.014 308.299);
- --color-purple-100: oklch(0.946 0.033 307.174);
- --color-purple-200: oklch(0.902 0.063 306.703);
- --color-purple-300: oklch(0.827 0.119 306.383);
- --color-purple-400: oklch(0.714 0.203 305.504);
- --color-purple-500: oklch(0.627 0.265 303.9);
- --color-purple-600: oklch(0.558 0.288 302.321);
- --color-purple-700: oklch(0.496 0.265 301.924);
- --color-purple-800: oklch(0.438 0.218 303.724);
- --color-purple-900: oklch(0.381 0.176 304.987);
- --color-purple-950: oklch(0.291 0.149 302.717);
- --color-fuchsia-50: oklch(0.977 0.017 320.058);
- --color-fuchsia-100: oklch(0.952 0.037 318.852);
- --color-fuchsia-200: oklch(0.903 0.076 319.62);
- --color-fuchsia-300: oklch(0.833 0.145 321.434);
- --color-fuchsia-400: oklch(0.74 0.238 322.16);
- --color-fuchsia-500: oklch(0.667 0.295 322.15);
- --color-fuchsia-600: oklch(0.591 0.293 322.896);
- --color-fuchsia-700: oklch(0.518 0.253 323.949);
- --color-fuchsia-800: oklch(0.452 0.211 324.591);
- --color-fuchsia-900: oklch(0.401 0.17 325.612);
- --color-fuchsia-950: oklch(0.293 0.136 325.661);
- --color-pink-50: oklch(0.971 0.014 343.198);
- --color-pink-100: oklch(0.948 0.028 342.258);
- --color-pink-200: oklch(0.899 0.061 343.231);
- --color-pink-300: oklch(0.823 0.12 346.018);
- --color-pink-400: oklch(0.718 0.202 349.761);
- --color-pink-500: oklch(0.656 0.241 354.308);
- --color-pink-600: oklch(0.592 0.249 0.584);
- --color-pink-700: oklch(0.525 0.223 3.958);
- --color-pink-800: oklch(0.459 0.187 3.815);
- --color-pink-900: oklch(0.408 0.153 2.432);
- --color-pink-950: oklch(0.284 0.109 3.907);
- --color-rose-50: oklch(0.969 0.015 12.422);
- --color-rose-100: oklch(0.941 0.03 12.58);
- --color-rose-200: oklch(0.892 0.058 10.001);
- --color-rose-300: oklch(0.81 0.117 11.638);
- --color-rose-400: oklch(0.712 0.194 13.428);
- --color-rose-500: oklch(0.645 0.246 16.439);
- --color-rose-600: oklch(0.586 0.253 17.585);
- --color-rose-700: oklch(0.514 0.222 16.935);
- --color-rose-800: oklch(0.455 0.188 13.697);
- --color-rose-900: oklch(0.41 0.159 10.272);
- --color-rose-950: oklch(0.271 0.105 12.094);
- --color-slate-50: oklch(0.984 0.003 247.858);
- --color-slate-100: oklch(0.968 0.007 247.896);
- --color-slate-200: oklch(0.929 0.013 255.508);
- --color-slate-300: oklch(0.869 0.022 252.894);
- --color-slate-400: oklch(0.704 0.04 256.788);
- --color-slate-500: oklch(0.554 0.046 257.417);
- --color-slate-600: oklch(0.446 0.043 257.281);
- --color-slate-700: oklch(0.372 0.044 257.287);
- --color-slate-800: oklch(0.279 0.041 260.031);
- --color-slate-900: oklch(0.208 0.042 265.755);
- --color-slate-950: oklch(0.129 0.042 264.695);
- --color-gray-50: oklch(0.985 0.002 247.839);
- --color-gray-100: oklch(0.967 0.003 264.542);
- --color-gray-200: oklch(0.928 0.006 264.531);
- --color-gray-300: oklch(0.872 0.01 258.338);
- --color-gray-400: oklch(0.707 0.022 261.325);
- --color-gray-500: oklch(0.551 0.027 264.364);
- --color-gray-600: oklch(0.446 0.03 256.802);
- --color-gray-700: oklch(0.373 0.034 259.733);
- --color-gray-800: oklch(0.278 0.033 256.848);
- --color-gray-900: oklch(0.21 0.034 264.665);
- --color-gray-950: oklch(0.13 0.028 261.692);
- --color-zinc-50: oklch(0.985 0 0);
- --color-zinc-100: oklch(0.967 0.001 286.375);
- --color-zinc-200: oklch(0.92 0.004 286.32);
- --color-zinc-300: oklch(0.871 0.006 286.286);
- --color-zinc-400: oklch(0.705 0.015 286.067);
- --color-zinc-500: oklch(0.552 0.016 285.938);
- --color-zinc-600: oklch(0.442 0.017 285.786);
- --color-zinc-700: oklch(0.37 0.013 285.805);
- --color-zinc-800: oklch(0.274 0.006 286.033);
- --color-zinc-900: oklch(0.21 0.006 285.885);
- --color-zinc-950: oklch(0.141 0.005 285.823);
- --color-neutral-50: oklch(0.985 0 0);
- --color-neutral-100: oklch(0.97 0 0);
- --color-neutral-200: oklch(0.922 0 0);
- --color-neutral-300: oklch(0.87 0 0);
- --color-neutral-400: oklch(0.708 0 0);
- --color-neutral-500: oklch(0.556 0 0);
- --color-neutral-600: oklch(0.439 0 0);
- --color-neutral-700: oklch(0.371 0 0);
- --color-neutral-800: oklch(0.269 0 0);
- --color-neutral-900: oklch(0.205 0 0);
- --color-neutral-950: oklch(0.145 0 0);
- --color-stone-50: oklch(0.985 0.001 106.423);
- --color-stone-100: oklch(0.97 0.001 106.424);
- --color-stone-200: oklch(0.923 0.003 48.717);
- --color-stone-300: oklch(0.869 0.005 56.366);
- --color-stone-400: oklch(0.709 0.01 56.259);
- --color-stone-500: oklch(0.553 0.013 58.071);
- --color-stone-600: oklch(0.444 0.011 73.639);
- --color-stone-700: oklch(0.374 0.01 67.558);
- --color-stone-800: oklch(0.268 0.007 34.298);
- --color-stone-900: oklch(0.216 0.006 56.043);
- --color-stone-950: oklch(0.147 0.004 49.25);
+ --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
+ 'Noto Color Emoji';
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
+ monospace;
+ --color-blue-500: oklch(62.3% 0.214 259.815);
+ --color-gray-200: oklch(92.8% 0.006 264.531);
--color-black: #000;
- --color-white: #fff;
--spacing: 0.25rem;
- --breakpoint-sm: 40rem;
- --breakpoint-md: 48rem;
- --breakpoint-lg: 64rem;
--breakpoint-xl: 80rem;
- --breakpoint-2xl: 96rem;
- --container-3xs: 16rem;
- --container-2xs: 18rem;
- --container-xs: 20rem;
- --container-sm: 24rem;
--container-md: 28rem;
- --container-lg: 32rem;
- --container-xl: 36rem;
- --container-2xl: 42rem;
- --container-3xl: 48rem;
--container-4xl: 56rem;
- --container-5xl: 64rem;
- --container-6xl: 72rem;
- --container-7xl: 80rem;
--text-xs: 0.75rem;
--text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875);
- --text-base: 1rem;
- --text-base--line-height: calc(1.5 / 1);
--text-lg: 1.125rem;
--text-lg--line-height: calc(1.75 / 1.125);
--text-xl: 1.25rem;
@@ -287,115 +29,24 @@
--text-3xl--line-height: calc(2.25 / 1.875);
--text-4xl: 2.25rem;
--text-4xl--line-height: calc(2.5 / 2.25);
- --text-5xl: 3rem;
- --text-5xl--line-height: 1;
--text-6xl: 3.75rem;
--text-6xl--line-height: 1;
- --text-7xl: 4.5rem;
- --text-7xl--line-height: 1;
- --text-8xl: 6rem;
- --text-8xl--line-height: 1;
--text-9xl: 8rem;
--text-9xl--line-height: 1;
- --font-weight-thin: 100;
- --font-weight-extralight: 200;
- --font-weight-light: 300;
- --font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
- --font-weight-extrabold: 800;
- --font-weight-black: 900;
- --tracking-tighter: -0.05em;
--tracking-tight: -0.025em;
- --tracking-normal: 0em;
- --tracking-wide: 0.025em;
- --tracking-wider: 0.05em;
- --tracking-widest: 0.1em;
- --leading-tight: 1.25;
- --leading-snug: 1.375;
- --leading-normal: 1.5;
--leading-relaxed: 1.625;
- --leading-loose: 2;
- --radius-xs: 0.125rem;
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
- --radius-2xl: 1rem;
- --radius-3xl: 1.5rem;
- --radius-4xl: 2rem;
- --shadow-2xs: 0 1px rgb(0 0 0 / 0.05);
- --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
- --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
- --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
- --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
- --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
- --inset-shadow-2xs: inset 0 1px rgb(0 0 0 / 0.05);
- --inset-shadow-xs: inset 0 1px 1px rgb(0 0 0 / 0.05);
- --inset-shadow-sm: inset 0 2px 4px rgb(0 0 0 / 0.05);
- --drop-shadow-xs: 0 1px 1px rgb(0 0 0 / 0.05);
- --drop-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.15);
- --drop-shadow-md: 0 3px 3px rgb(0 0 0 / 0.12);
- --drop-shadow-lg: 0 4px 4px rgb(0 0 0 / 0.15);
- --drop-shadow-xl: 0 9px 7px rgb(0 0 0 / 0.1);
- --drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / 0.15);
- --ease-in: cubic-bezier(0.4, 0, 1, 1);
- --ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
- --animate-spin: spin 1s linear infinite;
- --animate-ping: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
- --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
- --animate-bounce: bounce 1s infinite;
- --blur-xs: 4px;
- --blur-sm: 8px;
- --blur-md: 12px;
- --blur-lg: 16px;
- --blur-xl: 24px;
- --blur-2xl: 40px;
- --blur-3xl: 64px;
- --perspective-dramatic: 100px;
- --perspective-near: 300px;
- --perspective-normal: 500px;
- --perspective-midrange: 800px;
- --perspective-distant: 1200px;
- --aspect-video: 16 / 9;
--default-transition-duration: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
- --default-font-feature-settings: var(--font-sans--font-feature-settings);
- --default-font-variation-settings: var(--font-sans--font-variation-settings);
--default-mono-font-family: var(--font-mono);
- --default-mono-font-feature-settings: var(--font-mono--font-feature-settings);
- --default-mono-font-variation-settings: var(--font-mono--font-variation-settings);
- --color-rosewater: var(--rosewater);
- --color-flamingo: var(--flamingo);
- --color-pink: var(--pink);
- --color-mauve: var(--mauve);
- --color-red: var(--red);
- --color-dark-red: var(--dark-red);
- --color-maroon: var(--maroon);
- --color-peach: var(--peach);
- --color-yellow: var(--yellow);
- --color-green: var(--green);
- --color-teal: var(--teal);
- --color-sky: var(--sky);
- --color-sapphire: var(--sapphire);
- --color-blue: var(--blue);
- --color-lavender: var(--lavender);
- --color-text: var(--text);
- --color-subtext1: var(--subtext1);
- --color-subtext0: var(--subtext0);
- --color-overlay2: var(--overlay2);
- --color-overlay1: var(--overlay1);
- --color-overlay0: var(--overlay0);
- --color-surface2: var(--surface2);
- --color-surface1: var(--surface1);
- --color-surface0: var(--surface0);
- --color-base: var(--base);
- --color-mantle: var(--mantle);
- --color-crust: var(--crust);
}
}
@layer base {
@@ -409,14 +60,11 @@
line-height: 1.5;
-webkit-text-size-adjust: 100%;
tab-size: 4;
- font-family: var( --default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' );
+ font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji');
font-feature-settings: var(--default-font-feature-settings, normal);
font-variation-settings: var(--default-font-variation-settings, normal);
-webkit-tap-highlight-color: transparent;
}
- body {
- line-height: inherit;
- }
hr {
height: 0;
color: inherit;
@@ -439,7 +87,7 @@
font-weight: bolder;
}
code, kbd, samp, pre {
- font-family: var( --default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace );
+ font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace);
font-feature-settings: var(--default-mono-font-feature-settings, normal);
font-variation-settings: var(--default-mono-font-variation-settings, normal);
font-size: 1em;
@@ -505,7 +153,14 @@
}
::placeholder {
opacity: 1;
- color: color-mix(in oklab, currentColor 50%, transparent);
+ }
+ @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
+ ::placeholder {
+ color: currentcolor;
+ @supports (color: color-mix(in lab, red, red)) {
+ color: color-mix(in oklab, currentcolor 50%, transparent);
+ }
+ }
}
textarea {
resize: vertical;
@@ -526,6 +181,9 @@
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
padding-block: 0;
}
+ ::-webkit-calendar-picker-indicator {
+ line-height: 1;
+ }
:-moz-ui-invalid {
box-shadow: none;
}
@@ -553,7 +211,7 @@
padding: 0;
margin: -1px;
overflow: hidden;
- clip: rect(0, 0, 0, 0);
+ clip-path: inset(50%);
white-space: nowrap;
border-width: 0;
}
@@ -701,7 +359,7 @@
.table {
display: table;
}
- .aspect-\[2\/3\] {
+ .aspect-2\/3 {
aspect-ratio: 2/3;
}
.size-5 {
@@ -799,7 +457,7 @@
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.transform {
- transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y);
+ transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
}
.flex-col {
flex-direction: column;
@@ -837,12 +495,6 @@
.gap-8 {
gap: calc(var(--spacing) * 8);
}
- .gap-x-1 {
- column-gap: calc(var(--spacing) * 1);
- }
- .gap-x-2 {
- column-gap: calc(var(--spacing) * 2);
- }
.space-y-1 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
@@ -857,8 +509,11 @@
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
}
}
- .gap-y-4 {
- row-gap: calc(var(--spacing) * 4);
+ .gap-x-1 {
+ column-gap: calc(var(--spacing) * 1);
+ }
+ .gap-x-2 {
+ column-gap: calc(var(--spacing) * 2);
}
.space-x-2 {
:where(& > :not(:last-child)) {
@@ -874,6 +529,9 @@
margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
}
}
+ .gap-y-4 {
+ row-gap: calc(var(--spacing) * 4);
+ }
.divide-y {
:where(& > :not(:last-child)) {
--tw-divide-y-reverse: 0;
@@ -965,7 +623,10 @@
background-color: var(--overlay0);
}
.bg-overlay0\/55 {
- background-color: color-mix(in oklab, var(--overlay0) 55%, transparent);
+ background-color: var(--overlay0);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--overlay0) 55%, transparent);
+ }
}
.bg-overlay2 {
background-color: var(--overlay2);
@@ -1161,10 +822,13 @@
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-black {
- --tw-shadow-color: var(--color-black);
+ --tw-shadow-color: #000;
+ @supports (color: color-mix(in lab, red, red)) {
+ --tw-shadow-color: color-mix(in oklab, var(--color-black) var(--tw-shadow-alpha), transparent);
+ }
}
.transition {
- transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
+ transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
@@ -1245,7 +909,10 @@
.hover\:bg-blue\/75 {
&:hover {
@media (hover: hover) {
- background-color: color-mix(in oklab, var(--blue) 75%, transparent);
+ background-color: var(--blue);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--blue) 75%, transparent);
+ }
}
}
}
@@ -1259,7 +926,10 @@
.hover\:bg-green\/75 {
&:hover {
@media (hover: hover) {
- background-color: color-mix(in oklab, var(--green) 75%, transparent);
+ background-color: var(--green);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--green) 75%, transparent);
+ }
}
}
}
@@ -1273,21 +943,30 @@
.hover\:bg-mauve\/75 {
&:hover {
@media (hover: hover) {
- background-color: color-mix(in oklab, var(--mauve) 75%, transparent);
+ background-color: var(--mauve);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--mauve) 75%, transparent);
+ }
}
}
}
.hover\:bg-red\/25 {
&:hover {
@media (hover: hover) {
- background-color: color-mix(in oklab, var(--red) 25%, transparent);
+ background-color: var(--red);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--red) 25%, transparent);
+ }
}
}
}
.hover\:bg-sapphire\/75 {
&:hover {
@media (hover: hover) {
- background-color: color-mix(in oklab, var(--sapphire) 75%, transparent);
+ background-color: var(--sapphire);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--sapphire) 75%, transparent);
+ }
}
}
}
@@ -1308,7 +987,10 @@
.hover\:bg-teal\/75 {
&:hover {
@media (hover: hover) {
- background-color: color-mix(in oklab, var(--teal) 75%, transparent);
+ background-color: var(--teal);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--teal) 75%, transparent);
+ }
}
}
}
@@ -1322,7 +1004,10 @@
.hover\:text-overlay2\/75 {
&:hover {
@media (hover: hover) {
- color: color-mix(in oklab, var(--overlay2) 75%, transparent);
+ color: var(--overlay2);
+ @supports (color: color-mix(in lab, red, red)) {
+ color: color-mix(in oklab, var(--overlay2) 75%, transparent);
+ }
}
}
}
@@ -1359,7 +1044,7 @@
}
.focus\:ring-2 {
&:focus {
- --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor);
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
}
@@ -1391,12 +1076,18 @@
}
.disabled\:bg-blue\/60 {
&:disabled {
- background-color: color-mix(in oklab, var(--blue) 60%, transparent);
+ background-color: var(--blue);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--blue) 60%, transparent);
+ }
}
}
.disabled\:bg-green\/60 {
&:disabled {
- background-color: color-mix(in oklab, var(--green) 60%, transparent);
+ background-color: var(--green);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--green) 60%, transparent);
+ }
}
}
.disabled\:opacity-50 {
@@ -1745,32 +1436,6 @@
font-weight: 700;
font-style: italic;
}
-@keyframes spin {
- to {
- transform: rotate(360deg);
- }
-}
-@keyframes ping {
- 75%, 100% {
- transform: scale(2);
- opacity: 0;
- }
-}
-@keyframes pulse {
- 50% {
- opacity: 0.5;
- }
-}
-@keyframes bounce {
- 0%, 100% {
- transform: translateY(-25%);
- animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
- }
- 50% {
- transform: none;
- animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
- }
-}
@property --tw-translate-x {
syntax: "*";
inherits: false;
@@ -1789,27 +1454,22 @@
@property --tw-rotate-x {
syntax: "*";
inherits: false;
- initial-value: rotateX(0);
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
- initial-value: rotateY(0);
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
- initial-value: rotateZ(0);
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
- initial-value: skewX(0);
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
- initial-value: skewY(0);
}
@property --tw-space-y-reverse {
syntax: "*";
@@ -1852,6 +1512,11 @@
syntax: "*";
inherits: false;
}
+@property --tw-shadow-alpha {
+ syntax: "";
+ inherits: false;
+ initial-value: 100%;
+}
@property --tw-inset-shadow {
syntax: "*";
inherits: false;
@@ -1861,6 +1526,11 @@
syntax: "*";
inherits: false;
}
+@property --tw-inset-shadow-alpha {
+ syntax: "";
+ inherits: false;
+ initial-value: 100%;
+}
@property --tw-ring-color {
syntax: "*";
inherits: false;
@@ -1911,3 +1581,41 @@
initial-value: "";
inherits: false;
}
+@layer properties {
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
+ *, ::before, ::after, ::backdrop {
+ --tw-translate-x: 0;
+ --tw-translate-y: 0;
+ --tw-translate-z: 0;
+ --tw-rotate-x: initial;
+ --tw-rotate-y: initial;
+ --tw-rotate-z: initial;
+ --tw-skew-x: initial;
+ --tw-skew-y: initial;
+ --tw-space-y-reverse: 0;
+ --tw-space-x-reverse: 0;
+ --tw-divide-y-reverse: 0;
+ --tw-border-style: solid;
+ --tw-leading: initial;
+ --tw-font-weight: initial;
+ --tw-tracking: initial;
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-color: initial;
+ --tw-shadow-alpha: 100%;
+ --tw-inset-shadow: 0 0 #0000;
+ --tw-inset-shadow-color: initial;
+ --tw-inset-shadow-alpha: 100%;
+ --tw-ring-color: initial;
+ --tw-ring-shadow: 0 0 #0000;
+ --tw-inset-ring-color: initial;
+ --tw-inset-ring-shadow: 0 0 #0000;
+ --tw-ring-inset: initial;
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-duration: initial;
+ --tw-ease: initial;
+ --tw-content: "";
+ }
+ }
+}
diff --git a/pkg/jwt/create.go b/pkg/jwt/create.go
deleted file mode 100644
index 1ba66a4..0000000
--- a/pkg/jwt/create.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package jwt
-
-import (
- "time"
-
- "projectreshoot/internal/models"
- "projectreshoot/pkg/config"
-
- "github.com/golang-jwt/jwt"
- "github.com/google/uuid"
- "github.com/pkg/errors"
-)
-
-// Generates an access token for the provided user
-func GenerateAccessToken(
- config *config.Config,
- user *models.User,
- fresh bool,
- rememberMe bool,
-) (tokenStr string, exp int64, err error) {
- issuedAt := time.Now().Unix()
- expiresAt := issuedAt + (config.AccessTokenExpiry * 60)
- var freshExpiresAt int64
- if fresh {
- freshExpiresAt = issuedAt + (config.TokenFreshTime * 60)
- } else {
- freshExpiresAt = issuedAt
- }
- var ttl string
- if rememberMe {
- ttl = "exp"
- } else {
- ttl = "session"
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256,
- jwt.MapClaims{
- "iss": config.TrustedHost,
- "scope": "access",
- "ttl": ttl,
- "jti": uuid.New(),
- "iat": issuedAt,
- "exp": expiresAt,
- "fresh": freshExpiresAt,
- "sub": user.ID,
- })
-
- signedToken, err := token.SignedString([]byte(config.SecretKey))
- if err != nil {
- return "", 0, errors.Wrap(err, "token.SignedString")
- }
- return signedToken, expiresAt, nil
-}
-
-// Generates a refresh token for the provided user
-func GenerateRefreshToken(
- config *config.Config,
- user *models.User,
- rememberMe bool,
-) (tokenStr string, exp int64, err error) {
- issuedAt := time.Now().Unix()
- expiresAt := issuedAt + (config.RefreshTokenExpiry * 60)
- var ttl string
- if rememberMe {
- ttl = "exp"
- } else {
- ttl = "session"
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256,
- jwt.MapClaims{
- "iss": config.TrustedHost,
- "scope": "refresh",
- "ttl": ttl,
- "jti": uuid.New(),
- "iat": issuedAt,
- "exp": expiresAt,
- "sub": user.ID,
- })
-
- signedToken, err := token.SignedString([]byte(config.SecretKey))
- if err != nil {
- return "", 0, errors.Wrap(err, "token.SignedString")
- }
- return signedToken, expiresAt, nil
-}
diff --git a/pkg/jwt/parse.go b/pkg/jwt/parse.go
deleted file mode 100644
index fb56778..0000000
--- a/pkg/jwt/parse.go
+++ /dev/null
@@ -1,268 +0,0 @@
-package jwt
-
-import (
- "context"
- "fmt"
- "time"
-
- "projectreshoot/pkg/config"
- "projectreshoot/pkg/db"
-
- "github.com/golang-jwt/jwt"
- "github.com/google/uuid"
- "github.com/pkg/errors"
-)
-
-// Parse an access token and return a struct with all the claims. Does validation on
-// all the claims, including checking if it is expired, has a valid issuer, and
-// has the correct scope.
-func ParseAccessToken(
- config *config.Config,
- ctx context.Context,
- tx db.SafeTX,
- tokenString string,
-) (*AccessToken, error) {
- if tokenString == "" {
- return nil, errors.New("Access token string not provided")
- }
- claims, err := parseToken(config.SecretKey, tokenString)
- if err != nil {
- return nil, errors.Wrap(err, "parseToken")
- }
- expiry, err := checkTokenExpired(claims["exp"])
- if err != nil {
- return nil, errors.Wrap(err, "checkTokenExpired")
- }
- issuer, err := checkTokenIssuer(config.TrustedHost, claims["iss"])
- if err != nil {
- return nil, errors.Wrap(err, "checkTokenIssuer")
- }
- ttl, err := getTokenTTL(claims["ttl"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenTTL")
- }
- scope, err := getTokenScope(claims["scope"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenScope")
- }
- if scope != "access" {
- return nil, errors.New("Token is not an Access token")
- }
- issuedAt, err := getIssuedTime(claims["iat"])
- if err != nil {
- return nil, errors.Wrap(err, "getIssuedTime")
- }
- subject, err := getTokenSubject(claims["sub"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenSubject")
- }
- fresh, err := getFreshTime(claims["fresh"])
- if err != nil {
- return nil, errors.Wrap(err, "getFreshTime")
- }
- jti, err := getTokenJTI(claims["jti"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenJTI")
- }
-
- token := &AccessToken{
- ISS: issuer,
- TTL: ttl,
- EXP: expiry,
- IAT: issuedAt,
- SUB: subject,
- Fresh: fresh,
- JTI: jti,
- Scope: scope,
- }
-
- valid, err := CheckTokenNotRevoked(ctx, tx, token)
- if err != nil {
- return nil, errors.Wrap(err, "CheckTokenNotRevoked")
- }
- if !valid {
- return nil, errors.New("Token has been revoked")
- }
- return token, nil
-}
-
-// Parse a refresh token and return a struct with all the claims. Does validation on
-// all the claims, including checking if it is expired, has a valid issuer, and
-// has the correct scope.
-func ParseRefreshToken(
- config *config.Config,
- ctx context.Context,
- tx db.SafeTX,
- tokenString string,
-) (*RefreshToken, error) {
- if tokenString == "" {
- return nil, errors.New("Refresh token string not provided")
- }
- claims, err := parseToken(config.SecretKey, tokenString)
- if err != nil {
- return nil, errors.Wrap(err, "parseToken")
- }
- expiry, err := checkTokenExpired(claims["exp"])
- if err != nil {
- return nil, errors.Wrap(err, "checkTokenExpired")
- }
- issuer, err := checkTokenIssuer(config.TrustedHost, claims["iss"])
- if err != nil {
- return nil, errors.Wrap(err, "checkTokenIssuer")
- }
- ttl, err := getTokenTTL(claims["ttl"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenTTL")
- }
- scope, err := getTokenScope(claims["scope"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenScope")
- }
- if scope != "refresh" {
- return nil, errors.New("Token is not an Refresh token")
- }
- issuedAt, err := getIssuedTime(claims["iat"])
- if err != nil {
- return nil, errors.Wrap(err, "getIssuedTime")
- }
- subject, err := getTokenSubject(claims["sub"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenSubject")
- }
- jti, err := getTokenJTI(claims["jti"])
- if err != nil {
- return nil, errors.Wrap(err, "getTokenJTI")
- }
-
- token := &RefreshToken{
- ISS: issuer,
- TTL: ttl,
- EXP: expiry,
- IAT: issuedAt,
- SUB: subject,
- JTI: jti,
- Scope: scope,
- }
-
- valid, err := CheckTokenNotRevoked(ctx, tx, token)
- if err != nil {
- return nil, errors.Wrap(err, "CheckTokenNotRevoked")
- }
- if !valid {
- return nil, errors.New("Token has been revoked")
- }
- return token, nil
-}
-
-// Parse a token, validating its signing sigature and returning the claims
-func parseToken(secretKey string, tokenString string) (jwt.MapClaims, error) {
- token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
- if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
- return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
- }
-
- return []byte(secretKey), nil
- })
- if err != nil {
- return nil, errors.Wrap(err, "jwt.Parse")
- }
- // Token decoded, parse the claims
- claims, ok := token.Claims.(jwt.MapClaims)
- if !ok {
- return nil, errors.New("Failed to parse claims")
- }
- return claims, nil
-}
-
-// Check if a token is expired. Returns the expiry if not expired
-func checkTokenExpired(expiry interface{}) (int64, error) {
- // Coerce the expiry to a float64 to avoid scientific notation
- expFloat, ok := expiry.(float64)
- if !ok {
- return 0, errors.New("Missing or invalid 'exp' claim")
- }
- // Convert to the int64 time we expect :)
- expiryTime := int64(expFloat)
-
- // Check if its expired
- isExpired := time.Now().After(time.Unix(expiryTime, 0))
- if isExpired {
- return 0, errors.New("Token has expired")
- }
- return expiryTime, nil
-}
-
-// Check if a token has a valid issuer. Returns the issuer if valid
-func checkTokenIssuer(trustedHost string, issuer interface{}) (string, error) {
- issuerVal, ok := issuer.(string)
- if !ok {
- return "", errors.New("Missing or invalid 'iss' claim")
- }
- if issuer != trustedHost {
- return "", errors.New("Issuer does not matched trusted host")
- }
- return issuerVal, nil
-}
-
-// Check the scope matches the expected scope. Returns scope if true
-func getTokenScope(scope interface{}) (string, error) {
- scopeStr, ok := scope.(string)
- if !ok {
- return "", errors.New("Missing or invalid 'scope' claim")
- }
- return scopeStr, nil
-}
-
-// Get the TTL of the token, either "session" or "exp"
-func getTokenTTL(ttl interface{}) (string, error) {
- ttlStr, ok := ttl.(string)
- if !ok {
- return "", errors.New("Missing or invalid 'ttl' claim")
- }
- if ttlStr != "exp" && ttlStr != "session" {
- return "", errors.New("TTL value is not recognised")
- }
- return ttlStr, nil
-}
-
-// Get the time the token was issued at
-func getIssuedTime(issued interface{}) (int64, error) {
- // Same float64 -> int64 trick as expiry
- issuedFloat, ok := issued.(float64)
- if !ok {
- return 0, errors.New("Missing or invalid 'iat' claim")
- }
- issuedAt := int64(issuedFloat)
- return issuedAt, nil
-}
-
-// Get the freshness expiry timestamp
-func getFreshTime(fresh interface{}) (int64, error) {
- freshUntil, ok := fresh.(float64)
- if !ok {
- return 0, errors.New("Missing or invalid 'fresh' claim")
- }
- return int64(freshUntil), nil
-}
-
-// Get the subject of the token
-func getTokenSubject(sub interface{}) (int, error) {
- subject, ok := sub.(float64)
- if !ok {
- return 0, errors.New("Missing or invalid 'sub' claim")
- }
- return int(subject), nil
-}
-
-// Get the JTI of the token
-func getTokenJTI(jti interface{}) (uuid.UUID, error) {
- jtiStr, ok := jti.(string)
- if !ok {
- return uuid.UUID{}, errors.New("Missing or invalid 'jti' claim")
- }
- jtiUUID, err := uuid.Parse(jtiStr)
- if err != nil {
- return uuid.UUID{}, errors.New("JTI is not a valid UUID")
- }
- return jtiUUID, nil
-}
diff --git a/pkg/jwt/revoke.go b/pkg/jwt/revoke.go
deleted file mode 100644
index 64f4020..0000000
--- a/pkg/jwt/revoke.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package jwt
-
-import (
- "context"
- "projectreshoot/pkg/db"
-
- "github.com/pkg/errors"
-)
-
-// Revoke a token by adding it to the database
-func RevokeToken(ctx context.Context, tx *db.SafeWTX, t Token) error {
- jti := t.GetJTI()
- exp := t.GetEXP()
- query := `INSERT INTO jwtblacklist (jti, exp) VALUES (?, ?)`
- _, err := tx.Exec(ctx, query, jti, exp)
- if err != nil {
- return errors.Wrap(err, "tx.Exec")
- }
- return nil
-}
-
-// Check if a token has been revoked. Returns true if not revoked.
-func CheckTokenNotRevoked(ctx context.Context, tx db.SafeTX, t Token) (bool, error) {
- jti := t.GetJTI()
- query := `SELECT 1 FROM jwtblacklist WHERE jti = ? LIMIT 1`
- rows, err := tx.Query(ctx, query, jti)
- if err != nil {
- return false, errors.Wrap(err, "tx.Query")
- }
- defer rows.Close()
- revoked := rows.Next()
- return !revoked, nil
-}
diff --git a/pkg/jwt/tokens.go b/pkg/jwt/tokens.go
deleted file mode 100644
index 26bdcc8..0000000
--- a/pkg/jwt/tokens.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package jwt
-
-import (
- "context"
- "projectreshoot/internal/models"
- "projectreshoot/pkg/db"
-
- "github.com/google/uuid"
- "github.com/pkg/errors"
-)
-
-type Token interface {
- GetJTI() uuid.UUID
- GetEXP() int64
- GetScope() string
- GetUser(ctx context.Context, tx db.SafeTX) (*models.User, error)
-}
-
-// Access token
-type AccessToken struct {
- ISS string // Issuer, generally TrustedHost
- IAT int64 // Time issued at
- EXP int64 // Time expiring at
- TTL string // Time-to-live: "session" or "exp". Used with 'remember me'
- SUB int // Subject (user) ID
- JTI uuid.UUID // UUID-4 used for identifying blacklisted tokens
- Fresh int64 // Time freshness expiring at
- Scope string // Should be "access"
-}
-
-// Refresh token
-type RefreshToken struct {
- ISS string // Issuer, generally TrustedHost
- IAT int64 // Time issued at
- EXP int64 // Time expiring at
- TTL string // Time-to-live: "session" or "exp". Used with 'remember me'
- SUB int // Subject (user) ID
- JTI uuid.UUID // UUID-4 used for identifying blacklisted tokens
- Scope string // Should be "refresh"
-}
-
-func (a AccessToken) GetUser(ctx context.Context, tx db.SafeTX) (*models.User, error) {
- user, err := models.GetUserFromID(ctx, tx, a.SUB)
- if err != nil {
- return nil, errors.Wrap(err, "db.GetUserFromID")
- }
- return user, nil
-}
-func (r RefreshToken) GetUser(ctx context.Context, tx db.SafeTX) (*models.User, error) {
- user, err := models.GetUserFromID(ctx, tx, r.SUB)
- if err != nil {
- return nil, errors.Wrap(err, "db.GetUserFromID")
- }
- return user, nil
-}
-
-func (a AccessToken) GetJTI() uuid.UUID {
- return a.JTI
-}
-func (r RefreshToken) GetJTI() uuid.UUID {
- return r.JTI
-}
-func (a AccessToken) GetEXP() int64 {
- return a.EXP
-}
-func (r RefreshToken) GetEXP() int64 {
- return r.EXP
-}
-func (a AccessToken) GetScope() string {
- return a.Scope
-}
-func (r RefreshToken) GetScope() string {
- return r.Scope
-}
diff --git a/pkg/tests/config.go b/pkg/tests/config.go
deleted file mode 100644
index 757d3b0..0000000
--- a/pkg/tests/config.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package tests
-
-import (
- "os"
- "projectreshoot/pkg/config"
-
- "github.com/pkg/errors"
-)
-
-func TestConfig() (*config.Config, error) {
- os.Setenv("SECRET_KEY", ".")
- os.Setenv("TMDB_API_TOKEN", ".")
- cfg, err := config.GetConfig(map[string]string{})
- if err != nil {
- return nil, errors.Wrap(err, "config.GetConfig")
- }
- return cfg, nil
-}
diff --git a/pkg/tests/database.go b/pkg/tests/database.go
deleted file mode 100644
index d5856a6..0000000
--- a/pkg/tests/database.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package tests
-
-import (
- "context"
- "database/sql"
- "fmt"
- "io/fs"
- "os"
- "path/filepath"
-
- "github.com/pkg/errors"
- "github.com/pressly/goose/v3"
-
- _ "modernc.org/sqlite"
-)
-
-func findMigrations() (*fs.FS, error) {
- dir, err := os.Getwd()
- if err != nil {
- return nil, err
- }
-
- for {
- if _, err := os.Stat(filepath.Join(dir, "Makefile")); err == nil {
- migrationsdir := os.DirFS(filepath.Join(dir, "cmd", "migrate", "migrations"))
- return &migrationsdir, nil
- }
-
- parent := filepath.Dir(dir)
- if parent == dir { // Reached root
- return nil, errors.New("Unable to locate migrations directory")
- }
- dir = parent
- }
-}
-
-func findTestData() (string, error) {
- dir, err := os.Getwd()
- if err != nil {
- return "", err
- }
-
- for {
- if _, err := os.Stat(filepath.Join(dir, "Makefile")); err == nil {
- return filepath.Join(dir, "pkg", "tests", "testdata.sql"), nil
- }
-
- parent := filepath.Dir(dir)
- if parent == dir { // Reached root
- return "", errors.New("Unable to locate test data")
- }
- dir = parent
- }
-}
-
-func migrateTestDB(wconn *sql.DB, version int64) error {
- migrations, err := findMigrations()
- if err != nil {
- return errors.Wrap(err, "findMigrations")
- }
- provider, err := goose.NewProvider(goose.DialectSQLite3, wconn, *migrations)
- if err != nil {
- return errors.Wrap(err, "goose.NewProvider")
- }
- ctx := context.Background()
- if _, err := provider.UpTo(ctx, version); err != nil {
- return errors.Wrap(err, "provider.UpTo")
- }
- return nil
-}
-
-func loadTestData(wconn *sql.DB) error {
- dataPath, err := findTestData()
- if err != nil {
- return errors.Wrap(err, "findSchema")
- }
- sqlBytes, err := os.ReadFile(dataPath)
- if err != nil {
- return errors.Wrap(err, "os.ReadFile")
- }
- dataSQL := string(sqlBytes)
-
- _, err = wconn.Exec(dataSQL)
- if err != nil {
- return errors.Wrap(err, "tx.Exec")
- }
- return nil
-}
-
-// Returns two db connection handles. First is a readwrite connection, second
-// is a read only connection
-func SetupTestDB(version int64) (*sql.DB, *sql.DB, error) {
- opts := "_journal_mode=WAL&_synchronous=NORMAL&_txlock=IMMEDIATE"
- file := fmt.Sprintf("file::memory:?cache=shared&%s", opts)
- wconn, err := sql.Open("sqlite", file)
- if err != nil {
- return nil, nil, errors.Wrap(err, "sql.Open")
- }
-
- err = migrateTestDB(wconn, version)
- if err != nil {
- return nil, nil, errors.Wrap(err, "migrateTestDB")
- }
- err = loadTestData(wconn)
- if err != nil {
- return nil, nil, errors.Wrap(err, "loadTestData")
- }
-
- opts = "_synchronous=NORMAL&mode=ro"
- file = fmt.Sprintf("file::memory:?cache=shared&%s", opts)
- rconn, err := sql.Open("sqlite", file)
- if err != nil {
- return nil, nil, errors.Wrap(err, "sql.Open")
- }
- return wconn, rconn, nil
-}
diff --git a/pkg/tests/logger.go b/pkg/tests/logger.go
deleted file mode 100644
index c3a9118..0000000
--- a/pkg/tests/logger.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package tests
-
-import (
- "testing"
-
- "github.com/rs/zerolog"
-)
-
-type TLogWriter struct {
- t *testing.T
-}
-
-// Write implements the io.Writer interface for TLogWriter.
-func (w *TLogWriter) Write(p []byte) (n int, err error) {
- w.t.Logf("%s", p)
- return len(p), nil
-}
-
-// Return a fake logger to satisfy functions that expect one
-func NilLogger() *zerolog.Logger {
- logger := zerolog.New(nil)
- return &logger
-}
-
-// Return a logger that makes use of the T.Log method to enable debugging tests
-func DebugLogger(t *testing.T) *zerolog.Logger {
- logger := zerolog.New(GetTLogWriter(t))
- return &logger
-}
-
-func GetTLogWriter(t *testing.T) *TLogWriter {
- return &TLogWriter{t: t}
-}
diff --git a/pkg/tests/testdata.sql b/pkg/tests/testdata.sql
deleted file mode 100644
index be1ee04..0000000
--- a/pkg/tests/testdata.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-INSERT INTO users VALUES(1,'testuser','hashedpassword',1738995274, 'bio');
-INSERT INTO jwtblacklist VALUES('0a6b338e-930a-43fe-8f70-1a6daed256fa', 33299675344);
-INSERT INTO jwtblacklist VALUES('b7fa51dc-8532-42e1-8756-5d25bfb2003a', 33299675344);
diff --git a/pkg/tmdb/config.go b/pkg/tmdb/config.go
deleted file mode 100644
index b58df22..0000000
--- a/pkg/tmdb/config.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package tmdb
-
-import (
- "encoding/json"
-
- "github.com/pkg/errors"
-)
-
-type Config struct {
- Image Image `json:"images"`
-}
-
-type Image struct {
- BaseURL string `json:"base_url"`
- SecureBaseURL string `json:"secure_base_url"`
- BackdropSizes []string `json:"backdrop_sizes"`
- LogoSizes []string `json:"logo_sizes"`
- PosterSizes []string `json:"poster_sizes"`
- ProfileSizes []string `json:"profile_sizes"`
- StillSizes []string `json:"still_sizes"`
-}
-
-func GetConfig(token string) (*Config, error) {
- url := "https://api.themoviedb.org/3/configuration"
- data, err := tmdbGet(url, token)
- if err != nil {
- return nil, errors.Wrap(err, "tmdbGet")
- }
- config := Config{}
- json.Unmarshal(data, &config)
- return &config, nil
-}
diff --git a/pkg/tmdb/credits.go b/pkg/tmdb/credits.go
deleted file mode 100644
index 86d9a23..0000000
--- a/pkg/tmdb/credits.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package tmdb
-
-import (
- "encoding/json"
- "fmt"
-
- "github.com/pkg/errors"
-)
-
-type Credits struct {
- ID int32 `json:"id"`
- Cast []Cast `json:"cast"`
- Crew []Crew `json:"crew"`
-}
-
-type Cast struct {
- Adult bool `json:"adult"`
- Gender int `json:"gender"`
- ID int32 `json:"id"`
- KnownFor string `json:"known_for_department"`
- Name string `json:"name"`
- OriginalName string `json:"original_name"`
- Popularity int `json:"popularity"`
- Profile string `json:"profile_path"`
- CastID int32 `json:"cast_id"`
- Character string `json:"character"`
- CreditID string `json:"credit_id"`
- Order int `json:"order"`
-}
-
-type Crew struct {
- Adult bool `json:"adult"`
- Gender int `json:"gender"`
- ID int32 `json:"id"`
- KnownFor string `json:"known_for_department"`
- Name string `json:"name"`
- OriginalName string `json:"original_name"`
- Popularity int `json:"popularity"`
- Profile string `json:"profile_path"`
- CreditID string `json:"credit_id"`
- Department string `json:"department"`
- Job string `json:"job"`
-}
-
-func GetCredits(movieid int32, token string) (*Credits, error) {
- url := fmt.Sprintf("https://api.themoviedb.org/3/movie/%v/credits?language=en-US", movieid)
- data, err := tmdbGet(url, token)
- if err != nil {
- return nil, errors.Wrap(err, "tmdbGet")
- }
- credits := Credits{}
- json.Unmarshal(data, &credits)
- return &credits, nil
-}
diff --git a/pkg/tmdb/crew_functions.go b/pkg/tmdb/crew_functions.go
deleted file mode 100644
index 03e549c..0000000
--- a/pkg/tmdb/crew_functions.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package tmdb
-
-import "sort"
-
-type BilledCrew struct {
- Name string
- Roles []string
-}
-
-func (credits *Credits) BilledCrew() []BilledCrew {
- crewmap := make(map[string][]string)
- billedcrew := []BilledCrew{}
- for _, crew := range credits.Crew {
- if crew.Job == "Director" ||
- crew.Job == "Screenplay" ||
- crew.Job == "Writer" ||
- crew.Job == "Novel" ||
- crew.Job == "Story" {
- crewmap[crew.Name] = append(crewmap[crew.Name], crew.Job)
- }
- }
-
- for name, jobs := range crewmap {
- billedcrew = append(billedcrew, BilledCrew{Name: name, Roles: jobs})
- }
- for i := range billedcrew {
- sort.Strings(billedcrew[i].Roles)
- }
- sort.Slice(billedcrew, func(i, j int) bool {
- return billedcrew[i].Roles[0] < billedcrew[j].Roles[0]
- })
- return billedcrew
-}
-
-func (billedcrew *BilledCrew) FRoles() string {
- jobs := ""
- for _, job := range billedcrew.Roles {
- jobs += job + ", "
- }
- return jobs[:len(jobs)-2]
-}
diff --git a/pkg/tmdb/movie.go b/pkg/tmdb/movie.go
deleted file mode 100644
index a3b77f7..0000000
--- a/pkg/tmdb/movie.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package tmdb
-
-import (
- "encoding/json"
- "fmt"
-
- "github.com/pkg/errors"
-)
-
-type Movie struct {
- Adult bool `json:"adult"`
- Backdrop string `json:"backdrop_path"`
- Collection string `json:"belongs_to_collection"`
- Budget int `json:"budget"`
- Genres []Genre `json:"genres"`
- Homepage string `json:"homepage"`
- ID int32 `json:"id"`
- IMDbID string `json:"imdb_id"`
- OriginalLanguage string `json:"original_language"`
- OriginalTitle string `json:"original_title"`
- Overview string `json:"overview"`
- Popularity float32 `json:"popularity"`
- Poster string `json:"poster_path"`
- ProductionCompanies []ProductionCompany `json:"production_companies"`
- ProductionCountries []ProductionCountry `json:"production_countries"`
- ReleaseDate string `json:"release_date"`
- Revenue int `json:"revenue"`
- Runtime int `json:"runtime"`
- SpokenLanguages []SpokenLanguage `json:"spoken_languages"`
- Status string `json:"status"`
- Tagline string `json:"tagline"`
- Title string `json:"title"`
- Video bool `json:"video"`
-}
-
-func GetMovie(id int32, token string) (*Movie, error) {
- url := fmt.Sprintf("https://api.themoviedb.org/3/movie/%v?language=en-US", id)
- data, err := tmdbGet(url, token)
- if err != nil {
- return nil, errors.Wrap(err, "tmdbGet")
- }
- movie := Movie{}
- json.Unmarshal(data, &movie)
- return &movie, nil
-}
diff --git a/pkg/tmdb/movie_functions.go b/pkg/tmdb/movie_functions.go
deleted file mode 100644
index ebfa679..0000000
--- a/pkg/tmdb/movie_functions.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package tmdb
-
-import (
- "fmt"
- "net/url"
- "path"
-)
-
-func (movie *Movie) FRuntime() string {
- hours := movie.Runtime / 60
- mins := movie.Runtime % 60
- return fmt.Sprintf("%dh %02dm", hours, mins)
-}
-
-func (movie *Movie) GetPoster(image *Image, size string) string {
- base, err := url.Parse(image.SecureBaseURL)
- if err != nil {
- return ""
- }
- fullPath := path.Join(base.Path, size, movie.Poster)
- base.Path = fullPath
- return base.String()
-}
-
-func (movie *Movie) ReleaseYear() string {
- if movie.ReleaseDate == "" {
- return ""
- } else {
- return "(" + movie.ReleaseDate[:4] + ")"
- }
-}
-
-func (movie *Movie) FGenres() string {
- genres := ""
- for _, genre := range movie.Genres {
- genres += genre.Name + ", "
- }
- if len(genres) > 2 {
- return genres[:len(genres)-2]
- }
- return genres
-}
diff --git a/pkg/tmdb/request.go b/pkg/tmdb/request.go
deleted file mode 100644
index 6c454b4..0000000
--- a/pkg/tmdb/request.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package tmdb
-
-import (
- "fmt"
- "io"
- "net/http"
-
- "github.com/pkg/errors"
-)
-
-func tmdbGet(url string, token string) ([]byte, error) {
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- return nil, errors.Wrap(err, "http.NewRequest")
- }
- req.Header.Add("accept", "application/json")
- req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
- res, err := http.DefaultClient.Do(req)
- if err != nil {
- return nil, errors.Wrap(err, "http.DefaultClient.Do")
- }
- defer res.Body.Close()
- body, err := io.ReadAll(res.Body)
- if err != nil {
- return nil, errors.Wrap(err, "io.ReadAll")
- }
- return body, nil
-}
diff --git a/pkg/tmdb/search.go b/pkg/tmdb/search.go
deleted file mode 100644
index 701ff04..0000000
--- a/pkg/tmdb/search.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package tmdb
-
-import (
- "encoding/json"
- "fmt"
- "net/url"
- "path"
-
- "github.com/pkg/errors"
-)
-
-type Result struct {
- Page int `json:"page"`
- TotalPages int `json:"total_pages"`
- TotalResults int `json:"total_results"`
-}
-
-type ResultMovies struct {
- Result
- Results []ResultMovie `json:"results"`
-}
-type ResultMovie struct {
- Adult bool `json:"adult"`
- BackdropPath string `json:"backdrop_path"`
- GenreIDs []int `json:"genre_ids"`
- ID int32 `json:"id"`
- OriginalLanguage string `json:"original_language"`
- OriginalTitle string `json:"original_title"`
- Overview string `json:"overview"`
- Popularity int `json:"popularity"`
- PosterPath string `json:"poster_path"`
- ReleaseDate string `json:"release_date"`
- Title string `json:"title"`
- Video bool `json:"video"`
- VoteAverage int `json:"vote_average"`
- VoteCount int `json:"vote_count"`
-}
-
-func (movie *ResultMovie) GetPoster(image *Image, size string) string {
- base, err := url.Parse(image.SecureBaseURL)
- if err != nil {
- return ""
- }
- fullPath := path.Join(base.Path, size, movie.PosterPath)
- base.Path = fullPath
- return base.String()
-}
-
-func (movie *ResultMovie) ReleaseYear() string {
- if movie.ReleaseDate == "" {
- return ""
- } else {
- return "(" + movie.ReleaseDate[:4] + ")"
- }
-}
-
-// TODO: genres list https://developer.themoviedb.org/reference/genre-movie-list
-// func (movie *ResultMovie) FGenres() string {
-// genres := ""
-// for _, genre := range movie.Genres {
-// genres += genre.Name + ", "
-// }
-// return genres[:len(genres)-2]
-// }
-
-func SearchMovies(token string, query string, adult bool, page int) (*ResultMovies, error) {
- url := "https://api.themoviedb.org/3/search/movie" +
- fmt.Sprintf("?query=%s", url.QueryEscape(query)) +
- fmt.Sprintf("&include_adult=%t", adult) +
- fmt.Sprintf("&page=%v", page) +
- "&language=en-US"
- response, err := tmdbGet(url, token)
- if err != nil {
- return nil, errors.Wrap(err, "tmdbGet")
- }
- var results ResultMovies
- json.Unmarshal(response, &results)
- return &results, nil
-}
diff --git a/pkg/tmdb/structs.go b/pkg/tmdb/structs.go
deleted file mode 100644
index 3a68d08..0000000
--- a/pkg/tmdb/structs.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package tmdb
-
-type Genre struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
-
-type ProductionCompany struct {
- ID int `json:"id"`
- Logo string `json:"logo_path"`
- Name string `json:"name"`
- OriginCountry string `json:"origin_country"`
-}
-
-type ProductionCountry struct {
- ISO_3166_1 string `json:"iso_3166_1"`
- Name string `json:"name"`
-}
-
-type SpokenLanguage struct {
- EnglishName string `json:"english_name"`
- ISO_639_1 string `json:"iso_639_1"`
- Name string `json:"name"`
-}
diff --git a/tester.go b/tester.go
deleted file mode 100644
index e474d82..0000000
--- a/tester.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package main
-
-import (
- "net/http"
-
- "projectreshoot/config"
- "projectreshoot/db"
-
- "github.com/rs/zerolog"
-)
-
-// This function will only be called if the --test commandline flag is set.
-// After the function finishes the application will close.
-// Running command `make tester` will run the test using port 3232 to avoid
-// conflicts on the default 3333. Useful for testing things out during dev.
-// If you add code here, remember to run:
-// `git update-index --assume-unchanged tester.go` to avoid tracking changes
-func test(
- config *config.Config,
- logger *zerolog.Logger,
- conn *db.SafeConn,
- srv *http.Server,
-) {
-}