From e9c44cbc94614bd87b6fc14bbf92c79ef535561b Mon Sep 17 00:00:00 2001 From: trev Date: Thu, 10 Apr 2025 00:59:28 -0400 Subject: [PATCH] feat: bob --- .github/workflows/lint.yaml | 6 - .github/workflows/release.yaml | 6 - .vscode/settings.json | 6 + cli/cmd/ts-run/ts-run.go | 89 ---- cli/go.mod | 31 -- cli/go.sum | 47 --- cli/internal/apps/msg.go | 19 - cli/internal/apps/node.go | 118 ------ cli/internal/apps/proto.go | 269 ------------ cli/internal/models/cbox.go | 82 ---- cli/internal/models/header.go | 33 -- cli/internal/models/help.go | 84 ---- cli/internal/models/runner.go | 220 ---------- cli/internal/utils/pointers.go | 13 - cli/internal/utils/run.go | 55 --- client/src/lib/index.ts | 1 - client/src/lib/services/item/v1/item_pb.ts | 22 +- client/src/lib/services/user/v1/auth_pb.ts | 112 +---- client/src/lib/services/user/v1/user_pb.ts | 78 +--- client/src/lib/ui/Avatar.svelte | 2 +- client/src/lib/ui/Button.svelte | 9 +- client/src/lib/ui/Pagination.svelte | 5 +- client/src/routes/(app)/items/+page.svelte | 4 +- client/src/routes/(app)/settings/+page.svelte | 3 +- client/static/openapi/openapi.yaml | 257 ++---------- client/vite.config.ts | 2 +- flake.lock | 93 ++++- flake.nix | 12 +- proto/item/v1/item.proto | 16 +- proto/user/v1/auth.proto | 26 +- proto/user/v1/user.proto | 16 +- server/bobgen.yaml | 10 + server/internal/database/logger.go | 22 - server/internal/database/migrate.go | 15 - server/internal/database/postgres.go | 16 +- server/internal/database/sqlite.go | 16 +- server/internal/handlers/{ => file}/file.go | 55 ++- server/internal/handlers/item/v1/item.go | 139 +++++-- server/internal/handlers/user/v1/auth.go | 206 ++++----- server/internal/handlers/user/v1/user.go | 153 ++++--- server/internal/interceptors/auth.go | 8 +- server/internal/interceptors/cors.go | 19 + server/internal/interceptors/ratelimit.go | 4 +- server/internal/models/bob_main.bob.go | 9 +- .../models/factory/bobfactory_random.bob.go | 4 +- .../factory/bobfactory_random_test.bob.go | 30 +- .../factory/{files.bob.go => file.bob.go} | 187 ++++++--- .../factory/{items.bob.go => item.bob.go} | 193 +++++---- .../factory/{users.bob.go => user.bob.go} | 166 ++++---- .../models/{files.bob.go => file.bob.go} | 269 ++++++++++-- .../models/{items.bob.go => item.bob.go} | 122 +++--- .../models/{users.bob.go => user.bob.go} | 303 ++++++++++---- server/internal/services/item/v1/item.pb.go | 42 +- server/internal/services/user/v1/auth.pb.go | 361 ++-------------- server/internal/services/user/v1/user.pb.go | 391 +++++------------- .../user/v1/userv1connect/auth.connect.go | 93 +---- .../user/v1/userv1connect/user.connect.go | 66 +-- server/main.go | 118 +++--- 58 files changed, 1649 insertions(+), 3104 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 cli/cmd/ts-run/ts-run.go delete mode 100644 cli/go.mod delete mode 100644 cli/go.sum delete mode 100644 cli/internal/apps/msg.go delete mode 100644 cli/internal/apps/node.go delete mode 100644 cli/internal/apps/proto.go delete mode 100644 cli/internal/models/cbox.go delete mode 100644 cli/internal/models/header.go delete mode 100644 cli/internal/models/help.go delete mode 100644 cli/internal/models/runner.go delete mode 100644 cli/internal/utils/pointers.go delete mode 100644 cli/internal/utils/run.go delete mode 100644 client/src/lib/index.ts delete mode 100644 server/internal/database/logger.go delete mode 100644 server/internal/database/migrate.go rename server/internal/handlers/{ => file}/file.go (52%) create mode 100644 server/internal/interceptors/cors.go rename server/internal/models/factory/{files.bob.go => file.bob.go} (75%) rename server/internal/models/factory/{items.bob.go => item.bob.go} (86%) rename server/internal/models/factory/{users.bob.go => user.bob.go} (83%) rename server/internal/models/{files.bob.go => file.bob.go} (69%) rename server/internal/models/{items.bob.go => item.bob.go} (88%) rename server/internal/models/{users.bob.go => user.bob.go} (72%) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index d563907..a8bbfd1 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -20,12 +20,6 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - - name: Use Cachix - uses: cachix/cachix-action@v16 - with: - name: trevstack - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - name: Install NPM Packages working-directory: ./client run: npm ci --legacy-peer-deps diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6fe3c8e..1e45c58 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,12 +19,6 @@ jobs: uses: cachix/install-nix-action@v31 with: nix_path: nixpkgs=channel:nixos-unstable - - - name: Use Cachix - uses: cachix/cachix-action@v16 - with: - name: trevstack - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Build run: nix develop --command ts-build diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bb1f4cf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "go.lintTool": "revive", + "go.lintFlags": [ + "--config=server/revive.toml" + ], +} \ No newline at end of file diff --git a/cli/cmd/ts-run/ts-run.go b/cli/cmd/ts-run/ts-run.go deleted file mode 100644 index f7db522..0000000 --- a/cli/cmd/ts-run/ts-run.go +++ /dev/null @@ -1,89 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - - "github.com/boyter/gocodewalker" -) - -type env struct { - DBType string - DBUser string - DBPass string - DBHost string - DBPort string - DBName string - - RootDir string - NodeDir string - ProtoDir string -} - -func main() { - // Get pwd - path, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - fmt.Printf("Current path: %s\n", path) - - findApps(path) - return - - // c := make(chan apps.Msg, 10) - - // // Create protobuf watcher - // proto, err := apps.NewProto(env.ProtoDir, env.RootDir, c) - // if err != nil { - // log.Fatal(err) - // } - - // // Create node watcher - // node := apps.NewNode(env.NodeDir, c) - // if err != nil { - // log.Fatal(err) - // } - - // apps := []*apps.App{ - // &proto.App, - // &node.App, - // } - - // // Start tea - // p := tea.NewProgram( - // models.NewRunner(c, apps), - // tea.WithAltScreen(), - // tea.WithMouseCellMotion(), - // ) - // if _, err := p.Run(); err != nil { - // fmt.Printf("Alas, there's been an error: %v", err) - // } - - // // Cancel watchers - // proto.Cancel() - // proto.Wait() - - // node.Cancel() - // node.Wait() - - // close(c) -} - -func findApps(path string) { - fileListQueue := make(chan *gocodewalker.File, 100) - fileWalker := gocodewalker.NewFileWalker(path, fileListQueue) - - errorHandler := func(e error) bool { - fmt.Println("ERR", e.Error()) - return true - } - fileWalker.SetErrorHandler(errorHandler) - - go fileWalker.Start() - - for f := range fileListQueue { - fmt.Printf("%s, %s\n", f.Filename, f.Location) - } -} diff --git a/cli/go.mod b/cli/go.mod deleted file mode 100644 index afcbb5d..0000000 --- a/cli/go.mod +++ /dev/null @@ -1,31 +0,0 @@ -module github.com/spotdemo4/trevstack/cli - -go 1.23.6 - -require ( - github.com/charmbracelet/bubbles v0.20.0 - github.com/charmbracelet/bubbletea v1.3.4 - github.com/charmbracelet/lipgloss v1.0.0 - github.com/fsnotify/fsnotify v1.8.0 - github.com/joho/godotenv v1.5.1 -) - -require ( - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/boyter/gocodewalker v1.4.0 // indirect - github.com/charmbracelet/x/ansi v0.8.0 // indirect - github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.3.8 // indirect -) diff --git a/cli/go.sum b/cli/go.sum deleted file mode 100644 index 7b95fb0..0000000 --- a/cli/go.sum +++ /dev/null @@ -1,47 +0,0 @@ -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/boyter/gocodewalker v1.4.0 h1:fVmFeQxKpj5tlpjPcyTtJ96btgaHYd9yn6m+T/66et4= -github.com/boyter/gocodewalker v1.4.0/go.mod h1:hXG8xzR1uURS+99P5/3xh3uWHjaV2XfoMMmvPyhrCDg= -github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= -github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= -github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= -github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= -github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= -github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= -github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= -github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -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/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.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/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= diff --git a/cli/internal/apps/msg.go b/cli/internal/apps/msg.go deleted file mode 100644 index ceb6258..0000000 --- a/cli/internal/apps/msg.go +++ /dev/null @@ -1,19 +0,0 @@ -package apps - -import "time" - -type Msg struct { - Text string - Time time.Time - Key *string - Loading *bool - Success *bool - - App *App -} - -type App struct { - Name string - Color string - Loading *bool -} diff --git a/cli/internal/apps/node.go b/cli/internal/apps/node.go deleted file mode 100644 index 2b51d50..0000000 --- a/cli/internal/apps/node.go +++ /dev/null @@ -1,118 +0,0 @@ -package apps - -import ( - "context" - "fmt" - "os" - "os/exec" - "sync" - "time" - - "github.com/spotdemo4/trevstack/cli/internal/utils" -) - -type Node struct { - App App - c chan Msg - ctx context.Context - wg *sync.WaitGroup - - dir string - - Cancel context.CancelFunc - Wait func() -} - -func NewNode(dir string, c chan Msg) *Node { - - // Create new context - ctx, cancel := context.WithCancel(context.Background()) - - // Create wait group - wg := sync.WaitGroup{} - - node := Node{ - App: App{ - Name: "node", - Color: "#fab387", - }, - c: c, - ctx: ctx, - wg: &wg, - - dir: dir, - - Cancel: cancel, - Wait: wg.Wait, - } - - // Start watching - go node.dev() - - return &node -} - -func (n *Node) msg(m Msg) { - m.Time = time.Now() - m.App = &n.App - n.c <- m -} - -func (n *Node) dev() { - n.wg.Add(1) - defer n.wg.Done() - - // Create cmd - cmd := exec.Command("npm", "run", "dev") - cmd.Dir = n.dir - - // Stop cmd on exit - n.wg.Add(1) - go func() { - defer n.wg.Done() - <-n.ctx.Done() - - if err := cmd.Process.Signal(os.Interrupt); err != nil { - cmd.Process.Kill() // If the process is not responding to the interrupt signal, kill it - } - }() - - // Start cmd - out, err := utils.Run(cmd) - if err != nil { - n.msg(Msg{ - Text: err.Error(), - Success: utils.BoolPointer(false), - }) - return - } - - // Watch for output - for line := range out { - switch line := line.(type) { - case utils.Stdout: - n.msg(Msg{ - Text: string(line), - }) - - case utils.Stderr: - n.msg(Msg{ - Text: string(line), - Success: utils.BoolPointer(false), - }) - - case utils.ExitCode: - if line == 0 { - n.msg(Msg{ - Text: "Node stopped", - Success: utils.BoolPointer(true), - }) - } else { - n.msg(Msg{ - Text: fmt.Sprintf("Node failed with exit code %d", out), - Success: utils.BoolPointer(false), - }) - } - } - } -} diff --git a/cli/internal/apps/proto.go b/cli/internal/apps/proto.go deleted file mode 100644 index 79e9fe4..0000000 --- a/cli/internal/apps/proto.go +++ /dev/null @@ -1,269 +0,0 @@ -package apps - -import ( - "context" - "fmt" - "io/fs" - "os/exec" - "path/filepath" - "slices" - "strings" - "sync" - "time" - - "github.com/fsnotify/fsnotify" - "github.com/spotdemo4/trevstack/cli/internal/utils" -) - -type Proto struct { - App App - c chan Msg - ctx context.Context - wg *sync.WaitGroup - watcher *fsnotify.Watcher - - dir string - rootDir string - - Cancel context.CancelFunc - Wait func() -} - -func NewProto(dir string, rootDir string, c chan Msg) (*Proto, error) { - - // Create new watcher - watcher, err := fsnotify.NewWatcher() - if err != nil { - return nil, err - } - - // Add directory to watcher - err = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - - if d.IsDir() { - if slices.Contains(watcher.WatchList(), path) { - return nil - } - - err := watcher.Add(path) - if err != nil { - return err - } - } - - return nil - }) - if err != nil { - return nil, err - } - - // Create new context - ctx, cancel := context.WithCancel(context.Background()) - - // Create wait group - wg := sync.WaitGroup{} - - proto := Proto{ - App: App{ - Name: "proto", - Color: "#89dceb", - }, - c: c, - ctx: ctx, - wg: &wg, - watcher: watcher, - - dir: dir, - rootDir: rootDir, - - Cancel: cancel, - Wait: wg.Wait, - } - - // Start watching - go proto.watch() - - return &proto, nil -} - -func (p *Proto) msg(m Msg) { - m.Time = time.Now() - m.App = &p.App - p.c <- m -} - -func (p *Proto) watch() { - p.wg.Add(1) - defer p.wg.Done() - defer p.watcher.Close() - - // Create new rate limit map - rateLimit := make(map[string]time.Time) - - p.lint() - - p.msg(Msg{ - Text: "Watching for proto changes...", - }) - - for { - select { - case <-p.ctx.Done(): - return - - case event, ok := <-p.watcher.Events: - if !ok { - return - } - - // Rate limit - rl, ok := rateLimit[event.Name] - if ok && time.Since(rl) < 1*time.Second { - continue - } - rateLimit[event.Name] = time.Now() - - p.msg(Msg{ - Text: "File changed: " + strings.TrimPrefix(event.Name, p.dir), - }) - - ok, _ = p.lint() - if !ok { - continue - } - - p.generate() - - case err, ok := <-p.watcher.Errors: - if !ok { - return - } - - p.msg(Msg{ - Text: err.Error(), - Success: utils.BoolPointer(false), - }) - } - } -} - -func (p *Proto) lint() (bool, error) { - p.msg(Msg{ - Text: "Linting", - Loading: utils.BoolPointer(true), - Key: utils.StringPointer("lint"), - }) - - // Run buf lint - cmd := exec.Command("buf", "lint") - cmd.Dir = p.rootDir - out, err := utils.Run(cmd) - if err != nil { - p.msg(Msg{ - Text: err.Error(), - Success: utils.BoolPointer(false), - }) - return false, err - } - - // Watch for output - for line := range out { - switch line := line.(type) { - case utils.Stdout: - p.msg(Msg{ - Text: string(line), - }) - - case utils.Stderr: - p.msg(Msg{ - Text: string(line), - Success: utils.BoolPointer(false), - }) - - case utils.ExitCode: - if line == 0 { - p.msg(Msg{ - Text: "Buf lint successful", - Success: utils.BoolPointer(true), - Loading: utils.BoolPointer(false), - Key: utils.StringPointer("lint"), - }) - - return true, nil - } - - p.msg(Msg{ - Text: fmt.Sprintf("Buf lint failed with exit code %d", out), - Success: utils.BoolPointer(false), - Loading: utils.BoolPointer(false), - Key: utils.StringPointer("lint"), - }) - - return false, fmt.Errorf("buf lint failed with exit code %d", line) - } - } - - return false, fmt.Errorf("buf lint failed") -} - -func (p *Proto) generate() error { - p.msg(Msg{ - Text: "Generating proto files", - Loading: utils.BoolPointer(true), - Key: utils.StringPointer("generate"), - }) - - // Run buf gen - cmd := exec.Command("buf", "generate") - cmd.Dir = p.rootDir - out, err := utils.Run(cmd) - if err != nil { - p.msg(Msg{ - Text: err.Error(), - Success: utils.BoolPointer(false), - }) - return err - } - - // Watch for output - for line := range out { - switch line := line.(type) { - case utils.Stdout: - p.msg(Msg{ - Text: string(line), - }) - - case utils.Stderr: - p.msg(Msg{ - Text: string(line), - Success: utils.BoolPointer(false), - }) - - case utils.ExitCode: - if line == 0 { - p.msg(Msg{ - Text: "Buf generate successful", - Success: utils.BoolPointer(true), - Loading: utils.BoolPointer(false), - Key: utils.StringPointer("generate"), - }) - - return nil - } - - p.msg(Msg{ - Text: fmt.Sprintf("Buf generate failed with exit code %d", out), - Success: utils.BoolPointer(false), - Loading: utils.BoolPointer(false), - Key: utils.StringPointer("generate"), - }) - - return fmt.Errorf("generate failed with exit code %d", line) - } - } - - return fmt.Errorf("generate failed") -} diff --git a/cli/internal/models/cbox.go b/cli/internal/models/cbox.go deleted file mode 100644 index 77c667a..0000000 --- a/cli/internal/models/cbox.go +++ /dev/null @@ -1,82 +0,0 @@ -package models - -import ( - "time" - - "github.com/charmbracelet/bubbles/viewport" - "github.com/charmbracelet/lipgloss" -) - -type Cbox struct { - style lipgloss.Style - Viewport *viewport.Model - maxPrefixLen int -} - -func NewCbox(maxPrefixLen int) *Cbox { - s := lipgloss.NewStyle(). - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color("#45475a")). - BorderTop(true). - BorderBottom(true). - Margin(1, 0, 0) - - return &Cbox{ - style: s, - Viewport: nil, - maxPrefixLen: maxPrefixLen, - } -} - -func (c *Cbox) Gen(text string, width int, height int, mtop int, mbottom int) string { - if c.Viewport == nil { - vp := viewport.New(width, height-(mtop+mbottom)) - c.Viewport = &vp - c.Viewport.YPosition = mtop - c.Viewport.Style = c.style - } else { - c.Viewport.Width = width - c.Viewport.Height = height - (mtop + mbottom) - c.Viewport.YPosition = mtop - } - - atBottom := c.Viewport.AtBottom() - - // Need to add extra lines because of https://github.com/charmbracelet/bubbles/pull/731 - c.Viewport.SetContent(text + "\n\n\n") - - if atBottom { - c.Viewport.GotoBottom() - } - - return c.Viewport.View() -} - -func (c *Cbox) GenItem(ti time.Time, prefix string, text string, color string, width int) string { - t := lipgloss.NewStyle(). - Padding(0, 1, 0, 1). - Foreground(lipgloss.Color("#a6adc8")). - Render(ti.Format(time.Kitchen)) - - p := lipgloss.NewStyle(). - Padding(0, 1, 0, 0). - Width(c.maxPrefixLen). - Foreground(lipgloss.Color(color)). - BorderStyle(lipgloss.NormalBorder()). - BorderRight(true). - BorderForeground(lipgloss.Color(color)) - - m := lipgloss.NewStyle(). - Padding(0, 1, 0, 1). - Foreground(lipgloss.Color("#cdd6f4")). - Width(width - lipgloss.Width(t) - lipgloss.Width(p.Render(prefix))). - Render(text) - - p = p.Height(lipgloss.Height(m)) - - combine := lipgloss.JoinHorizontal(lipgloss.Top, t, p.Render(prefix), m) - - return lipgloss.NewStyle(). - Width(width). - Render(combine) -} diff --git a/cli/internal/models/header.go b/cli/internal/models/header.go deleted file mode 100644 index 19a33bc..0000000 --- a/cli/internal/models/header.go +++ /dev/null @@ -1,33 +0,0 @@ -package models - -import "github.com/charmbracelet/lipgloss" - -type Header struct { - style lipgloss.Style -} - -func NewHeader() *Header { - s := lipgloss.NewStyle(). - AlignHorizontal(lipgloss.Center). - AlignVertical(lipgloss.Bottom). - MarginTop(1) - - return &Header{ - style: s, - } -} - -func (h *Header) Gen(width int, items ...string) string { - s := h.style.Width(width) - - pp := lipgloss.JoinHorizontal(lipgloss.Center, items...) - - return s.Render(pp) -} - -func (h *Header) GenItem(text string) string { - return lipgloss.NewStyle(). - Foreground(lipgloss.Color("#cdd6f4")). - Margin(0, 1). - Render(text) -} diff --git a/cli/internal/models/help.go b/cli/internal/models/help.go deleted file mode 100644 index 042cbc5..0000000 --- a/cli/internal/models/help.go +++ /dev/null @@ -1,84 +0,0 @@ -package models - -import ( - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/lipgloss" -) - -// keyMap defines a set of keybindings. To work for help it must satisfy -// key.Map. It could also very easily be a map[string]key.Binding. -type keyMap struct { - Up key.Binding - Down key.Binding - Left key.Binding - Right key.Binding - Help key.Binding - Quit key.Binding -} - -// ShortHelp returns keybindings to be shown in the mini help view. It's part -// of the key.Map interface. -func (k keyMap) ShortHelp() []key.Binding { - return []key.Binding{k.Help, k.Quit} -} - -// FullHelp returns keybindings for the expanded help view. It's part of the -// key.Map interface. -func (k keyMap) FullHelp() [][]key.Binding { - return [][]key.Binding{ - {k.Up, k.Down, k.Left, k.Right}, // first column - {k.Help, k.Quit}, // second column - } -} - -type Help struct { - keys keyMap - style lipgloss.Style - help help.Model -} - -func NewHelp() *Help { - keys := keyMap{ - Up: key.NewBinding( - key.WithKeys("up", "k"), - key.WithHelp("↑/k", "move up"), - ), - Down: key.NewBinding( - key.WithKeys("down", "j"), - key.WithHelp("↓/j", "move down"), - ), - Left: key.NewBinding( - key.WithKeys("left", "h"), - key.WithHelp("←/h", "move left"), - ), - Right: key.NewBinding( - key.WithKeys("right", "l"), - key.WithHelp("→/l", "move right"), - ), - Help: key.NewBinding( - key.WithKeys("?"), - key.WithHelp("?", "toggle help"), - ), - Quit: key.NewBinding( - key.WithKeys("q", "esc", "ctrl+c"), - key.WithHelp("q", "quit"), - ), - } - - return &Help{ - keys: keys, - style: lipgloss.NewStyle().Padding(1, 2), - help: help.New(), - } -} - -func (h *Help) Gen(width int) string { - h.help.Width = width - render := h.help.View(h.keys) - return h.style.Render(render) -} - -func (h *Help) Toggle() { - h.help.ShowAll = !h.help.ShowAll -} diff --git a/cli/internal/models/runner.go b/cli/internal/models/runner.go deleted file mode 100644 index cad0932..0000000 --- a/cli/internal/models/runner.go +++ /dev/null @@ -1,220 +0,0 @@ -package models - -import ( - "fmt" - "strings" - - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/spinner" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/spotdemo4/trevstack/cli/internal/apps" - "github.com/spotdemo4/trevstack/cli/internal/utils" -) - -type runner struct { - width *int - height *int - - prefix lipgloss.Style - checkmark string - xmark string - - header *Header - cbox *Cbox - help *Help - spinner spinner.Model - - msgChan chan apps.Msg - msgs []apps.Msg - apps []*apps.App -} - -func NewRunner(msgChan chan apps.Msg, applications []*apps.App) *runner { - - prefix := lipgloss.NewStyle(). - Padding(0, 1, 0, 1). - Margin(0, 1, 0, 1). - Background(lipgloss.Color("#89dceb")). - Foreground(lipgloss.Color("#11111b")) - - checkmark := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#a6e3a1")). - Bold(true). - Render("✓") - - xmark := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#f38ba8")). - Bold(true). - Render("✕") - - mpl := 0 - for _, app := range applications { - if len(app.Name) > mpl { - mpl = len(app.Name) - } - } - - return &runner{ - width: nil, - height: nil, - - prefix: prefix, - checkmark: checkmark, - xmark: xmark, - - header: NewHeader(), - cbox: NewCbox(mpl + 1), - help: NewHelp(), - spinner: spinner.New(spinner.WithSpinner(spinner.MiniDot), spinner.WithStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("#a6adc8")))), - - msgChan: msgChan, - msgs: []apps.Msg{}, - apps: applications, - } -} - -func (m runner) Init() tea.Cmd { - return tea.Batch( - m.spinner.Tick, - func() tea.Msg { - return apps.Msg(<-m.msgChan) - }, - ) -} - -func (m runner) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmd tea.Cmd - var cmds []tea.Cmd - - switch msg := msg.(type) { - - case apps.Msg: - // Remove old message with the same key - if msg.Key != nil && msg.Loading != nil && !*msg.Loading { - for i, prev := range m.msgs { - if prev.Key != nil && prev.Loading != nil && *prev.Key == *msg.Key && *prev.Loading { - m.msgs = append(m.msgs[:i], m.msgs[i+1:]...) - break - } - } - } - - // Set current state - if msg.Loading != nil { - if *msg.Loading { - msg.App.Loading = nil - } else { - msg.App.Loading = msg.Success - } - } - - // Append new message - m.msgs = append(m.msgs, msg) - - return m, func() tea.Msg { - return apps.Msg(<-m.msgChan) - } - - case spinner.TickMsg: - m.spinner, cmd = m.spinner.Update(msg) - return m, cmd - - case tea.WindowSizeMsg: - m.width = utils.IntPointer(msg.Width) - m.height = utils.IntPointer(msg.Height) - - case tea.KeyMsg: - switch { - case key.Matches(msg, m.help.keys.Help): - m.help.Toggle() - - case key.Matches(msg, m.help.keys.Quit): - return m, tea.Quit - } - - case tea.MouseMsg: - switch msg.Button { - case tea.MouseButtonWheelDown: - m.cbox.Viewport.LineDown(1) - - case tea.MouseButtonWheelUp: - m.cbox.Viewport.LineUp(1) - } - } - - return m, tea.Batch(cmds...) -} - -func (m runner) term() (rows []string) { - if m.width == nil { - return rows - } - - for _, msg := range m.msgs { - item := []string{} - - if msg.Loading != nil && *msg.Loading { - item = append(item, m.spinner.View()) - } - - if msg.Success != nil { - if *msg.Success { - item = append(item, m.checkmark) - } else { - item = append(item, m.xmark) - } - } - - item = append(item, msg.Text) - itemStr := strings.Join(item, " ") - - // Render the row - rows = append(rows, m.cbox.GenItem(msg.Time, msg.App.Name, itemStr, msg.App.Color, *m.width)) - } - - return rows -} - -func (m runner) head() (items []string) { - for _, app := range m.apps { - item := []string{} - - if app.Loading == nil { - item = append(item, m.spinner.View()) - } else if *app.Loading { - item = append(item, m.checkmark) - } else { - item = append(item, m.xmark) - } - - item = append(item, app.Name) - items = append(items, m.header.GenItem(strings.Join(item, " "))) - } - - return items -} - -func (m runner) View() string { - if m.width == nil || m.height == nil { - return fmt.Sprintf("\n %s Loading...", m.spinner.View()) - } - - // Generate the UI - header := m.header.Gen(*m.width, m.head()...) - footer := m.help.Gen(*m.width) - main := m.cbox.Gen( - strings.Join(m.term(), "\n"), - *m.width, - *m.height, - lipgloss.Height(header), - lipgloss.Height(footer), - ) - - s := header - s += main - s += footer - - // Send the UI for rendering - return s -} diff --git a/cli/internal/utils/pointers.go b/cli/internal/utils/pointers.go deleted file mode 100644 index e047ed8..0000000 --- a/cli/internal/utils/pointers.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -func BoolPointer(b bool) *bool { - return &b -} - -func StringPointer(s string) *string { - return &s -} - -func IntPointer(i int) *int { - return &i -} diff --git a/cli/internal/utils/run.go b/cli/internal/utils/run.go deleted file mode 100644 index 32ed9b3..0000000 --- a/cli/internal/utils/run.go +++ /dev/null @@ -1,55 +0,0 @@ -package utils - -import ( - "bufio" - "os/exec" -) - -type Stdout string -type Stderr string -type ExitCode int - -func Run(cmd *exec.Cmd) (chan interface{}, error) { - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - stderr, err := cmd.StderrPipe() - if err != nil { - return nil, err - } - if err := cmd.Start(); err != nil { - return nil, err - } - - c := make(chan interface{}, 10) - - go func() { - scan := bufio.NewScanner(stdout) - for scan.Scan() { - c <- Stdout(scan.Text()) - } - }() - go func() { - scan := bufio.NewScanner(stderr) - for scan.Scan() { - c <- Stderr(scan.Text()) - } - }() - - go func() { - defer close(c) - - if err := cmd.Wait(); err != nil { - if exitError, ok := err.(*exec.ExitError); ok { - c <- ExitCode(exitError.ExitCode()) - } else { - c <- ExitCode(1) - } - } else { - c <- ExitCode(0) - } - }() - - return c, nil -} diff --git a/client/src/lib/index.ts b/client/src/lib/index.ts deleted file mode 100644 index 856f2b6..0000000 --- a/client/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/client/src/lib/services/item/v1/item_pb.ts b/client/src/lib/services/item/v1/item_pb.ts index 9fe15c9..0fa9365 100644 --- a/client/src/lib/services/item/v1/item_pb.ts +++ b/client/src/lib/services/item/v1/item_pb.ts @@ -12,16 +12,16 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file item/v1/item.proto. */ export const file_item_v1_item: GenFile = /*@__PURE__*/ - fileDesc("ChJpdGVtL3YxL2l0ZW0ucHJvdG8SB2l0ZW0udjEinAEKBEl0ZW0SDwoCaWQYASABKA1IAIgBARIMCgRuYW1lGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg0KBXByaWNlGAQgASgCEhAKCHF1YW50aXR5GAUgASgNEi4KBWFkZGVkGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEgBiAEBQgUKA19pZEIICgZfYWRkZWQiHAoOR2V0SXRlbVJlcXVlc3QSCgoCaWQYASABKA0iLgoPR2V0SXRlbVJlc3BvbnNlEhsKBGl0ZW0YASABKAsyDS5pdGVtLnYxLkl0ZW0i3wEKD0dldEl0ZW1zUmVxdWVzdBIuCgVzdGFydBgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBIAIgBARIsCgNlbmQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSAGIAQESEwoGZmlsdGVyGAMgASgJSAKIAQESEgoFbGltaXQYBCABKA1IA4gBARITCgZvZmZzZXQYBSABKA1IBIgBAUIICgZfc3RhcnRCBgoEX2VuZEIJCgdfZmlsdGVyQggKBl9saW1pdEIJCgdfb2Zmc2V0Ij8KEEdldEl0ZW1zUmVzcG9uc2USHAoFaXRlbXMYASADKAsyDS5pdGVtLnYxLkl0ZW0SDQoFY291bnQYAiABKAQiMAoRQ3JlYXRlSXRlbVJlcXVlc3QSGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIxChJDcmVhdGVJdGVtUmVzcG9uc2USGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIwChFVcGRhdGVJdGVtUmVxdWVzdBIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIjEKElVwZGF0ZUl0ZW1SZXNwb25zZRIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIh8KEURlbGV0ZUl0ZW1SZXF1ZXN0EgoKAmlkGAEgASgNIhQKEkRlbGV0ZUl0ZW1SZXNwb25zZTLrAgoLSXRlbVNlcnZpY2USPgoHR2V0SXRlbRIXLml0ZW0udjEuR2V0SXRlbVJlcXVlc3QaGC5pdGVtLnYxLkdldEl0ZW1SZXNwb25zZSIAEkEKCEdldEl0ZW1zEhguaXRlbS52MS5HZXRJdGVtc1JlcXVlc3QaGS5pdGVtLnYxLkdldEl0ZW1zUmVzcG9uc2UiABJHCgpDcmVhdGVJdGVtEhouaXRlbS52MS5DcmVhdGVJdGVtUmVxdWVzdBobLml0ZW0udjEuQ3JlYXRlSXRlbVJlc3BvbnNlIgASRwoKVXBkYXRlSXRlbRIaLml0ZW0udjEuVXBkYXRlSXRlbVJlcXVlc3QaGy5pdGVtLnYxLlVwZGF0ZUl0ZW1SZXNwb25zZSIAEkcKCkRlbGV0ZUl0ZW0SGi5pdGVtLnYxLkRlbGV0ZUl0ZW1SZXF1ZXN0GhsuaXRlbS52MS5EZWxldGVJdGVtUmVzcG9uc2UiAEKdAQoLY29tLml0ZW0udjFCCUl0ZW1Qcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL2l0ZW0vdjE7aXRlbXYxogIDSVhYqgIHSXRlbS5WMcoCB0l0ZW1cVjHiAhNJdGVtXFYxXEdQQk1ldGFkYXRh6gIISXRlbTo6VjFiBnByb3RvMw", [file_google_protobuf_timestamp]); + fileDesc("ChJpdGVtL3YxL2l0ZW0ucHJvdG8SB2l0ZW0udjEinAEKBEl0ZW0SDwoCaWQYASABKANIAIgBARIMCgRuYW1lGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg0KBXByaWNlGAQgASgCEhAKCHF1YW50aXR5GAUgASgFEi4KBWFkZGVkGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEgBiAEBQgUKA19pZEIICgZfYWRkZWQiHAoOR2V0SXRlbVJlcXVlc3QSCgoCaWQYASABKAMiLgoPR2V0SXRlbVJlc3BvbnNlEhsKBGl0ZW0YASABKAsyDS5pdGVtLnYxLkl0ZW0i3wEKD0dldEl0ZW1zUmVxdWVzdBIuCgVzdGFydBgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBIAIgBARIsCgNlbmQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSAGIAQESEwoGZmlsdGVyGAMgASgJSAKIAQESEgoFbGltaXQYBCABKAVIA4gBARITCgZvZmZzZXQYBSABKAVIBIgBAUIICgZfc3RhcnRCBgoEX2VuZEIJCgdfZmlsdGVyQggKBl9saW1pdEIJCgdfb2Zmc2V0Ij8KEEdldEl0ZW1zUmVzcG9uc2USHAoFaXRlbXMYASADKAsyDS5pdGVtLnYxLkl0ZW0SDQoFY291bnQYAiABKAMiMAoRQ3JlYXRlSXRlbVJlcXVlc3QSGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIxChJDcmVhdGVJdGVtUmVzcG9uc2USGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIwChFVcGRhdGVJdGVtUmVxdWVzdBIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIjEKElVwZGF0ZUl0ZW1SZXNwb25zZRIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIh8KEURlbGV0ZUl0ZW1SZXF1ZXN0EgoKAmlkGAEgASgDIhQKEkRlbGV0ZUl0ZW1SZXNwb25zZTLrAgoLSXRlbVNlcnZpY2USPgoHR2V0SXRlbRIXLml0ZW0udjEuR2V0SXRlbVJlcXVlc3QaGC5pdGVtLnYxLkdldEl0ZW1SZXNwb25zZSIAEkEKCEdldEl0ZW1zEhguaXRlbS52MS5HZXRJdGVtc1JlcXVlc3QaGS5pdGVtLnYxLkdldEl0ZW1zUmVzcG9uc2UiABJHCgpDcmVhdGVJdGVtEhouaXRlbS52MS5DcmVhdGVJdGVtUmVxdWVzdBobLml0ZW0udjEuQ3JlYXRlSXRlbVJlc3BvbnNlIgASRwoKVXBkYXRlSXRlbRIaLml0ZW0udjEuVXBkYXRlSXRlbVJlcXVlc3QaGy5pdGVtLnYxLlVwZGF0ZUl0ZW1SZXNwb25zZSIAEkcKCkRlbGV0ZUl0ZW0SGi5pdGVtLnYxLkRlbGV0ZUl0ZW1SZXF1ZXN0GhsuaXRlbS52MS5EZWxldGVJdGVtUmVzcG9uc2UiAEKdAQoLY29tLml0ZW0udjFCCUl0ZW1Qcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL2l0ZW0vdjE7aXRlbXYxogIDSVhYqgIHSXRlbS5WMcoCB0l0ZW1cVjHiAhNJdGVtXFYxXEdQQk1ldGFkYXRh6gIISXRlbTo6VjFiBnByb3RvMw", [file_google_protobuf_timestamp]); /** * @generated from message item.v1.Item */ export type Item = Message<"item.v1.Item"> & { /** - * @generated from field: optional uint32 id = 1; + * @generated from field: optional int64 id = 1; */ - id?: number; + id?: bigint; /** * @generated from field: string name = 2; @@ -39,7 +39,7 @@ export type Item = Message<"item.v1.Item"> & { price: number; /** - * @generated from field: uint32 quantity = 5; + * @generated from field: int32 quantity = 5; */ quantity: number; @@ -61,9 +61,9 @@ export const ItemSchema: GenMessage = /*@__PURE__*/ */ export type GetItemRequest = Message<"item.v1.GetItemRequest"> & { /** - * @generated from field: uint32 id = 1; + * @generated from field: int64 id = 1; */ - id: number; + id: bigint; }; /** @@ -110,12 +110,12 @@ export type GetItemsRequest = Message<"item.v1.GetItemsRequest"> & { filter?: string; /** - * @generated from field: optional uint32 limit = 4; + * @generated from field: optional int32 limit = 4; */ limit?: number; /** - * @generated from field: optional uint32 offset = 5; + * @generated from field: optional int32 offset = 5; */ offset?: number; }; @@ -137,7 +137,7 @@ export type GetItemsResponse = Message<"item.v1.GetItemsResponse"> & { items: Item[]; /** - * @generated from field: uint64 count = 2; + * @generated from field: int64 count = 2; */ count: bigint; }; @@ -222,9 +222,9 @@ export const UpdateItemResponseSchema: GenMessage = /*@__PUR */ export type DeleteItemRequest = Message<"item.v1.DeleteItemRequest"> & { /** - * @generated from field: uint32 id = 1; + * @generated from field: int64 id = 1; */ - id: number; + id: bigint; }; /** diff --git a/client/src/lib/services/user/v1/auth_pb.ts b/client/src/lib/services/user/v1/auth_pb.ts index be31bc4..c5ad0fd 100644 --- a/client/src/lib/services/user/v1/auth_pb.ts +++ b/client/src/lib/services/user/v1/auth_pb.ts @@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file user/v1/auth.proto. */ export const file_user_v1_auth: GenFile = /*@__PURE__*/ - fileDesc("ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UiKAoUR2V0UGFzc2tleUlEc1JlcXVlc3QSEAoIdXNlcm5hbWUYASABKAkiLAoVR2V0UGFzc2tleUlEc1Jlc3BvbnNlEhMKC3Bhc3NrZXlfaWRzGAEgAygJIhoKGEJlZ2luUGFzc2tleUxvZ2luUmVxdWVzdCIbChlCZWdpblBhc3NrZXlMb2dpblJlc3BvbnNlIhsKGUZpbmlzaFBhc3NrZXlMb2dpblJlcXVlc3QiHAoaRmluaXNoUGFzc2tleUxvZ2luUmVzcG9uc2Uy0gMKC0F1dGhTZXJ2aWNlEjgKBUxvZ2luEhUudXNlci52MS5Mb2dpblJlcXVlc3QaFi51c2VyLnYxLkxvZ2luUmVzcG9uc2UiABI7CgZTaWduVXASFi51c2VyLnYxLlNpZ25VcFJlcXVlc3QaFy51c2VyLnYxLlNpZ25VcFJlc3BvbnNlIgASOwoGTG9nb3V0EhYudXNlci52MS5Mb2dvdXRSZXF1ZXN0GhcudXNlci52MS5Mb2dvdXRSZXNwb25zZSIAElAKDUdldFBhc3NrZXlJRHMSHS51c2VyLnYxLkdldFBhc3NrZXlJRHNSZXF1ZXN0Gh4udXNlci52MS5HZXRQYXNza2V5SURzUmVzcG9uc2UiABJcChFCZWdpblBhc3NrZXlMb2dpbhIhLnVzZXIudjEuQmVnaW5QYXNza2V5TG9naW5SZXF1ZXN0GiIudXNlci52MS5CZWdpblBhc3NrZXlMb2dpblJlc3BvbnNlIgASXwoSRmluaXNoUGFzc2tleUxvZ2luEiIudXNlci52MS5GaW5pc2hQYXNza2V5TG9naW5SZXF1ZXN0GiMudXNlci52MS5GaW5pc2hQYXNza2V5TG9naW5SZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJQXV0aFByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z"); + fileDesc("ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UywQEKC0F1dGhTZXJ2aWNlEjgKBUxvZ2luEhUudXNlci52MS5Mb2dpblJlcXVlc3QaFi51c2VyLnYxLkxvZ2luUmVzcG9uc2UiABI7CgZTaWduVXASFi51c2VyLnYxLlNpZ25VcFJlcXVlc3QaFy51c2VyLnYxLlNpZ25VcFJlc3BvbnNlIgASOwoGTG9nb3V0EhYudXNlci52MS5Mb2dvdXRSZXF1ZXN0GhcudXNlci52MS5Mb2dvdXRSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJQXV0aFByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z"); /** * @generated from message user.v1.LoginRequest @@ -117,92 +117,6 @@ export type LogoutResponse = Message<"user.v1.LogoutResponse"> & { export const LogoutResponseSchema: GenMessage = /*@__PURE__*/ messageDesc(file_user_v1_auth, 5); -/** - * @generated from message user.v1.GetPasskeyIDsRequest - */ -export type GetPasskeyIDsRequest = Message<"user.v1.GetPasskeyIDsRequest"> & { - /** - * @generated from field: string username = 1; - */ - username: string; -}; - -/** - * Describes the message user.v1.GetPasskeyIDsRequest. - * Use `create(GetPasskeyIDsRequestSchema)` to create a new message. - */ -export const GetPasskeyIDsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_auth, 6); - -/** - * @generated from message user.v1.GetPasskeyIDsResponse - */ -export type GetPasskeyIDsResponse = Message<"user.v1.GetPasskeyIDsResponse"> & { - /** - * @generated from field: repeated string passkey_ids = 1; - */ - passkeyIds: string[]; -}; - -/** - * Describes the message user.v1.GetPasskeyIDsResponse. - * Use `create(GetPasskeyIDsResponseSchema)` to create a new message. - */ -export const GetPasskeyIDsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_auth, 7); - -/** - * @generated from message user.v1.BeginPasskeyLoginRequest - */ -export type BeginPasskeyLoginRequest = Message<"user.v1.BeginPasskeyLoginRequest"> & { -}; - -/** - * Describes the message user.v1.BeginPasskeyLoginRequest. - * Use `create(BeginPasskeyLoginRequestSchema)` to create a new message. - */ -export const BeginPasskeyLoginRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_auth, 8); - -/** - * @generated from message user.v1.BeginPasskeyLoginResponse - */ -export type BeginPasskeyLoginResponse = Message<"user.v1.BeginPasskeyLoginResponse"> & { -}; - -/** - * Describes the message user.v1.BeginPasskeyLoginResponse. - * Use `create(BeginPasskeyLoginResponseSchema)` to create a new message. - */ -export const BeginPasskeyLoginResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_auth, 9); - -/** - * @generated from message user.v1.FinishPasskeyLoginRequest - */ -export type FinishPasskeyLoginRequest = Message<"user.v1.FinishPasskeyLoginRequest"> & { -}; - -/** - * Describes the message user.v1.FinishPasskeyLoginRequest. - * Use `create(FinishPasskeyLoginRequestSchema)` to create a new message. - */ -export const FinishPasskeyLoginRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_auth, 10); - -/** - * @generated from message user.v1.FinishPasskeyLoginResponse - */ -export type FinishPasskeyLoginResponse = Message<"user.v1.FinishPasskeyLoginResponse"> & { -}; - -/** - * Describes the message user.v1.FinishPasskeyLoginResponse. - * Use `create(FinishPasskeyLoginResponseSchema)` to create a new message. - */ -export const FinishPasskeyLoginResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_auth, 11); - /** * @generated from service user.v1.AuthService */ @@ -231,30 +145,6 @@ export const AuthService: GenService<{ input: typeof LogoutRequestSchema; output: typeof LogoutResponseSchema; }, - /** - * @generated from rpc user.v1.AuthService.GetPasskeyIDs - */ - getPasskeyIDs: { - methodKind: "unary"; - input: typeof GetPasskeyIDsRequestSchema; - output: typeof GetPasskeyIDsResponseSchema; - }, - /** - * @generated from rpc user.v1.AuthService.BeginPasskeyLogin - */ - beginPasskeyLogin: { - methodKind: "unary"; - input: typeof BeginPasskeyLoginRequestSchema; - output: typeof BeginPasskeyLoginResponseSchema; - }, - /** - * @generated from rpc user.v1.AuthService.FinishPasskeyLogin - */ - finishPasskeyLogin: { - methodKind: "unary"; - input: typeof FinishPasskeyLoginRequestSchema; - output: typeof FinishPasskeyLoginResponseSchema; - }, }> = /*@__PURE__*/ serviceDesc(file_user_v1_auth, 0); diff --git a/client/src/lib/services/user/v1/user_pb.ts b/client/src/lib/services/user/v1/user_pb.ts index 956c7ae..289401a 100644 --- a/client/src/lib/services/user/v1/user_pb.ts +++ b/client/src/lib/services/user/v1/user_pb.ts @@ -10,16 +10,16 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file user/v1/user.proto. */ export const file_user_v1_user: GenFile = /*@__PURE__*/ - fileDesc("ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiVgoEVXNlchIKCgJpZBgBIAEoDRIQCgh1c2VybmFtZRgCIAEoCRIcCg9wcm9maWxlX3BpY3R1cmUYAyABKAlIAIgBAUISChBfcHJvZmlsZV9waWN0dXJlIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlciIhCh9CZWdpblBhc3NrZXlSZWdpc3RyYXRpb25SZXF1ZXN0IiIKIEJlZ2luUGFzc2tleVJlZ2lzdHJhdGlvblJlc3BvbnNlIiIKIEZpbmlzaFBhc3NrZXlSZWdpc3RyYXRpb25SZXF1ZXN0IiMKIUZpbmlzaFBhc3NrZXlSZWdpc3RyYXRpb25SZXNwb25zZTK4BAoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAEnEKGEJlZ2luUGFzc2tleVJlZ2lzdHJhdGlvbhIoLnVzZXIudjEuQmVnaW5QYXNza2V5UmVnaXN0cmF0aW9uUmVxdWVzdBopLnVzZXIudjEuQmVnaW5QYXNza2V5UmVnaXN0cmF0aW9uUmVzcG9uc2UiABJ0ChlGaW5pc2hQYXNza2V5UmVnaXN0cmF0aW9uEikudXNlci52MS5GaW5pc2hQYXNza2V5UmVnaXN0cmF0aW9uUmVxdWVzdBoqLnVzZXIudjEuRmluaXNoUGFzc2tleVJlZ2lzdHJhdGlvblJlc3BvbnNlIgBCnQEKC2NvbS51c2VyLnYxQglVc2VyUHJvdG9QAVpGZ2l0aHViLmNvbS9zcG90ZGVtbzQvdHJldnN0YWNrL3NlcnZlci9pbnRlcm5hbC9zZXJ2aWNlcy91c2VyL3YxO3VzZXJ2MaICA1VYWKoCB1VzZXIuVjHKAgdVc2VyXFYx4gITVXNlclxWMVxHUEJNZXRhZGF0YeoCCFVzZXI6OlYxYgZwcm90bzM"); + fileDesc("ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiXAoEVXNlchIKCgJpZBgBIAEoAxIQCgh1c2VybmFtZRgCIAEoCRIfChJwcm9maWxlX3BpY3R1cmVfaWQYAyABKANIAIgBAUIVChNfcHJvZmlsZV9waWN0dXJlX2lkIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlcjLPAgoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJVXNlclByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z"); /** * @generated from message user.v1.User */ export type User = Message<"user.v1.User"> & { /** - * @generated from field: uint32 id = 1; + * @generated from field: int64 id = 1; */ - id: number; + id: bigint; /** * @generated from field: string username = 2; @@ -27,9 +27,9 @@ export type User = Message<"user.v1.User"> & { username: string; /** - * @generated from field: optional string profile_picture = 3; + * @generated from field: optional int64 profile_picture_id = 3; */ - profilePicture?: string; + profilePictureId?: bigint; }; /** @@ -191,58 +191,6 @@ export type UpdateProfilePictureResponse = Message<"user.v1.UpdateProfilePicture export const UpdateProfilePictureResponseSchema: GenMessage = /*@__PURE__*/ messageDesc(file_user_v1_user, 8); -/** - * @generated from message user.v1.BeginPasskeyRegistrationRequest - */ -export type BeginPasskeyRegistrationRequest = Message<"user.v1.BeginPasskeyRegistrationRequest"> & { -}; - -/** - * Describes the message user.v1.BeginPasskeyRegistrationRequest. - * Use `create(BeginPasskeyRegistrationRequestSchema)` to create a new message. - */ -export const BeginPasskeyRegistrationRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_user, 9); - -/** - * @generated from message user.v1.BeginPasskeyRegistrationResponse - */ -export type BeginPasskeyRegistrationResponse = Message<"user.v1.BeginPasskeyRegistrationResponse"> & { -}; - -/** - * Describes the message user.v1.BeginPasskeyRegistrationResponse. - * Use `create(BeginPasskeyRegistrationResponseSchema)` to create a new message. - */ -export const BeginPasskeyRegistrationResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_user, 10); - -/** - * @generated from message user.v1.FinishPasskeyRegistrationRequest - */ -export type FinishPasskeyRegistrationRequest = Message<"user.v1.FinishPasskeyRegistrationRequest"> & { -}; - -/** - * Describes the message user.v1.FinishPasskeyRegistrationRequest. - * Use `create(FinishPasskeyRegistrationRequestSchema)` to create a new message. - */ -export const FinishPasskeyRegistrationRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_user, 11); - -/** - * @generated from message user.v1.FinishPasskeyRegistrationResponse - */ -export type FinishPasskeyRegistrationResponse = Message<"user.v1.FinishPasskeyRegistrationResponse"> & { -}; - -/** - * Describes the message user.v1.FinishPasskeyRegistrationResponse. - * Use `create(FinishPasskeyRegistrationResponseSchema)` to create a new message. - */ -export const FinishPasskeyRegistrationResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_user_v1_user, 12); - /** * @generated from service user.v1.UserService */ @@ -279,22 +227,6 @@ export const UserService: GenService<{ input: typeof UpdateProfilePictureRequestSchema; output: typeof UpdateProfilePictureResponseSchema; }, - /** - * @generated from rpc user.v1.UserService.BeginPasskeyRegistration - */ - beginPasskeyRegistration: { - methodKind: "unary"; - input: typeof BeginPasskeyRegistrationRequestSchema; - output: typeof BeginPasskeyRegistrationResponseSchema; - }, - /** - * @generated from rpc user.v1.UserService.FinishPasskeyRegistration - */ - finishPasskeyRegistration: { - methodKind: "unary"; - input: typeof FinishPasskeyRegistrationRequestSchema; - output: typeof FinishPasskeyRegistrationResponseSchema; - }, }> = /*@__PURE__*/ serviceDesc(file_user_v1_user, 0); diff --git a/client/src/lib/ui/Avatar.svelte b/client/src/lib/ui/Avatar.svelte index 6154e42..b489041 100644 --- a/client/src/lib/ui/Avatar.svelte +++ b/client/src/lib/ui/Avatar.svelte @@ -5,7 +5,7 @@ diff --git a/client/src/lib/ui/Button.svelte b/client/src/lib/ui/Button.svelte index 5df9006..b97b824 100644 --- a/client/src/lib/ui/Button.svelte +++ b/client/src/lib/ui/Button.svelte @@ -1,9 +1,10 @@ @@ -23,8 +24,8 @@ 'bg-sky text-crust focus:outline-sky flex w-fit cursor-pointer items-center justify-center rounded p-2 px-4 text-sm font-medium transition-all hover:brightness-120 focus:outline-2 focus:outline-offset-1', className )} - onclick={() => { - onclick?.(); + onclick={(e: me) => { + onclick?.(e); }} > {@render children?.()} diff --git a/client/src/lib/ui/Pagination.svelte b/client/src/lib/ui/Pagination.svelte index ee4f192..2c423d0 100644 --- a/client/src/lib/ui/Pagination.svelte +++ b/client/src/lib/ui/Pagination.svelte @@ -3,7 +3,7 @@ import { ChevronLeft, ChevronRight } from '@lucide/svelte'; import { Pagination } from 'bits-ui'; import { pushState, replaceState } from '$app/navigation'; - import { onMount } from 'svelte'; + import { onMount, tick } from 'svelte'; let { count = $bindable(), @@ -21,7 +21,8 @@ let page: number = $state(1); - onMount(() => { + onMount(async() => { + await tick(); replaceState('', `${page}`); }); diff --git a/client/src/routes/(app)/items/+page.svelte b/client/src/routes/(app)/items/+page.svelte index 4dc9fd5..3edf13e 100644 --- a/client/src/routes/(app)/items/+page.svelte +++ b/client/src/routes/(app)/items/+page.svelte @@ -26,8 +26,8 @@ // Open let addedOpen = $state(false); - let deletesOpen: SvelteMap = new SvelteMap(); - let editsOpen: SvelteMap = new SvelteMap(); + let deletesOpen: SvelteMap = new SvelteMap(); + let editsOpen: SvelteMap = new SvelteMap(); async function getItems() { return await ItemClient.getItems({ diff --git a/client/src/routes/(app)/settings/+page.svelte b/client/src/routes/(app)/settings/+page.svelte index c0639aa..540e586 100644 --- a/client/src/routes/(app)/settings/+page.svelte +++ b/client/src/routes/(app)/settings/+page.svelte @@ -140,8 +140,7 @@ className="bg-text" onclick={async () => { if (userState.user) { - await createPasskey(userState.user.username, userState.user.id, "what"); - + //await createPasskey(userState.user.username, userState.user.id, "what"); } }}>Register Device diff --git a/client/static/openapi/openapi.yaml b/client/static/openapi/openapi.yaml index 16b06f3..5667407 100644 --- a/client/static/openapi/openapi.yaml +++ b/client/static/openapi/openapi.yaml @@ -128,8 +128,11 @@ components: type: object properties: id: - type: integer + type: + - integer + - string title: id + format: int64 title: DeleteItemRequest additionalProperties: false item.v1.DeleteItemResponse: @@ -140,8 +143,11 @@ components: type: object properties: id: - type: integer + type: + - integer + - string title: id + format: int64 title: GetItemRequest additionalProperties: false item.v1.GetItemResponse: @@ -170,10 +176,12 @@ components: limit: type: integer title: limit + format: int32 nullable: true offset: type: integer title: offset + format: int32 nullable: true title: GetItemsRequest additionalProperties: false @@ -197,8 +205,11 @@ components: type: object properties: id: - type: integer + type: + - integer + - string title: id + format: int64 nullable: true name: type: string @@ -213,6 +224,7 @@ components: quantity: type: integer title: quantity + format: int32 added: title: added nullable: true @@ -292,40 +304,6 @@ components: additionalProperties: true additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. - user.v1.BeginPasskeyLoginRequest: - type: object - title: BeginPasskeyLoginRequest - additionalProperties: false - user.v1.BeginPasskeyLoginResponse: - type: object - title: BeginPasskeyLoginResponse - additionalProperties: false - user.v1.FinishPasskeyLoginRequest: - type: object - title: FinishPasskeyLoginRequest - additionalProperties: false - user.v1.FinishPasskeyLoginResponse: - type: object - title: FinishPasskeyLoginResponse - additionalProperties: false - user.v1.GetPasskeyIDsRequest: - type: object - properties: - username: - type: string - title: username - title: GetPasskeyIDsRequest - additionalProperties: false - user.v1.GetPasskeyIDsResponse: - type: object - properties: - passkeyIds: - type: array - items: - type: string - title: passkey_ids - title: GetPasskeyIDsResponse - additionalProperties: false user.v1.LoginRequest: type: object properties: @@ -371,22 +349,6 @@ components: type: object title: SignUpResponse additionalProperties: false - user.v1.BeginPasskeyRegistrationRequest: - type: object - title: BeginPasskeyRegistrationRequest - additionalProperties: false - user.v1.BeginPasskeyRegistrationResponse: - type: object - title: BeginPasskeyRegistrationResponse - additionalProperties: false - user.v1.FinishPasskeyRegistrationRequest: - type: object - title: FinishPasskeyRegistrationRequest - additionalProperties: false - user.v1.FinishPasskeyRegistrationResponse: - type: object - title: FinishPasskeyRegistrationResponse - additionalProperties: false user.v1.GetAPIKeyRequest: type: object properties: @@ -464,14 +426,20 @@ components: type: object properties: id: - type: integer + type: + - integer + - string title: id + format: int64 username: type: string title: username - profilePicture: - type: string - title: profile_picture + profilePictureId: + type: + - integer + - string + title: profile_picture_id + format: int64 nullable: true title: User additionalProperties: false @@ -758,111 +726,6 @@ paths: application/json: schema: $ref: '#/components/schemas/user.v1.LogoutResponse' - /user.v1.AuthService/GetPasskeyIDs: - post: - tags: - - user.v1.AuthService - summary: GetPasskeyIDs - operationId: user.v1.AuthService.GetPasskeyIDs - parameters: - - name: Connect-Protocol-Version - in: header - required: true - schema: - $ref: '#/components/schemas/connect-protocol-version' - - name: Connect-Timeout-Ms - in: header - schema: - $ref: '#/components/schemas/connect-timeout-header' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.GetPasskeyIDsRequest' - required: true - responses: - default: - description: Error - content: - application/json: - schema: - $ref: '#/components/schemas/connect.error' - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.GetPasskeyIDsResponse' - /user.v1.AuthService/BeginPasskeyLogin: - post: - tags: - - user.v1.AuthService - summary: BeginPasskeyLogin - operationId: user.v1.AuthService.BeginPasskeyLogin - parameters: - - name: Connect-Protocol-Version - in: header - required: true - schema: - $ref: '#/components/schemas/connect-protocol-version' - - name: Connect-Timeout-Ms - in: header - schema: - $ref: '#/components/schemas/connect-timeout-header' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.BeginPasskeyLoginRequest' - required: true - responses: - default: - description: Error - content: - application/json: - schema: - $ref: '#/components/schemas/connect.error' - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.BeginPasskeyLoginResponse' - /user.v1.AuthService/FinishPasskeyLogin: - post: - tags: - - user.v1.AuthService - summary: FinishPasskeyLogin - operationId: user.v1.AuthService.FinishPasskeyLogin - parameters: - - name: Connect-Protocol-Version - in: header - required: true - schema: - $ref: '#/components/schemas/connect-protocol-version' - - name: Connect-Timeout-Ms - in: header - schema: - $ref: '#/components/schemas/connect-timeout-header' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.FinishPasskeyLoginRequest' - required: true - responses: - default: - description: Error - content: - application/json: - schema: - $ref: '#/components/schemas/connect.error' - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.FinishPasskeyLoginResponse' /user.v1.UserService/GetUser: post: tags: @@ -1003,76 +866,6 @@ paths: application/json: schema: $ref: '#/components/schemas/user.v1.UpdateProfilePictureResponse' - /user.v1.UserService/BeginPasskeyRegistration: - post: - tags: - - user.v1.UserService - summary: BeginPasskeyRegistration - operationId: user.v1.UserService.BeginPasskeyRegistration - parameters: - - name: Connect-Protocol-Version - in: header - required: true - schema: - $ref: '#/components/schemas/connect-protocol-version' - - name: Connect-Timeout-Ms - in: header - schema: - $ref: '#/components/schemas/connect-timeout-header' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.BeginPasskeyRegistrationRequest' - required: true - responses: - default: - description: Error - content: - application/json: - schema: - $ref: '#/components/schemas/connect.error' - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.BeginPasskeyRegistrationResponse' - /user.v1.UserService/FinishPasskeyRegistration: - post: - tags: - - user.v1.UserService - summary: FinishPasskeyRegistration - operationId: user.v1.UserService.FinishPasskeyRegistration - parameters: - - name: Connect-Protocol-Version - in: header - required: true - schema: - $ref: '#/components/schemas/connect-protocol-version' - - name: Connect-Timeout-Ms - in: header - schema: - $ref: '#/components/schemas/connect-timeout-header' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.FinishPasskeyRegistrationRequest' - required: true - responses: - default: - description: Error - content: - application/json: - schema: - $ref: '#/components/schemas/connect.error' - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/user.v1.FinishPasskeyRegistrationResponse' tags: - name: item.v1.ItemService - name: user.v1.AuthService diff --git a/client/vite.config.ts b/client/vite.config.ts index afd1aec..7435f32 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -16,5 +16,5 @@ export default defineConfig({ } }, host: '0.0.0.0' - } + }, }); diff --git a/flake.lock b/flake.lock index eabc3b4..71067db 100644 --- a/flake.lock +++ b/flake.lock @@ -18,6 +18,24 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -38,6 +56,27 @@ "type": "github" } }, + "gitignore_2": { + "inputs": { + "nixpkgs": [ + "treli", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1741513245, @@ -54,11 +93,28 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1743583204, + "narHash": "sha256-F7n4+KOIfWrwoQjXrL2wD9RhFYLs2/GGe/MQY1sSdlE=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "2c8d3f48d33929642c1c12cd243df4cc7d2ce434", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", "gitignore": "gitignore", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "treli": "treli" } }, "systems": { @@ -75,6 +131,41 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treli": { + "inputs": { + "flake-utils": "flake-utils_2", + "gitignore": "gitignore_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1744260822, + "narHash": "sha256-PMrIBuIM12tlG9hkoMHtFSDPfaXEXKziom/XZyFWXEo=", + "owner": "spotdemo4", + "repo": "treli", + "rev": "41ecacdfc1e720ac96d1d02200b8541314dd09ea", + "type": "github" + }, + "original": { + "owner": "spotdemo4", + "repo": "treli", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index ff45d76..cfafc8d 100644 --- a/flake.nix +++ b/flake.nix @@ -4,13 +4,14 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + treli.url = "github:spotdemo4/treli"; gitignore = { url = "github:hercules-ci/gitignore.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nixpkgs, flake-utils, gitignore }: + outputs = { self, nixpkgs, flake-utils, gitignore, treli }: flake-utils.lib.eachDefaultSystem (system: let @@ -80,12 +81,12 @@ packages = with pkgs; [ git nix-update + treli.packages."${system}".default # Go backend go gotools gopls - air revive bobgen @@ -95,11 +96,11 @@ protoc-gen-connect-go protoc-gen-es protoc-gen-connect-openapi - inotify-tools # Svelte frontend nodejs_22 + # Update (writeShellApplication { name = "ts-update"; @@ -132,6 +133,7 @@ ''; }) + # Bump version (writeShellApplication { name = "ts-bump"; @@ -155,6 +157,7 @@ ''; }) + # Lint (writeShellApplication { name = "ts-lint"; @@ -177,10 +180,11 @@ cd "''${git_root}/server" echo "Linting server" - revive -config revive.toml -formatter friendly ./... + revive -config revive.toml -set_exit_status ./... ''; }) + # Build (writeShellApplication { name = "ts-build"; diff --git a/proto/item/v1/item.proto b/proto/item/v1/item.proto index ab407e8..dbbbd0a 100644 --- a/proto/item/v1/item.proto +++ b/proto/item/v1/item.proto @@ -5,11 +5,11 @@ package item.v1; import "google/protobuf/timestamp.proto"; message Item { - optional uint32 id = 1; + optional int64 id = 1; string name = 2; string description = 3; float price = 4; - uint32 quantity = 5; + int32 quantity = 5; optional google.protobuf.Timestamp added = 6; } @@ -22,7 +22,7 @@ service ItemService { } message GetItemRequest { - uint32 id = 1; + int64 id = 1; } message GetItemResponse { Item item = 1; @@ -32,15 +32,15 @@ message GetItemsRequest { optional google.protobuf.Timestamp start = 1; optional google.protobuf.Timestamp end = 2; optional string filter = 3; - optional uint32 limit = 4; - optional uint32 offset = 5; + optional int32 limit = 4; + optional int32 offset = 5; } message GetItemsResponse { repeated Item items = 1; - uint64 count = 2; + int64 count = 2; } -message CreateItemRequest { +message CreateItemRequest { Item item = 1; } message CreateItemResponse { @@ -55,6 +55,6 @@ message UpdateItemResponse { } message DeleteItemRequest { - uint32 id = 1; + int64 id = 1; } message DeleteItemResponse {} \ No newline at end of file diff --git a/proto/user/v1/auth.proto b/proto/user/v1/auth.proto index bdd024d..e4a249e 100644 --- a/proto/user/v1/auth.proto +++ b/proto/user/v1/auth.proto @@ -6,9 +6,9 @@ service AuthService { rpc Login (LoginRequest) returns (LoginResponse) {} rpc SignUp (SignUpRequest) returns (SignUpResponse) {} rpc Logout (LogoutRequest) returns (LogoutResponse) {} - rpc GetPasskeyIDs (GetPasskeyIDsRequest) returns (GetPasskeyIDsResponse) {} - rpc BeginPasskeyLogin (BeginPasskeyLoginRequest) returns (BeginPasskeyLoginResponse) {} - rpc FinishPasskeyLogin (FinishPasskeyLoginRequest) returns (FinishPasskeyLoginResponse) {} + // rpc GetPasskeyIDs (GetPasskeyIDsRequest) returns (GetPasskeyIDsResponse) {} + // rpc BeginPasskeyLogin (BeginPasskeyLoginRequest) returns (BeginPasskeyLoginResponse) {} + // rpc FinishPasskeyLogin (FinishPasskeyLoginRequest) returns (FinishPasskeyLoginResponse) {} } message LoginRequest { @@ -29,15 +29,15 @@ message SignUpResponse {} message LogoutRequest {} message LogoutResponse {} -message GetPasskeyIDsRequest { - string username = 1; -} -message GetPasskeyIDsResponse { - repeated string passkey_ids = 1; -} +// message GetPasskeyIDsRequest { +// string username = 1; +// } +// message GetPasskeyIDsResponse { +// repeated string passkey_ids = 1; +// } -message BeginPasskeyLoginRequest {} -message BeginPasskeyLoginResponse {} +// message BeginPasskeyLoginRequest {} +// message BeginPasskeyLoginResponse {} -message FinishPasskeyLoginRequest {} -message FinishPasskeyLoginResponse {} \ No newline at end of file +// message FinishPasskeyLoginRequest {} +// message FinishPasskeyLoginResponse {} \ No newline at end of file diff --git a/proto/user/v1/user.proto b/proto/user/v1/user.proto index 85ec6aa..3d41da1 100644 --- a/proto/user/v1/user.proto +++ b/proto/user/v1/user.proto @@ -3,9 +3,9 @@ syntax = "proto3"; package user.v1; message User { - uint32 id = 1; + int64 id = 1; string username = 2; - optional string profile_picture = 3; + optional int64 profile_picture_id = 3; } service UserService { @@ -13,8 +13,8 @@ service UserService { rpc UpdatePassword (UpdatePasswordRequest) returns (UpdatePasswordResponse) {} rpc GetAPIKey (GetAPIKeyRequest) returns (GetAPIKeyResponse) {} rpc UpdateProfilePicture (UpdateProfilePictureRequest) returns (UpdateProfilePictureResponse) {} - rpc BeginPasskeyRegistration (BeginPasskeyRegistrationRequest) returns (BeginPasskeyRegistrationResponse) {} - rpc FinishPasskeyRegistration (FinishPasskeyRegistrationRequest) returns (FinishPasskeyRegistrationResponse) {} + // rpc BeginPasskeyRegistration (BeginPasskeyRegistrationRequest) returns (BeginPasskeyRegistrationResponse) {} + // rpc FinishPasskeyRegistration (FinishPasskeyRegistrationRequest) returns (FinishPasskeyRegistrationResponse) {} } message GetUserRequest {} @@ -47,8 +47,8 @@ message UpdateProfilePictureResponse { User user = 1; } -message BeginPasskeyRegistrationRequest {} -message BeginPasskeyRegistrationResponse {} +// message BeginPasskeyRegistrationRequest {} +// message BeginPasskeyRegistrationResponse {} -message FinishPasskeyRegistrationRequest {} -message FinishPasskeyRegistrationResponse {} \ No newline at end of file +// message FinishPasskeyRegistrationRequest {} +// message FinishPasskeyRegistrationResponse {} \ No newline at end of file diff --git a/server/bobgen.yaml b/server/bobgen.yaml index 2fe4ae2..2a68186 100644 --- a/server/bobgen.yaml +++ b/server/bobgen.yaml @@ -1,5 +1,15 @@ wipe: true +replacements: + - match: + db_type: "INTEGER" + replace: "int64" + + - match: + db_type: "INTEGER" + nullable: true + replace: "int64" + sqlite: output: internal/models diff --git a/server/internal/database/logger.go b/server/internal/database/logger.go deleted file mode 100644 index 0a3e618..0000000 --- a/server/internal/database/logger.go +++ /dev/null @@ -1,22 +0,0 @@ -package database - -import ( - "log" - "os" - "time" - - "gorm.io/gorm/logger" -) - -func NewLogger() logger.Interface { - return logger.New( - log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer - logger.Config{ - SlowThreshold: time.Second, // Slow SQL threshold - LogLevel: logger.Silent, // Log level - IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger - ParameterizedQueries: true, // Don't include params in the SQL log - Colorful: true, // Disable color - }, - ) -} diff --git a/server/internal/database/migrate.go b/server/internal/database/migrate.go deleted file mode 100644 index e62ae0d..0000000 --- a/server/internal/database/migrate.go +++ /dev/null @@ -1,15 +0,0 @@ -package database - -import ( - "github.com/spotdemo4/trevstack/server/internal/models" - "gorm.io/gorm" -) - -func Migrate(db *gorm.DB) error { - err := db.AutoMigrate(&models.User{}, &models.Item{}, &models.File{}, &models.Passkey{}) - if err != nil { - return err - } - - return nil -} diff --git a/server/internal/database/postgres.go b/server/internal/database/postgres.go index 055689d..c78eef9 100644 --- a/server/internal/database/postgres.go +++ b/server/internal/database/postgres.go @@ -1,18 +1,20 @@ package database import ( - "gorm.io/driver/postgres" - "gorm.io/gorm" + "database/sql" + + _ "github.com/lib/pq" // Postgres + "github.com/stephenafamo/bob" ) -func NewPostgresConnection(user, pass, host, port, name string) (*gorm.DB, error) { +func NewPostgresConnection(user, pass, host, port, name string) (*bob.DB, error) { dsn := "host=" + host + " user=" + user + " password=" + pass + " dbname=" + name + " port=" + port + " sslmode=disable TimeZone=UTC" - db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ - Logger: NewLogger(), - }) + db, err := sql.Open("postgres", dsn) if err != nil { return nil, err } - return db, nil + bobdb := bob.NewDB(db) + + return &bobdb, nil } diff --git a/server/internal/database/sqlite.go b/server/internal/database/sqlite.go index 193dc8c..fb2fa09 100644 --- a/server/internal/database/sqlite.go +++ b/server/internal/database/sqlite.go @@ -1,14 +1,15 @@ package database import ( + "database/sql" "os" "path/filepath" - "github.com/glebarez/sqlite" - "gorm.io/gorm" + "github.com/stephenafamo/bob" + _ "modernc.org/sqlite" // Sqlite ) -func NewSQLiteConnection(name string) (*gorm.DB, error) { +func NewSQLiteConnection(name string) (*bob.DB, error) { // Find config diretory configDir, err := os.UserConfigDir() if err != nil { @@ -24,12 +25,13 @@ func NewSQLiteConnection(name string) (*gorm.DB, error) { // Open database dbPath := filepath.Join(settingsPath, name) - db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ - Logger: NewLogger(), - }) + db, err := sql.Open("sqlite", dbPath) if err != nil { return nil, err } - return db, nil + // Create new bob db + bobdb := bob.NewDB(db) + + return &bobdb, nil } diff --git a/server/internal/handlers/file.go b/server/internal/handlers/file/file.go similarity index 52% rename from server/internal/handlers/file.go rename to server/internal/handlers/file/file.go index b048082..e4b8acd 100644 --- a/server/internal/handlers/file.go +++ b/server/internal/handlers/file/file.go @@ -1,18 +1,21 @@ -package handlers +package file import ( + "context" + "database/sql" "errors" - "log" "net/http" + "strconv" "strings" "github.com/spotdemo4/trevstack/server/internal/interceptors" "github.com/spotdemo4/trevstack/server/internal/models" - "gorm.io/gorm" + "github.com/stephenafamo/bob" + "github.com/stephenafamo/bob/dialect/sqlite" ) type FileHandler struct { - db *gorm.DB + db *bob.DB key []byte } @@ -23,37 +26,45 @@ func (h *FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + // Make sure this is a GET request + if r.Method != http.MethodGet { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + // Get the file id from the path pathItems := strings.Split(r.URL.Path, "/") if len(pathItems) < 3 { http.Redirect(w, r, "/auth", http.StatusFound) return } - id := pathItems[2] - - // Get the file from the database - file := models.File{} - if err := h.db.First(&file, "id = ? AND user_id = ?", id, userid).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - http.Error(w, "File not found", http.StatusNotFound) - return - } - - log.Println(err) + id, err := strconv.Atoi(pathItems[2]) + if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } - // Serve the file - if r.Method == http.MethodGet { - w.Header().Set("Content-Type", http.DetectContentType(file.Data)) - w.Write(file.Data) - } else { - http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + // Get the file from the database + file, err := models.Files.Query( + sqlite.WhereAnd( + models.SelectWhere.Files.ID.EQ(int64(id)), + models.SelectWhere.Files.UserID.EQ(userid), + ), + ).One(context.Background(), h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + http.Error(w, "Not Found", http.StatusNotFound) + } + + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return } + + w.Header().Set("Content-Type", http.DetectContentType(file.Data)) + w.Write(file.Data) } -func NewFileHandler(db *gorm.DB, key string) http.Handler { +func NewFileHandler(db *bob.DB, key string) http.Handler { return interceptors.WithAuthRedirect( &FileHandler{ db: db, diff --git a/server/internal/handlers/item/v1/item.go b/server/internal/handlers/item/v1/item.go index d9338e8..9cdce3d 100644 --- a/server/internal/handlers/item/v1/item.go +++ b/server/internal/handlers/item/v1/item.go @@ -2,21 +2,39 @@ package item import ( "context" + "database/sql" "errors" - "fmt" "net/http" "time" "connectrpc.com/connect" + "github.com/aarondl/opt/omit" + "github.com/aarondl/opt/omitnull" "github.com/spotdemo4/trevstack/server/internal/interceptors" "github.com/spotdemo4/trevstack/server/internal/models" itemv1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1" "github.com/spotdemo4/trevstack/server/internal/services/item/v1/itemv1connect" - "gorm.io/gorm" + "github.com/stephenafamo/bob" + "github.com/stephenafamo/bob/dialect/sqlite" + "github.com/stephenafamo/bob/dialect/sqlite/sm" + "google.golang.org/protobuf/types/known/timestamppb" ) +func itemToConnect(item *models.Item) *itemv1.Item { + timestamp := timestamppb.New(item.Added) + + return &itemv1.Item{ + Id: &item.ID, + Name: item.Name, + Description: item.Description.GetOrZero(), + Price: item.Price.GetOrZero(), + Quantity: int32(item.Quantity.GetOrZero()), + Added: timestamp, + } +} + type Handler struct { - db *gorm.DB + db *bob.DB key []byte } @@ -27,13 +45,22 @@ func (h *Handler) GetItem(ctx context.Context, req *connect.Request[itemv1.GetIt } // Get item - item := models.Item{} - if err := h.db.First(&item, "id = ? AND user_id = ?", req.Msg.Id, userid).Error; err != nil { - return nil, connect.NewError(connect.CodeNotFound, err) + item, err := models.Items.Query( + sqlite.WhereAnd( + models.SelectWhere.Items.ID.EQ(req.Msg.Id), + models.SelectWhere.Items.UserID.EQ(userid), + ), + ).One(ctx, h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, err) + } + + return nil, connect.NewError(connect.CodeInternal, err) } res := connect.NewResponse(&itemv1.GetItemResponse{ - Item: item.ToConnectV1(), + Item: itemToConnect(item), }) return res, nil } @@ -45,45 +72,55 @@ func (h *Handler) GetItems(ctx context.Context, req *connect.Request[itemv1.GetI } // Filters - sql := h.db.Where("user_id = ?", userid) + query := models.Items.Query(models.SelectWhere.Items.UserID.EQ(userid)) + countQuery := models.Items.Query(models.SelectWhere.Items.UserID.EQ(userid)) + + // Counted filters if req.Msg.Start != nil { - sql = sql.Where("added >= ?", req.Msg.Start.AsTime()) + query.Apply(models.SelectWhere.Items.Added.GTE(req.Msg.Start.AsTime())) + countQuery.Apply(models.SelectWhere.Items.Added.GTE(req.Msg.Start.AsTime())) } if req.Msg.End != nil { - sql = sql.Where("added <= ?", req.Msg.End.AsTime()) + query.Apply(models.SelectWhere.Items.Added.LTE(req.Msg.End.AsTime())) + countQuery.Apply(models.SelectWhere.Items.Added.LTE(req.Msg.End.AsTime())) } - if req.Msg.Filter != nil { - sql = sql.Where("name LIKE ?", fmt.Sprintf("%%%s%%", *req.Msg.Filter)) + if req.Msg.Filter != nil && *req.Msg.Filter != "" { + query.Apply(models.SelectWhere.Items.Name.Like("%" + *req.Msg.Filter + "%")) + countQuery.Apply(models.SelectWhere.Items.Name.Like(*req.Msg.Filter)) } // Uncounted filters - sqlu := sql.Session(&gorm.Session{}) if req.Msg.Limit != nil { - sqlu = sqlu.Limit(int(*req.Msg.Limit)) + query.Apply(sm.Limit(*req.Msg.Limit)) } if req.Msg.Offset != nil { - sqlu = sqlu.Offset(int(*req.Msg.Offset)) + query.Apply(sm.Offset(*req.Msg.Offset)) } // Get items & count - items := []models.Item{} - var count int64 - if err := sqlu.Order("added desc").Find(&items).Error; err != nil { - return nil, connect.NewError(connect.CodeNotFound, err) + items, err := query.All(ctx, h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, err) + } + + return nil, connect.NewError(connect.CodeInternal, err) } - if err := sql.Model(&items).Count(&count).Error; err != nil { + + count, err := query.Count(ctx, h.db) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } // Convert to connect v1 items resItems := []*itemv1.Item{} for _, item := range items { - resItems = append(resItems, item.ToConnectV1()) + resItems = append(resItems, itemToConnect(item)) } res := connect.NewResponse(&itemv1.GetItemsResponse{ Items: resItems, - Count: uint64(count), + Count: count, }) return res, nil } @@ -94,21 +131,20 @@ func (h *Handler) CreateItem(ctx context.Context, req *connect.Request[itemv1.Cr return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated")) } - // Create item - item := models.Item{ - Name: req.Msg.Item.Name, - Description: req.Msg.Item.Description, - Price: req.Msg.Item.Price, - Quantity: int(req.Msg.Item.Quantity), - Added: time.Now(), - UserID: uint(userid), - } - if err := h.db.Create(&item).Error; err != nil { + item, err := models.Items.Insert(&models.ItemSetter{ + Name: omit.From(req.Msg.Item.Name), + Description: omitnull.From(req.Msg.Item.Description), + Price: omitnull.From(req.Msg.Item.Price), + Quantity: omitnull.From(int64(req.Msg.Item.Quantity)), + Added: omit.From(time.Now()), + UserID: omit.From(userid), + }).One(ctx, h.db) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } res := connect.NewResponse(&itemv1.CreateItemResponse{ - Item: item.ToConnectV1(), + Item: itemToConnect(item), }) return res, nil } @@ -125,20 +161,27 @@ func (h *Handler) UpdateItem(ctx context.Context, req *connect.Request[itemv1.Up } // Update item - item := models.Item{ - ID: *req.Msg.Item.Id, - Name: req.Msg.Item.Name, - Description: req.Msg.Item.Description, - Price: req.Msg.Item.Price, - Quantity: int(req.Msg.Item.Quantity), - UserID: uint(userid), - } - if err := h.db.Where("id = ? AND user_id = ?", req.Msg.Item.Id, userid).Updates(&item).Error; err != nil { + item, err := models.Items.Update( + // Set col + models.ItemSetter{ + Name: omit.From(req.Msg.Item.Name), + Description: omitnull.From(req.Msg.Item.Description), + Price: omitnull.From(req.Msg.Item.Price), + Quantity: omitnull.From(int64(req.Msg.Item.Quantity)), + }.UpdateMod(), + + // Where + sqlite.WhereAnd( + models.UpdateWhere.Items.ID.EQ(*req.Msg.Item.Id), + models.UpdateWhere.Items.UserID.EQ(userid), + ), + ).One(ctx, h.db) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } res := connect.NewResponse(&itemv1.UpdateItemResponse{ - Item: item.ToConnectV1(), + Item: itemToConnect(item), }) return res, nil } @@ -150,7 +193,13 @@ func (h *Handler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.De } // Delete item - if err := h.db.Delete(&models.Item{}, "id = ? AND user_id = ?", req.Msg.Id, userid).Error; err != nil { + _, err := models.Items.Delete( + sqlite.WhereAnd( + models.DeleteWhere.Items.ID.EQ(req.Msg.Id), + models.DeleteWhere.Items.UserID.EQ(userid), + ), + ).Exec(ctx, h.db) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } @@ -158,7 +207,7 @@ func (h *Handler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.De return res, nil } -func NewHandler(db *gorm.DB, key string) (string, http.Handler) { +func NewHandler(db *bob.DB, key string) (string, http.Handler) { interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key)) return itemv1connect.NewItemServiceHandler( diff --git a/server/internal/handlers/user/v1/auth.go b/server/internal/handlers/user/v1/auth.go index dc4004c..5d72315 100644 --- a/server/internal/handlers/user/v1/auth.go +++ b/server/internal/handlers/user/v1/auth.go @@ -2,38 +2,44 @@ package user import ( "context" + "database/sql" "errors" "net/http" "strconv" "time" - _ "crypto/sha256" + _ "crypto/sha256" // Crypto "connectrpc.com/connect" + "github.com/aarondl/opt/omit" "github.com/golang-jwt/jwt/v5" "github.com/spotdemo4/trevstack/server/internal/interceptors" "github.com/spotdemo4/trevstack/server/internal/models" userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" "github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect" - "github.com/veraison/go-cose" + "github.com/stephenafamo/bob" "golang.org/x/crypto/bcrypt" - "gorm.io/gorm" ) type AuthHandler struct { - db *gorm.DB + db *bob.DB key []byte } -func (h *AuthHandler) Login(_ context.Context, req *connect.Request[userv1.LoginRequest]) (*connect.Response[userv1.LoginResponse], error) { - // Validate - user := models.User{} - if err := h.db.First(&user, "username = ?", req.Msg.Username).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, connect.NewError(connect.CodePermissionDenied, errors.New("invalid username or password")) +func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.LoginRequest]) (*connect.Response[userv1.LoginResponse], error) { + // Get user + user, err := models.Users.Query( + models.SelectWhere.Users.Username.EQ(req.Msg.Username), + ).One(ctx, h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, connect.NewError(connect.CodePermissionDenied, err) } + return nil, connect.NewError(connect.CodeInternal, err) } + + // Check password if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Msg.Password)); err != nil { return nil, connect.NewError(connect.CodePermissionDenied, errors.New("invalid username or password")) } @@ -72,15 +78,21 @@ func (h *AuthHandler) Login(_ context.Context, req *connect.Request[userv1.Login return res, nil } -func (h *AuthHandler) SignUp(_ context.Context, req *connect.Request[userv1.SignUpRequest]) (*connect.Response[userv1.SignUpResponse], error) { - // Validate - if err := h.db.First(&models.User{}, "username = ?", req.Msg.Username).Error; err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { +func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.SignUpRequest]) (*connect.Response[userv1.SignUpResponse], error) { + // Get user + user, err := models.Users.Query( + models.SelectWhere.Users.Username.EQ(req.Msg.Username), + ).One(ctx, h.db) + if err != nil { + if !errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeInternal, err) } - } else { + } + if user != nil { return nil, connect.NewError(connect.CodeAlreadyExists, errors.New("username already exists")) } + + // Check if confirmation passwords match if req.Msg.Password != req.Msg.ConfirmPassword { return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("passwords do not match")) } @@ -92,11 +104,11 @@ func (h *AuthHandler) SignUp(_ context.Context, req *connect.Request[userv1.Sign } // Create user - user := models.User{ - Username: req.Msg.Username, - Password: string(hash), - } - if err := h.db.Create(&user).Error; err != nil { + _, err = models.Users.Insert(&models.UserSetter{ + Username: omit.From(req.Msg.Username), + Password: omit.From(string(hash)), + }).One(ctx, h.db) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } @@ -121,95 +133,95 @@ func (h *AuthHandler) Logout(_ context.Context, _ *connect.Request[userv1.Logout return res, nil } -func (h *AuthHandler) GetPasskeyIDs(_ context.Context, req *connect.Request[userv1.GetPasskeyIDsRequest]) (*connect.Response[userv1.GetPasskeyIDsResponse], error) { - // Get user - user := models.User{} - if err := h.db.Preload("Passkeys").First(&user, "username = ?", req.Msg.Username).Error; err != nil { - return nil, connect.NewError(connect.CodeNotFound, err) - } +// func (h *AuthHandler) GetPasskeyIDs(_ context.Context, req *connect.Request[userv1.GetPasskeyIDsRequest]) (*connect.Response[userv1.GetPasskeyIDsResponse], error) { +// // Get user +// user := models.User{} +// if err := h.db.Preload("Passkeys").First(&user, "username = ?", req.Msg.Username).Error; err != nil { +// return nil, connect.NewError(connect.CodeNotFound, err) +// } - // Get IDs - ids := []string{} - for _, passkey := range user.Passkeys { - ids = append(ids, passkey.ID) - } +// // Get IDs +// ids := []string{} +// for _, passkey := range user.Passkeys { +// ids = append(ids, passkey.ID) +// } - return connect.NewResponse(&userv1.GetPasskeyIDsResponse{ - PasskeyIds: ids, - }), nil -} +// return connect.NewResponse(&userv1.GetPasskeyIDsResponse{ +// PasskeyIds: ids, +// }), nil +// } -func (h *AuthHandler) PasskeyLogin(_ context.Context, req *connect.Request[userv1.PasskeyLoginRequest]) (*connect.Response[userv1.PasskeyLoginResponse], error) { - // Get passkey - passkey := models.Passkey{} - if err := h.db.First(&passkey, "id = ?", req.Msg.Id).Error; err != nil { - return nil, connect.NewError(connect.CodeNotFound, err) - } +// func (h *AuthHandler) PasskeyLogin(_ context.Context, req *connect.Request[userv1.PasskeyLoginRequest]) (*connect.Response[userv1.PasskeyLoginResponse], error) { +// // Get passkey +// passkey := models.Passkey{} +// if err := h.db.First(&passkey, "id = ?", req.Msg.Id).Error; err != nil { +// return nil, connect.NewError(connect.CodeNotFound, err) +// } - // create a verifier from a trusted private key - var verifier cose.Verifier - var err error - switch req.Msg.Algorithm { - case -7: - verifier, err = cose.NewVerifier(cose.AlgorithmES256, passkey.PublicKey) +// // create a verifier from a trusted private key +// var verifier cose.Verifier +// var err error +// switch req.Msg.Algorithm { +// case -7: +// verifier, err = cose.NewVerifier(cose.AlgorithmES256, passkey.PublicKey) - case -257: - verifier, err = cose.NewVerifier(cose.AlgorithmRS256, passkey.PublicKey) +// case -257: +// verifier, err = cose.NewVerifier(cose.AlgorithmRS256, passkey.PublicKey) - default: - return nil, connect.NewError(connect.CodeInternal, errors.New("decode algorithm not implemented")) - } - if err != nil { - return nil, connect.NewError(connect.CodeInternal, err) - } +// default: +// return nil, connect.NewError(connect.CodeInternal, errors.New("decode algorithm not implemented")) +// } +// if err != nil { +// return nil, connect.NewError(connect.CodeInternal, err) +// } - // create a sign message from a raw signature payload - var msg cose.Sign1Message - if err = msg.UnmarshalCBOR(req.Msg.Signature); err != nil { - return nil, connect.NewError(connect.CodeInternal, err) - } +// // create a sign message from a raw signature payload +// var msg cose.Sign1Message +// if err = msg.UnmarshalCBOR(req.Msg.Signature); err != nil { +// return nil, connect.NewError(connect.CodeInternal, err) +// } - // Validate passkey - err = msg.Verify(nil, verifier) - if err != nil { - return nil, connect.NewError(connect.CodeUnauthenticated, err) - } +// // Validate passkey +// err = msg.Verify(nil, verifier) +// if err != nil { +// return nil, connect.NewError(connect.CodeUnauthenticated, err) +// } - // Generate JWT - t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ - Issuer: "trevstack", - Subject: strconv.FormatUint(uint64(passkey.UserID), 10), - IssuedAt: &jwt.NumericDate{ - Time: time.Now(), - }, - ExpiresAt: &jwt.NumericDate{ - Time: time.Now().Add(time.Hour * 24), - }, - }) - ss, err := t.SignedString(h.key) - if err != nil { - return nil, connect.NewError(connect.CodeInternal, err) - } +// // Generate JWT +// t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ +// Issuer: "trevstack", +// Subject: strconv.FormatUint(uint64(passkey.UserID), 10), +// IssuedAt: &jwt.NumericDate{ +// Time: time.Now(), +// }, +// ExpiresAt: &jwt.NumericDate{ +// Time: time.Now().Add(time.Hour * 24), +// }, +// }) +// ss, err := t.SignedString(h.key) +// if err != nil { +// return nil, connect.NewError(connect.CodeInternal, err) +// } - // Create cookie - cookie := http.Cookie{ - Name: "token", - Value: ss, - Path: "/", - MaxAge: 86400, - HttpOnly: true, - Secure: true, - SameSite: http.SameSiteStrictMode, - } +// // Create cookie +// cookie := http.Cookie{ +// Name: "token", +// Value: ss, +// Path: "/", +// MaxAge: 86400, +// HttpOnly: true, +// Secure: true, +// SameSite: http.SameSiteStrictMode, +// } - res := connect.NewResponse(&userv1.PasskeyLoginResponse{ - Token: ss, - }) - res.Header().Set("Set-Cookie", cookie.String()) - return res, nil -} +// res := connect.NewResponse(&userv1.PasskeyLoginResponse{ +// Token: ss, +// }) +// res.Header().Set("Set-Cookie", cookie.String()) +// return res, nil +// } -func NewAuthHandler(db *gorm.DB, key string) (string, http.Handler) { +func NewAuthHandler(db *bob.DB, key string) (string, http.Handler) { interceptors := connect.WithInterceptors(interceptors.NewRateLimitInterceptor(key)) return userv1connect.NewAuthServiceHandler( diff --git a/server/internal/handlers/user/v1/user.go b/server/internal/handlers/user/v1/user.go index a3418cc..91defae 100644 --- a/server/internal/handlers/user/v1/user.go +++ b/server/internal/handlers/user/v1/user.go @@ -2,23 +2,34 @@ package user import ( "context" + "database/sql" "errors" "net/http" "strconv" "time" "connectrpc.com/connect" + "github.com/aarondl/opt/omit" + "github.com/aarondl/opt/omitnull" "github.com/golang-jwt/jwt/v5" "github.com/spotdemo4/trevstack/server/internal/interceptors" "github.com/spotdemo4/trevstack/server/internal/models" userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" "github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect" + "github.com/stephenafamo/bob" "golang.org/x/crypto/bcrypt" - "gorm.io/gorm" ) +func userToConnect(item *models.User) *userv1.User { + return &userv1.User{ + Id: item.ID, + Username: item.Username, + ProfilePictureId: item.ProfilePictureID.Ptr(), + } +} + type Handler struct { - db *gorm.DB + db *bob.DB key []byte } @@ -29,13 +40,19 @@ func (h *Handler) GetUser(ctx context.Context, _ *connect.Request[userv1.GetUser } // Get user - user := models.User{} - if err := h.db.Preload("ProfilePicture").First(&user, "id = ?", userid).Error; err != nil { + user, err := models.Users.Query( + models.SelectWhere.Users.ID.EQ(userid), + ).One(ctx, h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, err) + } + return nil, connect.NewError(connect.CodeInternal, err) } res := connect.NewResponse(&userv1.GetUserResponse{ - User: user.ToConnectV1(), + User: userToConnect(user), }) return res, nil } @@ -47,8 +64,14 @@ func (h *Handler) UpdatePassword(ctx context.Context, req *connect.Request[userv } // Get user - user := models.User{} - if err := h.db.First(&user, "id = ?", userid).Error; err != nil { + user, err := models.Users.Query( + models.SelectWhere.Users.ID.EQ(userid), + ).One(ctx, h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, err) + } + return nil, connect.NewError(connect.CodeInternal, err) } @@ -67,7 +90,10 @@ func (h *Handler) UpdatePassword(ctx context.Context, req *connect.Request[userv } // Update password - if err := h.db.Model(&user).Update("password", string(hash)).Error; err != nil { + err = user.Update(ctx, h.db, &models.UserSetter{ + Password: omit.From(string(hash)), + }) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } @@ -82,8 +108,14 @@ func (h *Handler) GetAPIKey(ctx context.Context, req *connect.Request[userv1.Get } // Get user - user := models.User{} - if err := h.db.First(&user, "id = ?", userid).Error; err != nil { + user, err := models.Users.Query( + models.SelectWhere.Users.ID.EQ(userid), + ).One(ctx, h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, err) + } + return nil, connect.NewError(connect.CodeInternal, err) } @@ -98,7 +130,7 @@ func (h *Handler) GetAPIKey(ctx context.Context, req *connect.Request[userv1.Get // Generate JWT t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ Issuer: "trevstack", - Subject: strconv.FormatUint(uint64(user.ID), 10), + Subject: strconv.FormatInt(user.ID, 10), IssuedAt: &jwt.NumericDate{ Time: time.Now(), }, @@ -127,79 +159,88 @@ func (h *Handler) UpdateProfilePicture(ctx context.Context, req *connect.Request } // Save bytes into file - file := models.File{ - Name: req.Msg.FileName, - Data: req.Msg.Data, - UserID: uint(userid), - } - if err := h.db.Create(&file).Error; err != nil { + file, err := models.Files.Insert(&models.FileSetter{ + Name: omit.From(req.Msg.FileName), + Data: omit.From(req.Msg.Data), + UserID: omit.From(userid), + }).One(ctx, h.db) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } - // Get user info - user := models.User{} - if err := h.db.First(&user, "id = ?", userid).Error; err != nil { + // Get user + user, err := models.Users.Query( + models.SelectWhere.Users.ID.EQ(userid), + ).One(ctx, h.db) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, err) + } + return nil, connect.NewError(connect.CodeInternal, err) } // Get old profile picture ID - var ppid *uint32 - if user.ProfilePicture != nil { - ppid = &user.ProfilePicture.ID + var ppid *int64 + if user.ProfilePictureID.Ptr() != nil { + ppid = user.ProfilePictureID.Ptr() } // Update user profile picture - fid := uint(file.ID) - user.ProfilePictureID = &fid - user.ProfilePicture = &file - if err := h.db.Save(&user).Error; err != nil { + err = user.Update(ctx, h.db, &models.UserSetter{ + ProfilePictureID: omitnull.From(file.ID), + }) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } // Delete old profile picture if exists if ppid != nil { - if err := h.db.Delete(models.File{}, "id = ?", *ppid).Error; err != nil { + _, err = models.Files.Delete( + models.DeleteWhere.Files.ID.EQ(*ppid), + ).Exec(ctx, h.db) + if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } } res := connect.NewResponse(&userv1.UpdateProfilePictureResponse{ - User: user.ToConnectV1(), + User: userToConnect(user), }) return res, nil } -func (h *Handler) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) { - // Get user ID from context - userid, ok := interceptors.GetUserContext(ctx) - if !ok { - return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated")) - } +// func (h *Handler) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) { +// // Get user ID from context +// userid, ok := interceptors.GetUserContext(ctx) +// if !ok { +// return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated")) +// } - // Get user - user := models.User{} - if err := h.db.First(&user, "id = ?", userid).Error; err != nil { - return nil, connect.NewError(connect.CodeInternal, err) - } +// // Get user +// user := models.User{} +// if err := h.db.First(&user, "id = ?", userid).Error; err != nil { +// return nil, connect.NewError(connect.CodeInternal, err) +// } - return connect.NewResponse(&userv1.BeginPasskeyRegistrationResponse{}), nil -} +// return connect.NewResponse(&userv1.BeginPasskeyRegistrationResponse{}), nil +// } -func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.FinishPasskeyRegistrationRequest]) (*connect.Response[userv1.FinishPasskeyRegistrationResponse], error) { - // Get user ID from context - userid, ok := interceptors.GetUserContext(ctx) - if !ok { - return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated")) - } +// func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.FinishPasskeyRegistrationRequest]) (*connect.Response[userv1.FinishPasskeyRegistrationResponse], error) { +// // Get user ID from context +// userid, ok := interceptors.GetUserContext(ctx) +// if !ok { +// return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated")) +// } - // Get user - user := models.User{} - if err := h.db.First(&user, "id = ?", userid).Error; err != nil { - return nil, connect.NewError(connect.CodeInternal, err) - } +// // Get user +// user := models.User{} +// if err := h.db.First(&user, "id = ?", userid).Error; err != nil { +// return nil, connect.NewError(connect.CodeInternal, err) +// } - return connect.NewResponse(&userv1.FinishPasskeyRegistrationResponse{}), nil -} +// return connect.NewResponse(&userv1.FinishPasskeyRegistrationResponse{}), nil +// } // func BeginRegistration(ctx context.Context) error { // userid, ok := interceptors.GetUserContext(ctx) @@ -231,7 +272,7 @@ func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Re // return nil // } -func NewHandler(db *gorm.DB, key string) (string, http.Handler) { +func NewHandler(db *bob.DB, key string) (string, http.Handler) { interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key)) return userv1connect.NewUserServiceHandler( diff --git a/server/internal/interceptors/auth.go b/server/internal/interceptors/auth.go index 7941ec3..43dbdb0 100644 --- a/server/internal/interceptors/auth.go +++ b/server/internal/interceptors/auth.go @@ -217,7 +217,7 @@ func validateToken(tokenString string, key string) (subject string, err error) { // key is an unexported type for keys defined in this package. // This prevents collisions with keys defined in other packages. -type key int +type key int64 // userKey is the key for user.User values in Contexts. It is // unexported; clients use user.NewContext and user.FromContext @@ -231,11 +231,11 @@ func newUserContext(ctx context.Context, subject string) (context.Context, error return nil, err } - return context.WithValue(ctx, userKey, id), nil + return context.WithValue(ctx, userKey, int64(id)), nil } // getUserContext returns the User value stored in ctx, if any. -func GetUserContext(ctx context.Context) (int, bool) { - u, ok := ctx.Value(userKey).(int) +func GetUserContext(ctx context.Context) (int64, bool) { + u, ok := ctx.Value(userKey).(int64) return u, ok } diff --git a/server/internal/interceptors/cors.go b/server/internal/interceptors/cors.go new file mode 100644 index 0000000..d62b751 --- /dev/null +++ b/server/internal/interceptors/cors.go @@ -0,0 +1,19 @@ +package interceptors + +import ( + "net/http" + + connectcors "connectrpc.com/cors" + "github.com/rs/cors" +) + +// WithCORS adds CORS support to a Connect HTTP handler. +func WithCORS(pattern string, h http.Handler) (string, http.Handler) { + middleware := cors.New(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: connectcors.AllowedMethods(), + AllowedHeaders: connectcors.AllowedHeaders(), + ExposedHeaders: connectcors.ExposedHeaders(), + }) + return pattern, middleware.Handler(h) +} diff --git a/server/internal/interceptors/ratelimit.go b/server/internal/interceptors/ratelimit.go index 7050662..254f324 100644 --- a/server/internal/interceptors/ratelimit.go +++ b/server/internal/interceptors/ratelimit.go @@ -46,7 +46,7 @@ func (i *RatelimitInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFu // Get user agent limiter := i.getVisitor(req.Header().Get("User-Agent")) - if limiter.Allow() == false { + if !limiter.Allow() { return nil, connect.NewError(connect.CodeResourceExhausted, errors.New("rate limit exceeded")) } @@ -70,7 +70,7 @@ func (i *RatelimitInterceptor) WrapStreamingHandler(next connect.StreamingHandle ) error { // Get user agent limiter := i.getVisitor(conn.RequestHeader().Get("User-Agent")) - if limiter.Allow() == false { + if !limiter.Allow() { return connect.NewError(connect.CodeResourceExhausted, errors.New("rate limit exceeded")) } diff --git a/server/internal/models/bob_main.bob.go b/server/internal/models/bob_main.bob.go index 153ea59..d84014e 100644 --- a/server/internal/models/bob_main.bob.go +++ b/server/internal/models/bob_main.bob.go @@ -19,9 +19,9 @@ var TableNames = struct { Items string Users string }{ - Files: "files", - Items: "items", - Users: "users", + Files: "file", + Items: "item", + Users: "user", } var ColumnNames = struct { @@ -38,10 +38,10 @@ var ColumnNames = struct { Items: itemColumnNames{ ID: "id", Name: "name", + Added: "added", Description: "description", Price: "price", Quantity: "quantity", - Added: "added", UserID: "user_id", }, Users: userColumnNames{ @@ -49,7 +49,6 @@ var ColumnNames = struct { Username: "username", Password: "password", ProfilePictureID: "profile_picture_id", - Challenge: "challenge", }, } diff --git a/server/internal/models/factory/bobfactory_random.bob.go b/server/internal/models/factory/bobfactory_random.bob.go index f2e0ab4..eb5c1ec 100644 --- a/server/internal/models/factory/bobfactory_random.bob.go +++ b/server/internal/models/factory/bobfactory_random.bob.go @@ -28,12 +28,12 @@ func random_float32(f *faker.Faker) float32 { return f.Float32(10, -1_000_000, 1_000_000) } -func random_int32(f *faker.Faker) int32 { +func random_int64(f *faker.Faker) int64 { if f == nil { f = &defaultFaker } - return f.Int32() + return f.Int64() } func random_string(f *faker.Faker) string { diff --git a/server/internal/models/factory/bobfactory_random_test.bob.go b/server/internal/models/factory/bobfactory_random_test.bob.go index 3957f15..2c322bb 100644 --- a/server/internal/models/factory/bobfactory_random_test.bob.go +++ b/server/internal/models/factory/bobfactory_random_test.bob.go @@ -8,14 +8,14 @@ import ( "testing" ) -func TestRandom_int32(t *testing.T) { +func TestRandom_int64(t *testing.T) { t.Parallel() - val1 := random_int32(nil) - val2 := random_int32(nil) + val1 := random_int64(nil) + val2 := random_int64(nil) if val1 == val2 { - t.Fatalf("random_int32() returned the same value twice: %v", val1) + t.Fatalf("random_int64() returned the same value twice: %v", val1) } } @@ -41,17 +41,6 @@ func TestRandom___byte(t *testing.T) { } } -func TestRandom_float32(t *testing.T) { - t.Parallel() - - val1 := random_float32(nil) - val2 := random_float32(nil) - - if val1 == val2 { - t.Fatalf("random_float32() returned the same value twice: %v", val1) - } -} - func TestRandom_time_Time(t *testing.T) { t.Parallel() @@ -62,3 +51,14 @@ func TestRandom_time_Time(t *testing.T) { t.Fatalf("random_time_Time() returned the same value twice: %v", val1) } } + +func TestRandom_float32(t *testing.T) { + t.Parallel() + + val1 := random_float32(nil) + val2 := random_float32(nil) + + if val1 == val2 { + t.Fatalf("random_float32() returned the same value twice: %v", val1) + } +} diff --git a/server/internal/models/factory/files.bob.go b/server/internal/models/factory/file.bob.go similarity index 75% rename from server/internal/models/factory/files.bob.go rename to server/internal/models/factory/file.bob.go index 54cfef8..879a12d 100644 --- a/server/internal/models/factory/files.bob.go +++ b/server/internal/models/factory/file.bob.go @@ -9,7 +9,6 @@ import ( "github.com/aarondl/opt/null" "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" "github.com/jaswdr/faker/v2" models "github.com/spotdemo4/trevstack/server/internal/models" "github.com/stephenafamo/bob" @@ -36,22 +35,27 @@ func (mods FileModSlice) Apply(n *FileTemplate) { // FileTemplate is an object representing the database table. // all columns are optional and should be set by mods type FileTemplate struct { - ID func() int32 - Name func() null.Val[string] - Data func() null.Val[[]byte] - UserID func() null.Val[int32] + ID func() int64 + Name func() string + Data func() []byte + UserID func() int64 r fileR f *Factory } type fileR struct { - User *fileRUserR + User *fileRUserR + ProfilePictureUsers []*fileRProfilePictureUsersR } type fileRUserR struct { o *UserTemplate } +type fileRProfilePictureUsersR struct { + number int + o *UserTemplate +} // Apply mods to the FileTemplate func (o *FileTemplate) Apply(mods ...FileMod) { @@ -99,9 +103,22 @@ func (t FileTemplate) setModelRels(o *models.File) { if t.r.User != nil { rel := t.r.User.o.toModel() rel.R.Files = append(rel.R.Files, o) - o.UserID = null.From(rel.ID) + o.UserID = rel.ID o.R.User = rel } + + if t.r.ProfilePictureUsers != nil { + rel := models.UserSlice{} + for _, r := range t.r.ProfilePictureUsers { + related := r.o.toModels(r.number) + for _, rel := range related { + rel.ProfilePictureID = null.From(o.ID) + rel.R.ProfilePictureFile = o + } + rel = append(rel, related...) + } + o.R.ProfilePictureUsers = rel + } } // BuildSetter returns an *models.FileSetter @@ -113,13 +130,13 @@ func (o FileTemplate) BuildSetter() *models.FileSetter { m.ID = omit.From(o.ID()) } if o.Name != nil { - m.Name = omitnull.FromNull(o.Name()) + m.Name = omit.From(o.Name()) } if o.Data != nil { - m.Data = omitnull.FromNull(o.Data()) + m.Data = omit.From(o.Data()) } if o.UserID != nil { - m.UserID = omitnull.FromNull(o.UserID()) + m.UserID = omit.From(o.UserID()) } return m @@ -161,6 +178,15 @@ func (o FileTemplate) BuildMany(number int) models.FileSlice { } func ensureCreatableFile(m *models.FileSetter) { + if m.Name.IsUnset() { + m.Name = omit.From(random_string(nil)) + } + if m.Data.IsUnset() { + m.Data = omit.From(random___byte(nil)) + } + if m.UserID.IsUnset() { + m.UserID = omit.From(random_int64(nil)) + } } // insertOptRels creates and inserts any optional the relationships on *models.File @@ -169,15 +195,18 @@ func ensureCreatableFile(m *models.FileSetter) { func (o *FileTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.File) (context.Context, error) { var err error - if o.r.User != nil { - var rel0 *models.User - ctx, rel0, err = o.r.User.o.create(ctx, exec) - if err != nil { - return ctx, err - } - err = m.AttachUser(ctx, exec, rel0) - if err != nil { - return ctx, err + if o.r.ProfilePictureUsers != nil { + for _, r := range o.r.ProfilePictureUsers { + var rel1 models.UserSlice + ctx, rel1, err = r.o.createMany(ctx, exec, r.number) + if err != nil { + return ctx, err + } + + err = m.AttachProfilePictureUsers(ctx, exec, rel1...) + if err != nil { + return ctx, err + } } } @@ -223,12 +252,30 @@ func (o *FileTemplate) create(ctx context.Context, exec bob.Executor) (context.C opt := o.BuildSetter() ensureCreatableFile(opt) + var rel0 *models.User + if o.r.User == nil { + var ok bool + rel0, ok = userCtx.Value(ctx) + if !ok { + FileMods.WithNewUser().Apply(o) + } + } + if o.r.User != nil { + ctx, rel0, err = o.r.User.o.create(ctx, exec) + if err != nil { + return ctx, nil, err + } + } + opt.UserID = omit.From(rel0.ID) + m, err := models.Files.Insert(opt).One(ctx, exec) if err != nil { return ctx, nil, err } ctx = fileCtx.WithValue(ctx, m) + m.R.User = rel0 + ctx, err = o.insertOptRels(ctx, exec, m) return ctx, m, err } @@ -296,14 +343,14 @@ func (m fileMods) RandomizeAllColumns(f *faker.Faker) FileMod { } // Set the model columns to this value -func (m fileMods) ID(val int32) FileMod { +func (m fileMods) ID(val int64) FileMod { return FileModFunc(func(o *FileTemplate) { - o.ID = func() int32 { return val } + o.ID = func() int64 { return val } }) } // Set the Column from the function -func (m fileMods) IDFunc(f func() int32) FileMod { +func (m fileMods) IDFunc(f func() int64) FileMod { return FileModFunc(func(o *FileTemplate) { o.ID = f }) @@ -320,21 +367,21 @@ func (m fileMods) UnsetID() FileMod { // if faker is nil, a default faker is used func (m fileMods) RandomID(f *faker.Faker) FileMod { return FileModFunc(func(o *FileTemplate) { - o.ID = func() int32 { - return random_int32(f) + o.ID = func() int64 { + return random_int64(f) } }) } // Set the model columns to this value -func (m fileMods) Name(val null.Val[string]) FileMod { +func (m fileMods) Name(val string) FileMod { return FileModFunc(func(o *FileTemplate) { - o.Name = func() null.Val[string] { return val } + o.Name = func() string { return val } }) } // Set the Column from the function -func (m fileMods) NameFunc(f func() null.Val[string]) FileMod { +func (m fileMods) NameFunc(f func() string) FileMod { return FileModFunc(func(o *FileTemplate) { o.Name = f }) @@ -351,29 +398,21 @@ func (m fileMods) UnsetName() FileMod { // if faker is nil, a default faker is used func (m fileMods) RandomName(f *faker.Faker) FileMod { return FileModFunc(func(o *FileTemplate) { - o.Name = func() null.Val[string] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[string](nil) - } - - return null.From(random_string(f)) + o.Name = func() string { + return random_string(f) } }) } // Set the model columns to this value -func (m fileMods) Data(val null.Val[[]byte]) FileMod { +func (m fileMods) Data(val []byte) FileMod { return FileModFunc(func(o *FileTemplate) { - o.Data = func() null.Val[[]byte] { return val } + o.Data = func() []byte { return val } }) } // Set the Column from the function -func (m fileMods) DataFunc(f func() null.Val[[]byte]) FileMod { +func (m fileMods) DataFunc(f func() []byte) FileMod { return FileModFunc(func(o *FileTemplate) { o.Data = f }) @@ -390,29 +429,21 @@ func (m fileMods) UnsetData() FileMod { // if faker is nil, a default faker is used func (m fileMods) RandomData(f *faker.Faker) FileMod { return FileModFunc(func(o *FileTemplate) { - o.Data = func() null.Val[[]byte] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[[]byte](nil) - } - - return null.From(random___byte(f)) + o.Data = func() []byte { + return random___byte(f) } }) } // Set the model columns to this value -func (m fileMods) UserID(val null.Val[int32]) FileMod { +func (m fileMods) UserID(val int64) FileMod { return FileModFunc(func(o *FileTemplate) { - o.UserID = func() null.Val[int32] { return val } + o.UserID = func() int64 { return val } }) } // Set the Column from the function -func (m fileMods) UserIDFunc(f func() null.Val[int32]) FileMod { +func (m fileMods) UserIDFunc(f func() int64) FileMod { return FileModFunc(func(o *FileTemplate) { o.UserID = f }) @@ -429,16 +460,8 @@ func (m fileMods) UnsetUserID() FileMod { // if faker is nil, a default faker is used func (m fileMods) RandomUserID(f *faker.Faker) FileMod { return FileModFunc(func(o *FileTemplate) { - o.UserID = func() null.Val[int32] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[int32](nil) - } - - return null.From(random_int32(f)) + o.UserID = func() int64 { + return random_int64(f) } }) } @@ -464,3 +487,41 @@ func (m fileMods) WithoutUser() FileMod { o.r.User = nil }) } + +func (m fileMods) WithProfilePictureUsers(number int, related *UserTemplate) FileMod { + return FileModFunc(func(o *FileTemplate) { + o.r.ProfilePictureUsers = []*fileRProfilePictureUsersR{{ + number: number, + o: related, + }} + }) +} + +func (m fileMods) WithNewProfilePictureUsers(number int, mods ...UserMod) FileMod { + return FileModFunc(func(o *FileTemplate) { + related := o.f.NewUser(mods...) + m.WithProfilePictureUsers(number, related).Apply(o) + }) +} + +func (m fileMods) AddProfilePictureUsers(number int, related *UserTemplate) FileMod { + return FileModFunc(func(o *FileTemplate) { + o.r.ProfilePictureUsers = append(o.r.ProfilePictureUsers, &fileRProfilePictureUsersR{ + number: number, + o: related, + }) + }) +} + +func (m fileMods) AddNewProfilePictureUsers(number int, mods ...UserMod) FileMod { + return FileModFunc(func(o *FileTemplate) { + related := o.f.NewUser(mods...) + m.AddProfilePictureUsers(number, related).Apply(o) + }) +} + +func (m fileMods) WithoutProfilePictureUsers() FileMod { + return FileModFunc(func(o *FileTemplate) { + o.r.ProfilePictureUsers = nil + }) +} diff --git a/server/internal/models/factory/items.bob.go b/server/internal/models/factory/item.bob.go similarity index 86% rename from server/internal/models/factory/items.bob.go rename to server/internal/models/factory/item.bob.go index 3f7b961..281f7ad 100644 --- a/server/internal/models/factory/items.bob.go +++ b/server/internal/models/factory/item.bob.go @@ -37,13 +37,13 @@ func (mods ItemModSlice) Apply(n *ItemTemplate) { // ItemTemplate is an object representing the database table. // all columns are optional and should be set by mods type ItemTemplate struct { - ID func() int32 - Name func() null.Val[string] + ID func() int64 + Name func() string + Added func() time.Time Description func() null.Val[string] Price func() null.Val[float32] - Quantity func() null.Val[int32] - Added func() null.Val[time.Time] - UserID func() null.Val[int32] + Quantity func() null.Val[int64] + UserID func() int64 r itemR f *Factory @@ -75,6 +75,9 @@ func (o ItemTemplate) toModel() *models.Item { if o.Name != nil { m.Name = o.Name() } + if o.Added != nil { + m.Added = o.Added() + } if o.Description != nil { m.Description = o.Description() } @@ -84,9 +87,6 @@ func (o ItemTemplate) toModel() *models.Item { if o.Quantity != nil { m.Quantity = o.Quantity() } - if o.Added != nil { - m.Added = o.Added() - } if o.UserID != nil { m.UserID = o.UserID() } @@ -112,7 +112,7 @@ func (t ItemTemplate) setModelRels(o *models.Item) { if t.r.User != nil { rel := t.r.User.o.toModel() rel.R.Items = append(rel.R.Items, o) - o.UserID = null.From(rel.ID) + o.UserID = rel.ID o.R.User = rel } } @@ -126,7 +126,10 @@ func (o ItemTemplate) BuildSetter() *models.ItemSetter { m.ID = omit.From(o.ID()) } if o.Name != nil { - m.Name = omitnull.FromNull(o.Name()) + m.Name = omit.From(o.Name()) + } + if o.Added != nil { + m.Added = omit.From(o.Added()) } if o.Description != nil { m.Description = omitnull.FromNull(o.Description()) @@ -137,11 +140,8 @@ func (o ItemTemplate) BuildSetter() *models.ItemSetter { if o.Quantity != nil { m.Quantity = omitnull.FromNull(o.Quantity()) } - if o.Added != nil { - m.Added = omitnull.FromNull(o.Added()) - } if o.UserID != nil { - m.UserID = omitnull.FromNull(o.UserID()) + m.UserID = omit.From(o.UserID()) } return m @@ -183,6 +183,15 @@ func (o ItemTemplate) BuildMany(number int) models.ItemSlice { } func ensureCreatableItem(m *models.ItemSetter) { + if m.Name.IsUnset() { + m.Name = omit.From(random_string(nil)) + } + if m.Added.IsUnset() { + m.Added = omit.From(random_time_Time(nil)) + } + if m.UserID.IsUnset() { + m.UserID = omit.From(random_int64(nil)) + } } // insertOptRels creates and inserts any optional the relationships on *models.Item @@ -191,18 +200,6 @@ func ensureCreatableItem(m *models.ItemSetter) { func (o *ItemTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.Item) (context.Context, error) { var err error - if o.r.User != nil { - var rel0 *models.User - ctx, rel0, err = o.r.User.o.create(ctx, exec) - if err != nil { - return ctx, err - } - err = m.AttachUser(ctx, exec, rel0) - if err != nil { - return ctx, err - } - } - return ctx, err } @@ -245,12 +242,30 @@ func (o *ItemTemplate) create(ctx context.Context, exec bob.Executor) (context.C opt := o.BuildSetter() ensureCreatableItem(opt) + var rel0 *models.User + if o.r.User == nil { + var ok bool + rel0, ok = userCtx.Value(ctx) + if !ok { + ItemMods.WithNewUser().Apply(o) + } + } + if o.r.User != nil { + ctx, rel0, err = o.r.User.o.create(ctx, exec) + if err != nil { + return ctx, nil, err + } + } + opt.UserID = omit.From(rel0.ID) + m, err := models.Items.Insert(opt).One(ctx, exec) if err != nil { return ctx, nil, err } ctx = itemCtx.WithValue(ctx, m) + m.R.User = rel0 + ctx, err = o.insertOptRels(ctx, exec, m) return ctx, m, err } @@ -312,23 +327,23 @@ func (m itemMods) RandomizeAllColumns(f *faker.Faker) ItemMod { return ItemModSlice{ ItemMods.RandomID(f), ItemMods.RandomName(f), + ItemMods.RandomAdded(f), ItemMods.RandomDescription(f), ItemMods.RandomPrice(f), ItemMods.RandomQuantity(f), - ItemMods.RandomAdded(f), ItemMods.RandomUserID(f), } } // Set the model columns to this value -func (m itemMods) ID(val int32) ItemMod { +func (m itemMods) ID(val int64) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.ID = func() int32 { return val } + o.ID = func() int64 { return val } }) } // Set the Column from the function -func (m itemMods) IDFunc(f func() int32) ItemMod { +func (m itemMods) IDFunc(f func() int64) ItemMod { return ItemModFunc(func(o *ItemTemplate) { o.ID = f }) @@ -345,21 +360,21 @@ func (m itemMods) UnsetID() ItemMod { // if faker is nil, a default faker is used func (m itemMods) RandomID(f *faker.Faker) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.ID = func() int32 { - return random_int32(f) + o.ID = func() int64 { + return random_int64(f) } }) } // Set the model columns to this value -func (m itemMods) Name(val null.Val[string]) ItemMod { +func (m itemMods) Name(val string) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.Name = func() null.Val[string] { return val } + o.Name = func() string { return val } }) } // Set the Column from the function -func (m itemMods) NameFunc(f func() null.Val[string]) ItemMod { +func (m itemMods) NameFunc(f func() string) ItemMod { return ItemModFunc(func(o *ItemTemplate) { o.Name = f }) @@ -376,16 +391,39 @@ func (m itemMods) UnsetName() ItemMod { // if faker is nil, a default faker is used func (m itemMods) RandomName(f *faker.Faker) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.Name = func() null.Val[string] { - if f == nil { - f = &defaultFaker - } + o.Name = func() string { + return random_string(f) + } + }) +} - if f.Bool() { - return null.FromPtr[string](nil) - } +// Set the model columns to this value +func (m itemMods) Added(val time.Time) ItemMod { + return ItemModFunc(func(o *ItemTemplate) { + o.Added = func() time.Time { return val } + }) +} - return null.From(random_string(f)) +// Set the Column from the function +func (m itemMods) AddedFunc(f func() time.Time) ItemMod { + return ItemModFunc(func(o *ItemTemplate) { + o.Added = f + }) +} + +// Clear any values for the column +func (m itemMods) UnsetAdded() ItemMod { + return ItemModFunc(func(o *ItemTemplate) { + o.Added = nil + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +func (m itemMods) RandomAdded(f *faker.Faker) ItemMod { + return ItemModFunc(func(o *ItemTemplate) { + o.Added = func() time.Time { + return random_time_Time(f) } }) } @@ -469,14 +507,14 @@ func (m itemMods) RandomPrice(f *faker.Faker) ItemMod { } // Set the model columns to this value -func (m itemMods) Quantity(val null.Val[int32]) ItemMod { +func (m itemMods) Quantity(val null.Val[int64]) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.Quantity = func() null.Val[int32] { return val } + o.Quantity = func() null.Val[int64] { return val } }) } // Set the Column from the function -func (m itemMods) QuantityFunc(f func() null.Val[int32]) ItemMod { +func (m itemMods) QuantityFunc(f func() null.Val[int64]) ItemMod { return ItemModFunc(func(o *ItemTemplate) { o.Quantity = f }) @@ -493,68 +531,29 @@ func (m itemMods) UnsetQuantity() ItemMod { // if faker is nil, a default faker is used func (m itemMods) RandomQuantity(f *faker.Faker) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.Quantity = func() null.Val[int32] { + o.Quantity = func() null.Val[int64] { if f == nil { f = &defaultFaker } if f.Bool() { - return null.FromPtr[int32](nil) + return null.FromPtr[int64](nil) } - return null.From(random_int32(f)) + return null.From(random_int64(f)) } }) } // Set the model columns to this value -func (m itemMods) Added(val null.Val[time.Time]) ItemMod { +func (m itemMods) UserID(val int64) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.Added = func() null.Val[time.Time] { return val } + o.UserID = func() int64 { return val } }) } // Set the Column from the function -func (m itemMods) AddedFunc(f func() null.Val[time.Time]) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Added = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetAdded() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Added = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomAdded(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Added = func() null.Val[time.Time] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[time.Time](nil) - } - - return null.From(random_time_Time(f)) - } - }) -} - -// Set the model columns to this value -func (m itemMods) UserID(val null.Val[int32]) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.UserID = func() null.Val[int32] { return val } - }) -} - -// Set the Column from the function -func (m itemMods) UserIDFunc(f func() null.Val[int32]) ItemMod { +func (m itemMods) UserIDFunc(f func() int64) ItemMod { return ItemModFunc(func(o *ItemTemplate) { o.UserID = f }) @@ -571,16 +570,8 @@ func (m itemMods) UnsetUserID() ItemMod { // if faker is nil, a default faker is used func (m itemMods) RandomUserID(f *faker.Faker) ItemMod { return ItemModFunc(func(o *ItemTemplate) { - o.UserID = func() null.Val[int32] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[int32](nil) - } - - return null.From(random_int32(f)) + o.UserID = func() int64 { + return random_int64(f) } }) } diff --git a/server/internal/models/factory/users.bob.go b/server/internal/models/factory/user.bob.go similarity index 83% rename from server/internal/models/factory/users.bob.go rename to server/internal/models/factory/user.bob.go index 64d9b64..2210f16 100644 --- a/server/internal/models/factory/users.bob.go +++ b/server/internal/models/factory/user.bob.go @@ -36,19 +36,19 @@ func (mods UserModSlice) Apply(n *UserTemplate) { // UserTemplate is an object representing the database table. // all columns are optional and should be set by mods type UserTemplate struct { - ID func() int32 - Username func() null.Val[string] - Password func() null.Val[string] - ProfilePictureID func() null.Val[int32] - Challenge func() null.Val[string] + ID func() int64 + Username func() string + Password func() string + ProfilePictureID func() null.Val[int64] r userR f *Factory } type userR struct { - Files []*userRFilesR - Items []*userRItemsR + Files []*userRFilesR + Items []*userRItemsR + ProfilePictureFile *userRProfilePictureFileR } type userRFilesR struct { @@ -59,6 +59,9 @@ type userRItemsR struct { number int o *ItemTemplate } +type userRProfilePictureFileR struct { + o *FileTemplate +} // Apply mods to the UserTemplate func (o *UserTemplate) Apply(mods ...UserMod) { @@ -84,9 +87,6 @@ func (o UserTemplate) toModel() *models.User { if o.ProfilePictureID != nil { m.ProfilePictureID = o.ProfilePictureID() } - if o.Challenge != nil { - m.Challenge = o.Challenge() - } return m } @@ -111,7 +111,7 @@ func (t UserTemplate) setModelRels(o *models.User) { for _, r := range t.r.Files { related := r.o.toModels(r.number) for _, rel := range related { - rel.UserID = null.From(o.ID) + rel.UserID = o.ID rel.R.User = o } rel = append(rel, related...) @@ -124,13 +124,20 @@ func (t UserTemplate) setModelRels(o *models.User) { for _, r := range t.r.Items { related := r.o.toModels(r.number) for _, rel := range related { - rel.UserID = null.From(o.ID) + rel.UserID = o.ID rel.R.User = o } rel = append(rel, related...) } o.R.Items = rel } + + if t.r.ProfilePictureFile != nil { + rel := t.r.ProfilePictureFile.o.toModel() + rel.R.ProfilePictureUsers = append(rel.R.ProfilePictureUsers, o) + o.ProfilePictureID = null.From(rel.ID) + o.R.ProfilePictureFile = rel + } } // BuildSetter returns an *models.UserSetter @@ -142,17 +149,14 @@ func (o UserTemplate) BuildSetter() *models.UserSetter { m.ID = omit.From(o.ID()) } if o.Username != nil { - m.Username = omitnull.FromNull(o.Username()) + m.Username = omit.From(o.Username()) } if o.Password != nil { - m.Password = omitnull.FromNull(o.Password()) + m.Password = omit.From(o.Password()) } if o.ProfilePictureID != nil { m.ProfilePictureID = omitnull.FromNull(o.ProfilePictureID()) } - if o.Challenge != nil { - m.Challenge = omitnull.FromNull(o.Challenge()) - } return m } @@ -193,6 +197,12 @@ func (o UserTemplate) BuildMany(number int) models.UserSlice { } func ensureCreatableUser(m *models.UserSetter) { + if m.Username.IsUnset() { + m.Username = omit.From(random_string(nil)) + } + if m.Password.IsUnset() { + m.Password = omit.From(random_string(nil)) + } } // insertOptRels creates and inserts any optional the relationships on *models.User @@ -231,6 +241,18 @@ func (o *UserTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m * } } + if o.r.ProfilePictureFile != nil { + var rel2 *models.File + ctx, rel2, err = o.r.ProfilePictureFile.o.create(ctx, exec) + if err != nil { + return ctx, err + } + err = m.AttachProfilePictureFile(ctx, exec, rel2) + if err != nil { + return ctx, err + } + } + return ctx, err } @@ -342,19 +364,18 @@ func (m userMods) RandomizeAllColumns(f *faker.Faker) UserMod { UserMods.RandomUsername(f), UserMods.RandomPassword(f), UserMods.RandomProfilePictureID(f), - UserMods.RandomChallenge(f), } } // Set the model columns to this value -func (m userMods) ID(val int32) UserMod { +func (m userMods) ID(val int64) UserMod { return UserModFunc(func(o *UserTemplate) { - o.ID = func() int32 { return val } + o.ID = func() int64 { return val } }) } // Set the Column from the function -func (m userMods) IDFunc(f func() int32) UserMod { +func (m userMods) IDFunc(f func() int64) UserMod { return UserModFunc(func(o *UserTemplate) { o.ID = f }) @@ -371,21 +392,21 @@ func (m userMods) UnsetID() UserMod { // if faker is nil, a default faker is used func (m userMods) RandomID(f *faker.Faker) UserMod { return UserModFunc(func(o *UserTemplate) { - o.ID = func() int32 { - return random_int32(f) + o.ID = func() int64 { + return random_int64(f) } }) } // Set the model columns to this value -func (m userMods) Username(val null.Val[string]) UserMod { +func (m userMods) Username(val string) UserMod { return UserModFunc(func(o *UserTemplate) { - o.Username = func() null.Val[string] { return val } + o.Username = func() string { return val } }) } // Set the Column from the function -func (m userMods) UsernameFunc(f func() null.Val[string]) UserMod { +func (m userMods) UsernameFunc(f func() string) UserMod { return UserModFunc(func(o *UserTemplate) { o.Username = f }) @@ -402,29 +423,21 @@ func (m userMods) UnsetUsername() UserMod { // if faker is nil, a default faker is used func (m userMods) RandomUsername(f *faker.Faker) UserMod { return UserModFunc(func(o *UserTemplate) { - o.Username = func() null.Val[string] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[string](nil) - } - - return null.From(random_string(f)) + o.Username = func() string { + return random_string(f) } }) } // Set the model columns to this value -func (m userMods) Password(val null.Val[string]) UserMod { +func (m userMods) Password(val string) UserMod { return UserModFunc(func(o *UserTemplate) { - o.Password = func() null.Val[string] { return val } + o.Password = func() string { return val } }) } // Set the Column from the function -func (m userMods) PasswordFunc(f func() null.Val[string]) UserMod { +func (m userMods) PasswordFunc(f func() string) UserMod { return UserModFunc(func(o *UserTemplate) { o.Password = f }) @@ -441,29 +454,21 @@ func (m userMods) UnsetPassword() UserMod { // if faker is nil, a default faker is used func (m userMods) RandomPassword(f *faker.Faker) UserMod { return UserModFunc(func(o *UserTemplate) { - o.Password = func() null.Val[string] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[string](nil) - } - - return null.From(random_string(f)) + o.Password = func() string { + return random_string(f) } }) } // Set the model columns to this value -func (m userMods) ProfilePictureID(val null.Val[int32]) UserMod { +func (m userMods) ProfilePictureID(val null.Val[int64]) UserMod { return UserModFunc(func(o *UserTemplate) { - o.ProfilePictureID = func() null.Val[int32] { return val } + o.ProfilePictureID = func() null.Val[int64] { return val } }) } // Set the Column from the function -func (m userMods) ProfilePictureIDFunc(f func() null.Val[int32]) UserMod { +func (m userMods) ProfilePictureIDFunc(f func() null.Val[int64]) UserMod { return UserModFunc(func(o *UserTemplate) { o.ProfilePictureID = f }) @@ -480,59 +485,42 @@ func (m userMods) UnsetProfilePictureID() UserMod { // if faker is nil, a default faker is used func (m userMods) RandomProfilePictureID(f *faker.Faker) UserMod { return UserModFunc(func(o *UserTemplate) { - o.ProfilePictureID = func() null.Val[int32] { + o.ProfilePictureID = func() null.Val[int64] { if f == nil { f = &defaultFaker } if f.Bool() { - return null.FromPtr[int32](nil) + return null.FromPtr[int64](nil) } - return null.From(random_int32(f)) + return null.From(random_int64(f)) } }) } -// Set the model columns to this value -func (m userMods) Challenge(val null.Val[string]) UserMod { +func (m userMods) WithProfilePictureFile(rel *FileTemplate) UserMod { return UserModFunc(func(o *UserTemplate) { - o.Challenge = func() null.Val[string] { return val } - }) -} - -// Set the Column from the function -func (m userMods) ChallengeFunc(f func() null.Val[string]) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Challenge = f - }) -} - -// Clear any values for the column -func (m userMods) UnsetChallenge() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Challenge = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m userMods) RandomChallenge(f *faker.Faker) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Challenge = func() null.Val[string] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[string](nil) - } - - return null.From(random_string(f)) + o.r.ProfilePictureFile = &userRProfilePictureFileR{ + o: rel, } }) } +func (m userMods) WithNewProfilePictureFile(mods ...FileMod) UserMod { + return UserModFunc(func(o *UserTemplate) { + related := o.f.NewFile(mods...) + + m.WithProfilePictureFile(related).Apply(o) + }) +} + +func (m userMods) WithoutProfilePictureFile() UserMod { + return UserModFunc(func(o *UserTemplate) { + o.r.ProfilePictureFile = nil + }) +} + func (m userMods) WithFiles(number int, related *FileTemplate) UserMod { return UserModFunc(func(o *UserTemplate) { o.r.Files = []*userRFilesR{{ diff --git a/server/internal/models/files.bob.go b/server/internal/models/file.bob.go similarity index 69% rename from server/internal/models/files.bob.go rename to server/internal/models/file.bob.go index a987b61..71ea88c 100644 --- a/server/internal/models/files.bob.go +++ b/server/internal/models/file.bob.go @@ -10,7 +10,6 @@ import ( "fmt" "io" - "github.com/aarondl/opt/null" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" "github.com/stephenafamo/bob" @@ -26,10 +25,10 @@ import ( // File is an object representing the database table. type File struct { - ID int32 `db:"id,pk" ` - Name null.Val[string] `db:"name" ` - Data null.Val[[]byte] `db:"data" ` - UserID null.Val[int32] `db:"user_id" ` + ID int64 `db:"id,pk" ` + Name string `db:"name" ` + Data []byte `db:"data" ` + UserID int64 `db:"user_id" ` R fileR `db:"-" ` } @@ -38,15 +37,16 @@ type File struct { // This should almost always be used instead of []*File. type FileSlice []*File -// Files contains methods to work with the files table -var Files = sqlite.NewTablex[*File, FileSlice, *FileSetter]("", "files") +// Files contains methods to work with the file table +var Files = sqlite.NewTablex[*File, FileSlice, *FileSetter]("", "file") -// FilesQuery is a query on the files table +// FilesQuery is a query on the file table type FilesQuery = *sqlite.ViewQuery[*File, FileSlice] // fileR is where relationships are stored. type fileR struct { - User *User // fk_files_0 + User *User // fk_file_0 + ProfilePictureUsers UserSlice // fk_user_0 } type fileColumnNames struct { @@ -56,7 +56,7 @@ type fileColumnNames struct { UserID string } -var FileColumns = buildFileColumns("files") +var FileColumns = buildFileColumns("file") type fileColumns struct { tableAlias string @@ -85,10 +85,10 @@ func buildFileColumns(alias string) fileColumns { } type fileWhere[Q sqlite.Filterable] struct { - ID sqlite.WhereMod[Q, int32] - Name sqlite.WhereNullMod[Q, string] - Data sqlite.WhereNullMod[Q, []byte] - UserID sqlite.WhereNullMod[Q, int32] + ID sqlite.WhereMod[Q, int64] + Name sqlite.WhereMod[Q, string] + Data sqlite.WhereMod[Q, []byte] + UserID sqlite.WhereMod[Q, int64] } func (fileWhere[Q]) AliasedAs(alias string) fileWhere[Q] { @@ -97,29 +97,29 @@ func (fileWhere[Q]) AliasedAs(alias string) fileWhere[Q] { func buildFileWhere[Q sqlite.Filterable](cols fileColumns) fileWhere[Q] { return fileWhere[Q]{ - ID: sqlite.Where[Q, int32](cols.ID), - Name: sqlite.WhereNull[Q, string](cols.Name), - Data: sqlite.WhereNull[Q, []byte](cols.Data), - UserID: sqlite.WhereNull[Q, int32](cols.UserID), + ID: sqlite.Where[Q, int64](cols.ID), + Name: sqlite.Where[Q, string](cols.Name), + Data: sqlite.Where[Q, []byte](cols.Data), + UserID: sqlite.Where[Q, int64](cols.UserID), } } var FileErrors = &fileErrors{ - ErrUniquePkMainFiles: &UniqueConstraintError{s: "pk_main_files"}, + ErrUniquePkMainFile: &UniqueConstraintError{s: "pk_main_file"}, } type fileErrors struct { - ErrUniquePkMainFiles *UniqueConstraintError + ErrUniquePkMainFile *UniqueConstraintError } // FileSetter is used for insert/upsert/update operations // All values are optional, and do not have to be set // Generated columns are not included type FileSetter struct { - ID omit.Val[int32] `db:"id,pk" ` - Name omitnull.Val[string] `db:"name" ` - Data omitnull.Val[[]byte] `db:"data" ` - UserID omitnull.Val[int32] `db:"user_id" ` + ID omit.Val[int64] `db:"id,pk" ` + Name omit.Val[string] `db:"name" ` + Data omit.Val[[]byte] `db:"data" ` + UserID omit.Val[int64] `db:"user_id" ` } func (s FileSetter) SetColumns() []string { @@ -148,13 +148,13 @@ func (s FileSetter) Overwrite(t *File) { t.ID, _ = s.ID.Get() } if !s.Name.IsUnset() { - t.Name, _ = s.Name.GetNull() + t.Name, _ = s.Name.Get() } if !s.Data.IsUnset() { - t.Data, _ = s.Data.GetNull() + t.Data, _ = s.Data.Get() } if !s.UserID.IsUnset() { - t.UserID, _ = s.UserID.GetNull() + t.UserID, _ = s.UserID.Get() } } @@ -229,7 +229,7 @@ func (s FileSetter) Expressions(prefix ...string) []bob.Expression { // FindFile retrieves a single record by primary key // If cols is empty Find will return all columns. -func FindFile(ctx context.Context, exec bob.Executor, IDPK int32, cols ...string) (*File, error) { +func FindFile(ctx context.Context, exec bob.Executor, IDPK int64, cols ...string) (*File, error) { if len(cols) == 0 { return Files.Query( SelectWhere.Files.ID.EQ(IDPK), @@ -243,7 +243,7 @@ func FindFile(ctx context.Context, exec bob.Executor, IDPK int32, cols ...string } // FileExists checks the presence of a single record by primary key -func FileExists(ctx context.Context, exec bob.Executor, IDPK int32) (bool, error) { +func FileExists(ctx context.Context, exec bob.Executor, IDPK int64) (bool, error) { return Files.Query( SelectWhere.Files.ID.EQ(IDPK), ).Exists(ctx, exec) @@ -273,7 +273,7 @@ func (o *File) PrimaryKeyVals() bob.Expression { } func (o *File) pkEQ() dialect.Expression { - return sqlite.Quote("files", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { + return sqlite.Quote("file", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start) })) } @@ -334,7 +334,7 @@ func (o FileSlice) pkIN() dialect.Expression { return sqlite.Raw("NULL") } - return sqlite.Quote("files", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { + return sqlite.Quote("file", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { pkPairs := make([]bob.Expression, len(o)) for i, row := range o { pkPairs[i] = row.PrimaryKeyVals() @@ -451,8 +451,9 @@ func (o FileSlice) ReloadAll(ctx context.Context, exec bob.Executor) error { } type fileJoins[Q dialect.Joinable] struct { - typ string - User func(context.Context) modAs[Q, userColumns] + typ string + User func(context.Context) modAs[Q, userColumns] + ProfilePictureUsers func(context.Context) modAs[Q, userColumns] } func (j fileJoins[Q]) aliasedAs(alias string) fileJoins[Q] { @@ -461,8 +462,9 @@ func (j fileJoins[Q]) aliasedAs(alias string) fileJoins[Q] { func buildFileJoins[Q dialect.Joinable](cols fileColumns, typ string) fileJoins[Q] { return fileJoins[Q]{ - typ: typ, - User: filesJoinUser[Q](cols, typ), + typ: typ, + User: filesJoinUser[Q](cols, typ), + ProfilePictureUsers: filesJoinProfilePictureUsers[Q](cols, typ), } } @@ -485,7 +487,26 @@ func filesJoinUser[Q dialect.Joinable](from fileColumns, typ string) func(contex } } -// User starts a query for related objects on users +func filesJoinProfilePictureUsers[Q dialect.Joinable](from fileColumns, typ string) func(context.Context) modAs[Q, userColumns] { + return func(ctx context.Context) modAs[Q, userColumns] { + return modAs[Q, userColumns]{ + c: UserColumns, + f: func(to userColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, Users.Name().As(to.Alias())).On( + to.ProfilePictureID.EQ(from.ID), + )) + } + + return mods + }, + } + } +} + +// User starts a query for related objects on user func (o *File) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { return Users.Query(append(mods, sm.Where(UserColumns.ID.EQ(sqlite.Arg(o.UserID))), @@ -503,6 +524,24 @@ func (os FileSlice) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { )...) } +// ProfilePictureUsers starts a query for related objects on user +func (o *File) ProfilePictureUsers(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { + return Users.Query(append(mods, + sm.Where(UserColumns.ProfilePictureID.EQ(sqlite.Arg(o.ID))), + )...) +} + +func (os FileSlice) ProfilePictureUsers(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { + PKArgs := make([]bob.Expression, len(os)) + for i, o := range os { + PKArgs[i] = sqlite.ArgGroup(o.ID) + } + + return Users.Query(append(mods, + sm.Where(sqlite.Group(UserColumns.ProfilePictureID).In(PKArgs...)), + )...) +} + func (o *File) Preload(name string, retrieved any) error { if o == nil { return nil @@ -521,6 +560,20 @@ func (o *File) Preload(name string, retrieved any) error { rel.R.Files = FileSlice{o} } return nil + case "ProfilePictureUsers": + rels, ok := retrieved.(UserSlice) + if !ok { + return fmt.Errorf("file cannot load %T as %q", retrieved, name) + } + + o.R.ProfilePictureUsers = rels + + for _, rel := range rels { + if rel != nil { + rel.R.ProfilePictureFile = o + } + } + return nil default: return fmt.Errorf("file has no relationship %q", name) } @@ -597,7 +650,7 @@ func (os FileSlice) LoadFileUser(ctx context.Context, exec bob.Executor, mods .. for _, o := range os { for _, rel := range users { - if o.UserID.GetOrZero() != rel.ID { + if o.UserID != rel.ID { continue } @@ -611,9 +664,81 @@ func (os FileSlice) LoadFileUser(ctx context.Context, exec bob.Executor, mods .. return nil } +func ThenLoadFileProfilePictureUsers(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { + return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { + loader, isLoader := retrieved.(interface { + LoadFileProfilePictureUsers(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + }) + if !isLoader { + return fmt.Errorf("object %T cannot load FileProfilePictureUsers", retrieved) + } + + err := loader.LoadFileProfilePictureUsers(ctx, exec, queryMods...) + + // Don't cause an issue due to missing relationships + if errors.Is(err, sql.ErrNoRows) { + return nil + } + + return err + }) +} + +// LoadFileProfilePictureUsers loads the file's ProfilePictureUsers into the .R struct +func (o *File) LoadFileProfilePictureUsers(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.ProfilePictureUsers = nil + + related, err := o.ProfilePictureUsers(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, rel := range related { + rel.R.ProfilePictureFile = o + } + + o.R.ProfilePictureUsers = related + return nil +} + +// LoadFileProfilePictureUsers loads the file's ProfilePictureUsers into the .R struct +func (os FileSlice) LoadFileProfilePictureUsers(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + users, err := os.ProfilePictureUsers(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + o.R.ProfilePictureUsers = nil + } + + for _, o := range os { + for _, rel := range users { + if o.ID != rel.ProfilePictureID.GetOrZero() { + continue + } + + rel.R.ProfilePictureFile = o + + o.R.ProfilePictureUsers = append(o.R.ProfilePictureUsers, rel) + } + } + + return nil +} + func attachFileUser0(ctx context.Context, exec bob.Executor, count int, file0 *File, user1 *User) (*File, error) { setter := &FileSetter{ - UserID: omitnull.From(user1.ID), + UserID: omit.From(user1.ID), } err := file0.Update(ctx, exec, setter) @@ -656,3 +781,71 @@ func (file0 *File) AttachUser(ctx context.Context, exec bob.Executor, user1 *Use return nil } + +func insertFileProfilePictureUsers0(ctx context.Context, exec bob.Executor, users1 []*UserSetter, file0 *File) (UserSlice, error) { + for i := range users1 { + users1[i].ProfilePictureID = omitnull.From(file0.ID) + } + + ret, err := Users.Insert(bob.ToMods(users1...)).All(ctx, exec) + if err != nil { + return ret, fmt.Errorf("insertFileProfilePictureUsers0: %w", err) + } + + return ret, nil +} + +func attachFileProfilePictureUsers0(ctx context.Context, exec bob.Executor, count int, users1 UserSlice, file0 *File) (UserSlice, error) { + setter := &UserSetter{ + ProfilePictureID: omitnull.From(file0.ID), + } + + err := users1.UpdateAll(ctx, exec, *setter) + if err != nil { + return nil, fmt.Errorf("attachFileProfilePictureUsers0: %w", err) + } + + return users1, nil +} + +func (file0 *File) InsertProfilePictureUsers(ctx context.Context, exec bob.Executor, related ...*UserSetter) error { + if len(related) == 0 { + return nil + } + + var err error + + users1, err := insertFileProfilePictureUsers0(ctx, exec, related, file0) + if err != nil { + return err + } + + file0.R.ProfilePictureUsers = append(file0.R.ProfilePictureUsers, users1...) + + for _, rel := range users1 { + rel.R.ProfilePictureFile = file0 + } + return nil +} + +func (file0 *File) AttachProfilePictureUsers(ctx context.Context, exec bob.Executor, related ...*User) error { + if len(related) == 0 { + return nil + } + + var err error + users1 := UserSlice(related) + + _, err = attachFileProfilePictureUsers0(ctx, exec, len(related), users1, file0) + if err != nil { + return err + } + + file0.R.ProfilePictureUsers = append(file0.R.ProfilePictureUsers, users1...) + + for _, rel := range related { + rel.R.ProfilePictureFile = file0 + } + + return nil +} diff --git a/server/internal/models/items.bob.go b/server/internal/models/item.bob.go similarity index 88% rename from server/internal/models/items.bob.go rename to server/internal/models/item.bob.go index f5ba27f..8ff8664 100644 --- a/server/internal/models/items.bob.go +++ b/server/internal/models/item.bob.go @@ -27,13 +27,13 @@ import ( // Item is an object representing the database table. type Item struct { - ID int32 `db:"id,pk" ` - Name null.Val[string] `db:"name" ` - Description null.Val[string] `db:"description" ` - Price null.Val[float32] `db:"price" ` - Quantity null.Val[int32] `db:"quantity" ` - Added null.Val[time.Time] `db:"added" ` - UserID null.Val[int32] `db:"user_id" ` + ID int64 `db:"id,pk" ` + Name string `db:"name" ` + Added time.Time `db:"added" ` + Description null.Val[string] `db:"description" ` + Price null.Val[float32] `db:"price" ` + Quantity null.Val[int64] `db:"quantity" ` + UserID int64 `db:"user_id" ` R itemR `db:"-" ` } @@ -42,37 +42,37 @@ type Item struct { // This should almost always be used instead of []*Item. type ItemSlice []*Item -// Items contains methods to work with the items table -var Items = sqlite.NewTablex[*Item, ItemSlice, *ItemSetter]("", "items") +// Items contains methods to work with the item table +var Items = sqlite.NewTablex[*Item, ItemSlice, *ItemSetter]("", "item") -// ItemsQuery is a query on the items table +// ItemsQuery is a query on the item table type ItemsQuery = *sqlite.ViewQuery[*Item, ItemSlice] // itemR is where relationships are stored. type itemR struct { - User *User // fk_items_0 + User *User // fk_item_0 } type itemColumnNames struct { ID string Name string + Added string Description string Price string Quantity string - Added string UserID string } -var ItemColumns = buildItemColumns("items") +var ItemColumns = buildItemColumns("item") type itemColumns struct { tableAlias string ID sqlite.Expression Name sqlite.Expression + Added sqlite.Expression Description sqlite.Expression Price sqlite.Expression Quantity sqlite.Expression - Added sqlite.Expression UserID sqlite.Expression } @@ -89,22 +89,22 @@ func buildItemColumns(alias string) itemColumns { tableAlias: alias, ID: sqlite.Quote(alias, "id"), Name: sqlite.Quote(alias, "name"), + Added: sqlite.Quote(alias, "added"), Description: sqlite.Quote(alias, "description"), Price: sqlite.Quote(alias, "price"), Quantity: sqlite.Quote(alias, "quantity"), - Added: sqlite.Quote(alias, "added"), UserID: sqlite.Quote(alias, "user_id"), } } type itemWhere[Q sqlite.Filterable] struct { - ID sqlite.WhereMod[Q, int32] - Name sqlite.WhereNullMod[Q, string] + ID sqlite.WhereMod[Q, int64] + Name sqlite.WhereMod[Q, string] + Added sqlite.WhereMod[Q, time.Time] Description sqlite.WhereNullMod[Q, string] Price sqlite.WhereNullMod[Q, float32] - Quantity sqlite.WhereNullMod[Q, int32] - Added sqlite.WhereNullMod[Q, time.Time] - UserID sqlite.WhereNullMod[Q, int32] + Quantity sqlite.WhereNullMod[Q, int64] + UserID sqlite.WhereMod[Q, int64] } func (itemWhere[Q]) AliasedAs(alias string) itemWhere[Q] { @@ -113,35 +113,35 @@ func (itemWhere[Q]) AliasedAs(alias string) itemWhere[Q] { func buildItemWhere[Q sqlite.Filterable](cols itemColumns) itemWhere[Q] { return itemWhere[Q]{ - ID: sqlite.Where[Q, int32](cols.ID), - Name: sqlite.WhereNull[Q, string](cols.Name), + ID: sqlite.Where[Q, int64](cols.ID), + Name: sqlite.Where[Q, string](cols.Name), + Added: sqlite.Where[Q, time.Time](cols.Added), Description: sqlite.WhereNull[Q, string](cols.Description), Price: sqlite.WhereNull[Q, float32](cols.Price), - Quantity: sqlite.WhereNull[Q, int32](cols.Quantity), - Added: sqlite.WhereNull[Q, time.Time](cols.Added), - UserID: sqlite.WhereNull[Q, int32](cols.UserID), + Quantity: sqlite.WhereNull[Q, int64](cols.Quantity), + UserID: sqlite.Where[Q, int64](cols.UserID), } } var ItemErrors = &itemErrors{ - ErrUniquePkMainItems: &UniqueConstraintError{s: "pk_main_items"}, + ErrUniquePkMainItem: &UniqueConstraintError{s: "pk_main_item"}, } type itemErrors struct { - ErrUniquePkMainItems *UniqueConstraintError + ErrUniquePkMainItem *UniqueConstraintError } // ItemSetter is used for insert/upsert/update operations // All values are optional, and do not have to be set // Generated columns are not included type ItemSetter struct { - ID omit.Val[int32] `db:"id,pk" ` - Name omitnull.Val[string] `db:"name" ` - Description omitnull.Val[string] `db:"description" ` - Price omitnull.Val[float32] `db:"price" ` - Quantity omitnull.Val[int32] `db:"quantity" ` - Added omitnull.Val[time.Time] `db:"added" ` - UserID omitnull.Val[int32] `db:"user_id" ` + ID omit.Val[int64] `db:"id,pk" ` + Name omit.Val[string] `db:"name" ` + Added omit.Val[time.Time] `db:"added" ` + Description omitnull.Val[string] `db:"description" ` + Price omitnull.Val[float32] `db:"price" ` + Quantity omitnull.Val[int64] `db:"quantity" ` + UserID omit.Val[int64] `db:"user_id" ` } func (s ItemSetter) SetColumns() []string { @@ -154,6 +154,10 @@ func (s ItemSetter) SetColumns() []string { vals = append(vals, "name") } + if !s.Added.IsUnset() { + vals = append(vals, "added") + } + if !s.Description.IsUnset() { vals = append(vals, "description") } @@ -166,10 +170,6 @@ func (s ItemSetter) SetColumns() []string { vals = append(vals, "quantity") } - if !s.Added.IsUnset() { - vals = append(vals, "added") - } - if !s.UserID.IsUnset() { vals = append(vals, "user_id") } @@ -182,7 +182,10 @@ func (s ItemSetter) Overwrite(t *Item) { t.ID, _ = s.ID.Get() } if !s.Name.IsUnset() { - t.Name, _ = s.Name.GetNull() + t.Name, _ = s.Name.Get() + } + if !s.Added.IsUnset() { + t.Added, _ = s.Added.Get() } if !s.Description.IsUnset() { t.Description, _ = s.Description.GetNull() @@ -193,11 +196,8 @@ func (s ItemSetter) Overwrite(t *Item) { if !s.Quantity.IsUnset() { t.Quantity, _ = s.Quantity.GetNull() } - if !s.Added.IsUnset() { - t.Added, _ = s.Added.GetNull() - } if !s.UserID.IsUnset() { - t.UserID, _ = s.UserID.GetNull() + t.UserID, _ = s.UserID.Get() } } @@ -220,6 +220,10 @@ func (s *ItemSetter) Apply(q *dialect.InsertQuery) { vals = append(vals, sqlite.Arg(s.Name)) } + if !s.Added.IsUnset() { + vals = append(vals, sqlite.Arg(s.Added)) + } + if !s.Description.IsUnset() { vals = append(vals, sqlite.Arg(s.Description)) } @@ -232,10 +236,6 @@ func (s *ItemSetter) Apply(q *dialect.InsertQuery) { vals = append(vals, sqlite.Arg(s.Quantity)) } - if !s.Added.IsUnset() { - vals = append(vals, sqlite.Arg(s.Added)) - } - if !s.UserID.IsUnset() { vals = append(vals, sqlite.Arg(s.UserID)) } @@ -265,6 +265,13 @@ func (s ItemSetter) Expressions(prefix ...string) []bob.Expression { }}) } + if !s.Added.IsUnset() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + sqlite.Quote(append(prefix, "added")...), + sqlite.Arg(s.Added), + }}) + } + if !s.Description.IsUnset() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "description")...), @@ -286,13 +293,6 @@ func (s ItemSetter) Expressions(prefix ...string) []bob.Expression { }}) } - if !s.Added.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "added")...), - sqlite.Arg(s.Added), - }}) - } - if !s.UserID.IsUnset() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ sqlite.Quote(append(prefix, "user_id")...), @@ -305,7 +305,7 @@ func (s ItemSetter) Expressions(prefix ...string) []bob.Expression { // FindItem retrieves a single record by primary key // If cols is empty Find will return all columns. -func FindItem(ctx context.Context, exec bob.Executor, IDPK int32, cols ...string) (*Item, error) { +func FindItem(ctx context.Context, exec bob.Executor, IDPK int64, cols ...string) (*Item, error) { if len(cols) == 0 { return Items.Query( SelectWhere.Items.ID.EQ(IDPK), @@ -319,7 +319,7 @@ func FindItem(ctx context.Context, exec bob.Executor, IDPK int32, cols ...string } // ItemExists checks the presence of a single record by primary key -func ItemExists(ctx context.Context, exec bob.Executor, IDPK int32) (bool, error) { +func ItemExists(ctx context.Context, exec bob.Executor, IDPK int64) (bool, error) { return Items.Query( SelectWhere.Items.ID.EQ(IDPK), ).Exists(ctx, exec) @@ -349,7 +349,7 @@ func (o *Item) PrimaryKeyVals() bob.Expression { } func (o *Item) pkEQ() dialect.Expression { - return sqlite.Quote("items", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { + return sqlite.Quote("item", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start) })) } @@ -410,7 +410,7 @@ func (o ItemSlice) pkIN() dialect.Expression { return sqlite.Raw("NULL") } - return sqlite.Quote("items", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { + return sqlite.Quote("item", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { pkPairs := make([]bob.Expression, len(o)) for i, row := range o { pkPairs[i] = row.PrimaryKeyVals() @@ -561,7 +561,7 @@ func itemsJoinUser[Q dialect.Joinable](from itemColumns, typ string) func(contex } } -// User starts a query for related objects on users +// User starts a query for related objects on user func (o *Item) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { return Users.Query(append(mods, sm.Where(UserColumns.ID.EQ(sqlite.Arg(o.UserID))), @@ -673,7 +673,7 @@ func (os ItemSlice) LoadItemUser(ctx context.Context, exec bob.Executor, mods .. for _, o := range os { for _, rel := range users { - if o.UserID.GetOrZero() != rel.ID { + if o.UserID != rel.ID { continue } @@ -689,7 +689,7 @@ func (os ItemSlice) LoadItemUser(ctx context.Context, exec bob.Executor, mods .. func attachItemUser0(ctx context.Context, exec bob.Executor, count int, item0 *Item, user1 *User) (*Item, error) { setter := &ItemSetter{ - UserID: omitnull.From(user1.ID), + UserID: omit.From(user1.ID), } err := item0.Update(ctx, exec, setter) diff --git a/server/internal/models/users.bob.go b/server/internal/models/user.bob.go similarity index 72% rename from server/internal/models/users.bob.go rename to server/internal/models/user.bob.go index d415dd3..671af98 100644 --- a/server/internal/models/users.bob.go +++ b/server/internal/models/user.bob.go @@ -21,15 +21,15 @@ import ( "github.com/stephenafamo/bob/dialect/sqlite/um" "github.com/stephenafamo/bob/expr" "github.com/stephenafamo/bob/mods" + "github.com/stephenafamo/bob/orm" ) // User is an object representing the database table. type User struct { - ID int32 `db:"id,pk" ` - Username null.Val[string] `db:"username" ` - Password null.Val[string] `db:"password" ` - ProfilePictureID null.Val[int32] `db:"profile_picture_id" ` - Challenge null.Val[string] `db:"challenge" ` + ID int64 `db:"id,pk" ` + Username string `db:"username" ` + Password string `db:"password" ` + ProfilePictureID null.Val[int64] `db:"profile_picture_id" ` R userR `db:"-" ` } @@ -38,16 +38,17 @@ type User struct { // This should almost always be used instead of []*User. type UserSlice []*User -// Users contains methods to work with the users table -var Users = sqlite.NewTablex[*User, UserSlice, *UserSetter]("", "users") +// Users contains methods to work with the user table +var Users = sqlite.NewTablex[*User, UserSlice, *UserSetter]("", "user") -// UsersQuery is a query on the users table +// UsersQuery is a query on the user table type UsersQuery = *sqlite.ViewQuery[*User, UserSlice] // userR is where relationships are stored. type userR struct { - Files FileSlice // fk_files_0 - Items ItemSlice // fk_items_0 + Files FileSlice // fk_file_0 + Items ItemSlice // fk_item_0 + ProfilePictureFile *File // fk_user_0 } type userColumnNames struct { @@ -55,10 +56,9 @@ type userColumnNames struct { Username string Password string ProfilePictureID string - Challenge string } -var UserColumns = buildUserColumns("users") +var UserColumns = buildUserColumns("user") type userColumns struct { tableAlias string @@ -66,7 +66,6 @@ type userColumns struct { Username sqlite.Expression Password sqlite.Expression ProfilePictureID sqlite.Expression - Challenge sqlite.Expression } func (c userColumns) Alias() string { @@ -84,16 +83,14 @@ func buildUserColumns(alias string) userColumns { Username: sqlite.Quote(alias, "username"), Password: sqlite.Quote(alias, "password"), ProfilePictureID: sqlite.Quote(alias, "profile_picture_id"), - Challenge: sqlite.Quote(alias, "challenge"), } } type userWhere[Q sqlite.Filterable] struct { - ID sqlite.WhereMod[Q, int32] - Username sqlite.WhereNullMod[Q, string] - Password sqlite.WhereNullMod[Q, string] - ProfilePictureID sqlite.WhereNullMod[Q, int32] - Challenge sqlite.WhereNullMod[Q, string] + ID sqlite.WhereMod[Q, int64] + Username sqlite.WhereMod[Q, string] + Password sqlite.WhereMod[Q, string] + ProfilePictureID sqlite.WhereNullMod[Q, int64] } func (userWhere[Q]) AliasedAs(alias string) userWhere[Q] { @@ -102,35 +99,33 @@ func (userWhere[Q]) AliasedAs(alias string) userWhere[Q] { func buildUserWhere[Q sqlite.Filterable](cols userColumns) userWhere[Q] { return userWhere[Q]{ - ID: sqlite.Where[Q, int32](cols.ID), - Username: sqlite.WhereNull[Q, string](cols.Username), - Password: sqlite.WhereNull[Q, string](cols.Password), - ProfilePictureID: sqlite.WhereNull[Q, int32](cols.ProfilePictureID), - Challenge: sqlite.WhereNull[Q, string](cols.Challenge), + ID: sqlite.Where[Q, int64](cols.ID), + Username: sqlite.Where[Q, string](cols.Username), + Password: sqlite.Where[Q, string](cols.Password), + ProfilePictureID: sqlite.WhereNull[Q, int64](cols.ProfilePictureID), } } var UserErrors = &userErrors{ - ErrUniquePkMainUsers: &UniqueConstraintError{s: "pk_main_users"}, + ErrUniquePkMainUser: &UniqueConstraintError{s: "pk_main_user"}, } type userErrors struct { - ErrUniquePkMainUsers *UniqueConstraintError + ErrUniquePkMainUser *UniqueConstraintError } // UserSetter is used for insert/upsert/update operations // All values are optional, and do not have to be set // Generated columns are not included type UserSetter struct { - ID omit.Val[int32] `db:"id,pk" ` - Username omitnull.Val[string] `db:"username" ` - Password omitnull.Val[string] `db:"password" ` - ProfilePictureID omitnull.Val[int32] `db:"profile_picture_id" ` - Challenge omitnull.Val[string] `db:"challenge" ` + ID omit.Val[int64] `db:"id,pk" ` + Username omit.Val[string] `db:"username" ` + Password omit.Val[string] `db:"password" ` + ProfilePictureID omitnull.Val[int64] `db:"profile_picture_id" ` } func (s UserSetter) SetColumns() []string { - vals := make([]string, 0, 5) + vals := make([]string, 0, 4) if !s.ID.IsUnset() { vals = append(vals, "id") } @@ -147,10 +142,6 @@ func (s UserSetter) SetColumns() []string { vals = append(vals, "profile_picture_id") } - if !s.Challenge.IsUnset() { - vals = append(vals, "challenge") - } - return vals } @@ -159,17 +150,14 @@ func (s UserSetter) Overwrite(t *User) { t.ID, _ = s.ID.Get() } if !s.Username.IsUnset() { - t.Username, _ = s.Username.GetNull() + t.Username, _ = s.Username.Get() } if !s.Password.IsUnset() { - t.Password, _ = s.Password.GetNull() + t.Password, _ = s.Password.Get() } if !s.ProfilePictureID.IsUnset() { t.ProfilePictureID, _ = s.ProfilePictureID.GetNull() } - if !s.Challenge.IsUnset() { - t.Challenge, _ = s.Challenge.GetNull() - } } func (s *UserSetter) Apply(q *dialect.InsertQuery) { @@ -182,7 +170,7 @@ func (s *UserSetter) Apply(q *dialect.InsertQuery) { } q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 0, 5) + vals := make([]bob.Expression, 0, 4) if !s.ID.IsUnset() { vals = append(vals, sqlite.Arg(s.ID)) } @@ -199,10 +187,6 @@ func (s *UserSetter) Apply(q *dialect.InsertQuery) { vals = append(vals, sqlite.Arg(s.ProfilePictureID)) } - if !s.Challenge.IsUnset() { - vals = append(vals, sqlite.Arg(s.Challenge)) - } - return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -212,7 +196,7 @@ func (s UserSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s UserSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 5) + exprs := make([]bob.Expression, 0, 4) if !s.ID.IsUnset() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -242,19 +226,12 @@ func (s UserSetter) Expressions(prefix ...string) []bob.Expression { }}) } - if !s.Challenge.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "challenge")...), - sqlite.Arg(s.Challenge), - }}) - } - return exprs } // FindUser retrieves a single record by primary key // If cols is empty Find will return all columns. -func FindUser(ctx context.Context, exec bob.Executor, IDPK int32, cols ...string) (*User, error) { +func FindUser(ctx context.Context, exec bob.Executor, IDPK int64, cols ...string) (*User, error) { if len(cols) == 0 { return Users.Query( SelectWhere.Users.ID.EQ(IDPK), @@ -268,7 +245,7 @@ func FindUser(ctx context.Context, exec bob.Executor, IDPK int32, cols ...string } // UserExists checks the presence of a single record by primary key -func UserExists(ctx context.Context, exec bob.Executor, IDPK int32) (bool, error) { +func UserExists(ctx context.Context, exec bob.Executor, IDPK int64) (bool, error) { return Users.Query( SelectWhere.Users.ID.EQ(IDPK), ).Exists(ctx, exec) @@ -298,7 +275,7 @@ func (o *User) PrimaryKeyVals() bob.Expression { } func (o *User) pkEQ() dialect.Expression { - return sqlite.Quote("users", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { + return sqlite.Quote("user", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start) })) } @@ -359,7 +336,7 @@ func (o UserSlice) pkIN() dialect.Expression { return sqlite.Raw("NULL") } - return sqlite.Quote("users", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { + return sqlite.Quote("user", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { pkPairs := make([]bob.Expression, len(o)) for i, row := range o { pkPairs[i] = row.PrimaryKeyVals() @@ -476,9 +453,10 @@ func (o UserSlice) ReloadAll(ctx context.Context, exec bob.Executor) error { } type userJoins[Q dialect.Joinable] struct { - typ string - Files func(context.Context) modAs[Q, fileColumns] - Items func(context.Context) modAs[Q, itemColumns] + typ string + Files func(context.Context) modAs[Q, fileColumns] + Items func(context.Context) modAs[Q, itemColumns] + ProfilePictureFile func(context.Context) modAs[Q, fileColumns] } func (j userJoins[Q]) aliasedAs(alias string) userJoins[Q] { @@ -487,9 +465,10 @@ func (j userJoins[Q]) aliasedAs(alias string) userJoins[Q] { func buildUserJoins[Q dialect.Joinable](cols userColumns, typ string) userJoins[Q] { return userJoins[Q]{ - typ: typ, - Files: usersJoinFiles[Q](cols, typ), - Items: usersJoinItems[Q](cols, typ), + typ: typ, + Files: usersJoinFiles[Q](cols, typ), + Items: usersJoinItems[Q](cols, typ), + ProfilePictureFile: usersJoinProfilePictureFile[Q](cols, typ), } } @@ -531,7 +510,26 @@ func usersJoinItems[Q dialect.Joinable](from userColumns, typ string) func(conte } } -// Files starts a query for related objects on files +func usersJoinProfilePictureFile[Q dialect.Joinable](from userColumns, typ string) func(context.Context) modAs[Q, fileColumns] { + return func(ctx context.Context) modAs[Q, fileColumns] { + return modAs[Q, fileColumns]{ + c: FileColumns, + f: func(to fileColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, Files.Name().As(to.Alias())).On( + to.ID.EQ(from.ProfilePictureID), + )) + } + + return mods + }, + } + } +} + +// Files starts a query for related objects on file func (o *User) Files(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { return Files.Query(append(mods, sm.Where(FileColumns.UserID.EQ(sqlite.Arg(o.ID))), @@ -549,7 +547,7 @@ func (os UserSlice) Files(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { )...) } -// Items starts a query for related objects on items +// Items starts a query for related objects on item func (o *User) Items(mods ...bob.Mod[*dialect.SelectQuery]) ItemsQuery { return Items.Query(append(mods, sm.Where(ItemColumns.UserID.EQ(sqlite.Arg(o.ID))), @@ -567,6 +565,24 @@ func (os UserSlice) Items(mods ...bob.Mod[*dialect.SelectQuery]) ItemsQuery { )...) } +// ProfilePictureFile starts a query for related objects on file +func (o *User) ProfilePictureFile(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { + return Files.Query(append(mods, + sm.Where(FileColumns.ID.EQ(sqlite.Arg(o.ProfilePictureID))), + )...) +} + +func (os UserSlice) ProfilePictureFile(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { + PKArgs := make([]bob.Expression, len(os)) + for i, o := range os { + PKArgs[i] = sqlite.ArgGroup(o.ProfilePictureID) + } + + return Files.Query(append(mods, + sm.Where(sqlite.Group(FileColumns.ID).In(PKArgs...)), + )...) +} + func (o *User) Preload(name string, retrieved any) error { if o == nil { return nil @@ -601,6 +617,18 @@ func (o *User) Preload(name string, retrieved any) error { } } return nil + case "ProfilePictureFile": + rel, ok := retrieved.(*File) + if !ok { + return fmt.Errorf("user cannot load %T as %q", retrieved, name) + } + + o.R.ProfilePictureFile = rel + + if rel != nil { + rel.R.ProfilePictureUsers = UserSlice{o} + } + return nil default: return fmt.Errorf("user has no relationship %q", name) } @@ -665,7 +693,7 @@ func (os UserSlice) LoadUserFiles(ctx context.Context, exec bob.Executor, mods . for _, o := range os { for _, rel := range files { - if o.ID != rel.UserID.GetOrZero() { + if o.ID != rel.UserID { continue } @@ -737,7 +765,7 @@ func (os UserSlice) LoadUserItems(ctx context.Context, exec bob.Executor, mods . for _, o := range os { for _, rel := range items { - if o.ID != rel.UserID.GetOrZero() { + if o.ID != rel.UserID { continue } @@ -750,9 +778,94 @@ func (os UserSlice) LoadUserItems(ctx context.Context, exec bob.Executor, mods . return nil } +func PreloadUserProfilePictureFile(opts ...sqlite.PreloadOption) sqlite.Preloader { + return sqlite.Preload[*File, FileSlice](orm.Relationship{ + Name: "ProfilePictureFile", + Sides: []orm.RelSide{ + { + From: TableNames.Users, + To: TableNames.Files, + FromColumns: []string{ + ColumnNames.Users.ProfilePictureID, + }, + ToColumns: []string{ + ColumnNames.Files.ID, + }, + }, + }, + }, Files.Columns().Names(), opts...) +} + +func ThenLoadUserProfilePictureFile(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { + return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { + loader, isLoader := retrieved.(interface { + LoadUserProfilePictureFile(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + }) + if !isLoader { + return fmt.Errorf("object %T cannot load UserProfilePictureFile", retrieved) + } + + err := loader.LoadUserProfilePictureFile(ctx, exec, queryMods...) + + // Don't cause an issue due to missing relationships + if errors.Is(err, sql.ErrNoRows) { + return nil + } + + return err + }) +} + +// LoadUserProfilePictureFile loads the user's ProfilePictureFile into the .R struct +func (o *User) LoadUserProfilePictureFile(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.ProfilePictureFile = nil + + related, err := o.ProfilePictureFile(mods...).One(ctx, exec) + if err != nil { + return err + } + + related.R.ProfilePictureUsers = UserSlice{o} + + o.R.ProfilePictureFile = related + return nil +} + +// LoadUserProfilePictureFile loads the user's ProfilePictureFile into the .R struct +func (os UserSlice) LoadUserProfilePictureFile(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + files, err := os.ProfilePictureFile(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + for _, rel := range files { + if o.ProfilePictureID.GetOrZero() != rel.ID { + continue + } + + rel.R.ProfilePictureUsers = append(rel.R.ProfilePictureUsers, o) + + o.R.ProfilePictureFile = rel + break + } + } + + return nil +} + func insertUserFiles0(ctx context.Context, exec bob.Executor, files1 []*FileSetter, user0 *User) (FileSlice, error) { for i := range files1 { - files1[i].UserID = omitnull.From(user0.ID) + files1[i].UserID = omit.From(user0.ID) } ret, err := Files.Insert(bob.ToMods(files1...)).All(ctx, exec) @@ -765,7 +878,7 @@ func insertUserFiles0(ctx context.Context, exec bob.Executor, files1 []*FileSett func attachUserFiles0(ctx context.Context, exec bob.Executor, count int, files1 FileSlice, user0 *User) (FileSlice, error) { setter := &FileSetter{ - UserID: omitnull.From(user0.ID), + UserID: omit.From(user0.ID), } err := files1.UpdateAll(ctx, exec, *setter) @@ -820,7 +933,7 @@ func (user0 *User) AttachFiles(ctx context.Context, exec bob.Executor, related . func insertUserItems0(ctx context.Context, exec bob.Executor, items1 []*ItemSetter, user0 *User) (ItemSlice, error) { for i := range items1 { - items1[i].UserID = omitnull.From(user0.ID) + items1[i].UserID = omit.From(user0.ID) } ret, err := Items.Insert(bob.ToMods(items1...)).All(ctx, exec) @@ -833,7 +946,7 @@ func insertUserItems0(ctx context.Context, exec bob.Executor, items1 []*ItemSett func attachUserItems0(ctx context.Context, exec bob.Executor, count int, items1 ItemSlice, user0 *User) (ItemSlice, error) { setter := &ItemSetter{ - UserID: omitnull.From(user0.ID), + UserID: omit.From(user0.ID), } err := items1.UpdateAll(ctx, exec, *setter) @@ -885,3 +998,49 @@ func (user0 *User) AttachItems(ctx context.Context, exec bob.Executor, related . return nil } + +func attachUserProfilePictureFile0(ctx context.Context, exec bob.Executor, count int, user0 *User, file1 *File) (*User, error) { + setter := &UserSetter{ + ProfilePictureID: omitnull.From(file1.ID), + } + + err := user0.Update(ctx, exec, setter) + if err != nil { + return nil, fmt.Errorf("attachUserProfilePictureFile0: %w", err) + } + + return user0, nil +} + +func (user0 *User) InsertProfilePictureFile(ctx context.Context, exec bob.Executor, related *FileSetter) error { + file1, err := Files.Insert(related).One(ctx, exec) + if err != nil { + return fmt.Errorf("inserting related objects: %w", err) + } + + _, err = attachUserProfilePictureFile0(ctx, exec, 1, user0, file1) + if err != nil { + return err + } + + user0.R.ProfilePictureFile = file1 + + file1.R.ProfilePictureUsers = append(file1.R.ProfilePictureUsers, user0) + + return nil +} + +func (user0 *User) AttachProfilePictureFile(ctx context.Context, exec bob.Executor, file1 *File) error { + var err error + + _, err = attachUserProfilePictureFile0(ctx, exec, 1, user0, file1) + if err != nil { + return err + } + + user0.R.ProfilePictureFile = file1 + + file1.R.ProfilePictureUsers = append(file1.R.ProfilePictureUsers, user0) + + return nil +} diff --git a/server/internal/services/item/v1/item.pb.go b/server/internal/services/item/v1/item.pb.go index fcaadd0..3a27404 100644 --- a/server/internal/services/item/v1/item.pb.go +++ b/server/internal/services/item/v1/item.pb.go @@ -24,11 +24,11 @@ const ( type Item struct { state protoimpl.MessageState `protogen:"open.v1"` - Id *uint32 `protobuf:"varint,1,opt,name=id,proto3,oneof" json:"id,omitempty"` + Id *int64 `protobuf:"varint,1,opt,name=id,proto3,oneof" json:"id,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` Price float32 `protobuf:"fixed32,4,opt,name=price,proto3" json:"price,omitempty"` - Quantity uint32 `protobuf:"varint,5,opt,name=quantity,proto3" json:"quantity,omitempty"` + Quantity int32 `protobuf:"varint,5,opt,name=quantity,proto3" json:"quantity,omitempty"` Added *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=added,proto3,oneof" json:"added,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -64,7 +64,7 @@ func (*Item) Descriptor() ([]byte, []int) { return file_item_v1_item_proto_rawDescGZIP(), []int{0} } -func (x *Item) GetId() uint32 { +func (x *Item) GetId() int64 { if x != nil && x.Id != nil { return *x.Id } @@ -92,7 +92,7 @@ func (x *Item) GetPrice() float32 { return 0 } -func (x *Item) GetQuantity() uint32 { +func (x *Item) GetQuantity() int32 { if x != nil { return x.Quantity } @@ -108,7 +108,7 @@ func (x *Item) GetAdded() *timestamppb.Timestamp { type GetItemRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -143,7 +143,7 @@ func (*GetItemRequest) Descriptor() ([]byte, []int) { return file_item_v1_item_proto_rawDescGZIP(), []int{1} } -func (x *GetItemRequest) GetId() uint32 { +func (x *GetItemRequest) GetId() int64 { if x != nil { return x.Id } @@ -199,8 +199,8 @@ type GetItemsRequest struct { Start *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start,proto3,oneof" json:"start,omitempty"` End *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end,proto3,oneof" json:"end,omitempty"` Filter *string `protobuf:"bytes,3,opt,name=filter,proto3,oneof" json:"filter,omitempty"` - Limit *uint32 `protobuf:"varint,4,opt,name=limit,proto3,oneof" json:"limit,omitempty"` - Offset *uint32 `protobuf:"varint,5,opt,name=offset,proto3,oneof" json:"offset,omitempty"` + Limit *int32 `protobuf:"varint,4,opt,name=limit,proto3,oneof" json:"limit,omitempty"` + Offset *int32 `protobuf:"varint,5,opt,name=offset,proto3,oneof" json:"offset,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -256,14 +256,14 @@ func (x *GetItemsRequest) GetFilter() string { return "" } -func (x *GetItemsRequest) GetLimit() uint32 { +func (x *GetItemsRequest) GetLimit() int32 { if x != nil && x.Limit != nil { return *x.Limit } return 0 } -func (x *GetItemsRequest) GetOffset() uint32 { +func (x *GetItemsRequest) GetOffset() int32 { if x != nil && x.Offset != nil { return *x.Offset } @@ -273,7 +273,7 @@ func (x *GetItemsRequest) GetOffset() uint32 { type GetItemsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Items []*Item `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` - Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -315,7 +315,7 @@ func (x *GetItemsResponse) GetItems() []*Item { return nil } -func (x *GetItemsResponse) GetCount() uint64 { +func (x *GetItemsResponse) GetCount() int64 { if x != nil { return x.Count } @@ -500,7 +500,7 @@ func (x *UpdateItemResponse) GetItem() *Item { type DeleteItemRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -535,7 +535,7 @@ func (*DeleteItemRequest) Descriptor() ([]byte, []int) { return file_item_v1_item_proto_rawDescGZIP(), []int{9} } -func (x *DeleteItemRequest) GetId() uint32 { +func (x *DeleteItemRequest) GetId() int64 { if x != nil { return x.Id } @@ -586,20 +586,20 @@ var file_item_v1_item_proto_rawDesc = string([]byte{ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcb, 0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x13, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, + 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x69, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x22, 0x20, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x34, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x34, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, @@ -613,9 +613,9 @@ var file_item_v1_item_proto_rawDesc = string([]byte{ 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, - 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, + 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x04, 0x52, 0x06, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x09, @@ -624,7 +624,7 @@ var file_item_v1_item_proto_rawDesc = string([]byte{ 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x36, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, + 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x36, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, @@ -640,7 +640,7 @@ var file_item_v1_item_proto_rawDesc = string([]byte{ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xeb, 0x02, 0x0a, 0x0b, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, diff --git a/server/internal/services/user/v1/auth.pb.go b/server/internal/services/user/v1/auth.pb.go index 8ce9fec..761a7a6 100644 --- a/server/internal/services/user/v1/auth.pb.go +++ b/server/internal/services/user/v1/auth.pb.go @@ -285,238 +285,6 @@ func (*LogoutResponse) Descriptor() ([]byte, []int) { return file_user_v1_auth_proto_rawDescGZIP(), []int{5} } -type GetPasskeyIDsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetPasskeyIDsRequest) Reset() { - *x = GetPasskeyIDsRequest{} - mi := &file_user_v1_auth_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetPasskeyIDsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetPasskeyIDsRequest) ProtoMessage() {} - -func (x *GetPasskeyIDsRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_auth_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetPasskeyIDsRequest.ProtoReflect.Descriptor instead. -func (*GetPasskeyIDsRequest) Descriptor() ([]byte, []int) { - return file_user_v1_auth_proto_rawDescGZIP(), []int{6} -} - -func (x *GetPasskeyIDsRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -type GetPasskeyIDsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - PasskeyIds []string `protobuf:"bytes,1,rep,name=passkey_ids,json=passkeyIds,proto3" json:"passkey_ids,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetPasskeyIDsResponse) Reset() { - *x = GetPasskeyIDsResponse{} - mi := &file_user_v1_auth_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetPasskeyIDsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetPasskeyIDsResponse) ProtoMessage() {} - -func (x *GetPasskeyIDsResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_auth_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetPasskeyIDsResponse.ProtoReflect.Descriptor instead. -func (*GetPasskeyIDsResponse) Descriptor() ([]byte, []int) { - return file_user_v1_auth_proto_rawDescGZIP(), []int{7} -} - -func (x *GetPasskeyIDsResponse) GetPasskeyIds() []string { - if x != nil { - return x.PasskeyIds - } - return nil -} - -type BeginPasskeyLoginRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BeginPasskeyLoginRequest) Reset() { - *x = BeginPasskeyLoginRequest{} - mi := &file_user_v1_auth_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BeginPasskeyLoginRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BeginPasskeyLoginRequest) ProtoMessage() {} - -func (x *BeginPasskeyLoginRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_auth_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BeginPasskeyLoginRequest.ProtoReflect.Descriptor instead. -func (*BeginPasskeyLoginRequest) Descriptor() ([]byte, []int) { - return file_user_v1_auth_proto_rawDescGZIP(), []int{8} -} - -type BeginPasskeyLoginResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BeginPasskeyLoginResponse) Reset() { - *x = BeginPasskeyLoginResponse{} - mi := &file_user_v1_auth_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BeginPasskeyLoginResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BeginPasskeyLoginResponse) ProtoMessage() {} - -func (x *BeginPasskeyLoginResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_auth_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BeginPasskeyLoginResponse.ProtoReflect.Descriptor instead. -func (*BeginPasskeyLoginResponse) Descriptor() ([]byte, []int) { - return file_user_v1_auth_proto_rawDescGZIP(), []int{9} -} - -type FinishPasskeyLoginRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FinishPasskeyLoginRequest) Reset() { - *x = FinishPasskeyLoginRequest{} - mi := &file_user_v1_auth_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FinishPasskeyLoginRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FinishPasskeyLoginRequest) ProtoMessage() {} - -func (x *FinishPasskeyLoginRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_auth_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FinishPasskeyLoginRequest.ProtoReflect.Descriptor instead. -func (*FinishPasskeyLoginRequest) Descriptor() ([]byte, []int) { - return file_user_v1_auth_proto_rawDescGZIP(), []int{10} -} - -type FinishPasskeyLoginResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FinishPasskeyLoginResponse) Reset() { - *x = FinishPasskeyLoginResponse{} - mi := &file_user_v1_auth_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FinishPasskeyLoginResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FinishPasskeyLoginResponse) ProtoMessage() {} - -func (x *FinishPasskeyLoginResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_auth_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FinishPasskeyLoginResponse.ProtoReflect.Descriptor instead. -func (*FinishPasskeyLoginResponse) Descriptor() ([]byte, []int) { - return file_user_v1_auth_proto_rawDescGZIP(), []int{11} -} - var File_user_v1_auth_proto protoreflect.FileDescriptor var file_user_v1_auth_proto_rawDesc = string([]byte{ @@ -539,61 +307,30 @@ var file_user_v1_auth_proto_rawDesc = string([]byte{ 0x22, 0x10, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x10, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, - 0x6b, 0x65, 0x79, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x38, 0x0a, 0x15, 0x47, 0x65, 0x74, - 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, - 0x49, 0x64, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, - 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x1b, 0x0a, 0x19, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, - 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1c, 0x0a, 0x1a, 0x46, 0x69, 0x6e, - 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd2, 0x03, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x12, 0x15, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x12, 0x16, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, - 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, - 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x73, 0x12, 0x1d, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, - 0x79, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, - 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, - 0x11, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x12, 0x21, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x65, 0x67, - 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x12, 0x46, - 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x12, 0x22, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x69, - 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, - 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x41, 0x75, - 0x74, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, - 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, - 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, - 0x31, 0xca, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, - 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0xea, 0x02, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc1, 0x01, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x15, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x3b, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, + 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, + 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, + 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, + 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, + 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, + 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, 0x5c, + 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, }) var ( @@ -608,39 +345,27 @@ func file_user_v1_auth_proto_rawDescGZIP() []byte { return file_user_v1_auth_proto_rawDescData } -var file_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_user_v1_auth_proto_goTypes = []any{ - (*LoginRequest)(nil), // 0: user.v1.LoginRequest - (*LoginResponse)(nil), // 1: user.v1.LoginResponse - (*SignUpRequest)(nil), // 2: user.v1.SignUpRequest - (*SignUpResponse)(nil), // 3: user.v1.SignUpResponse - (*LogoutRequest)(nil), // 4: user.v1.LogoutRequest - (*LogoutResponse)(nil), // 5: user.v1.LogoutResponse - (*GetPasskeyIDsRequest)(nil), // 6: user.v1.GetPasskeyIDsRequest - (*GetPasskeyIDsResponse)(nil), // 7: user.v1.GetPasskeyIDsResponse - (*BeginPasskeyLoginRequest)(nil), // 8: user.v1.BeginPasskeyLoginRequest - (*BeginPasskeyLoginResponse)(nil), // 9: user.v1.BeginPasskeyLoginResponse - (*FinishPasskeyLoginRequest)(nil), // 10: user.v1.FinishPasskeyLoginRequest - (*FinishPasskeyLoginResponse)(nil), // 11: user.v1.FinishPasskeyLoginResponse + (*LoginRequest)(nil), // 0: user.v1.LoginRequest + (*LoginResponse)(nil), // 1: user.v1.LoginResponse + (*SignUpRequest)(nil), // 2: user.v1.SignUpRequest + (*SignUpResponse)(nil), // 3: user.v1.SignUpResponse + (*LogoutRequest)(nil), // 4: user.v1.LogoutRequest + (*LogoutResponse)(nil), // 5: user.v1.LogoutResponse } var file_user_v1_auth_proto_depIdxs = []int32{ - 0, // 0: user.v1.AuthService.Login:input_type -> user.v1.LoginRequest - 2, // 1: user.v1.AuthService.SignUp:input_type -> user.v1.SignUpRequest - 4, // 2: user.v1.AuthService.Logout:input_type -> user.v1.LogoutRequest - 6, // 3: user.v1.AuthService.GetPasskeyIDs:input_type -> user.v1.GetPasskeyIDsRequest - 8, // 4: user.v1.AuthService.BeginPasskeyLogin:input_type -> user.v1.BeginPasskeyLoginRequest - 10, // 5: user.v1.AuthService.FinishPasskeyLogin:input_type -> user.v1.FinishPasskeyLoginRequest - 1, // 6: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse - 3, // 7: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse - 5, // 8: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse - 7, // 9: user.v1.AuthService.GetPasskeyIDs:output_type -> user.v1.GetPasskeyIDsResponse - 9, // 10: user.v1.AuthService.BeginPasskeyLogin:output_type -> user.v1.BeginPasskeyLoginResponse - 11, // 11: user.v1.AuthService.FinishPasskeyLogin:output_type -> user.v1.FinishPasskeyLoginResponse - 6, // [6:12] is the sub-list for method output_type - 0, // [0:6] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: user.v1.AuthService.Login:input_type -> user.v1.LoginRequest + 2, // 1: user.v1.AuthService.SignUp:input_type -> user.v1.SignUpRequest + 4, // 2: user.v1.AuthService.Logout:input_type -> user.v1.LogoutRequest + 1, // 3: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse + 3, // 4: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse + 5, // 5: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse + 3, // [3:6] is the sub-list for method output_type + 0, // [0:3] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_user_v1_auth_proto_init() } @@ -654,7 +379,7 @@ func file_user_v1_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_auth_proto_rawDesc), len(file_user_v1_auth_proto_rawDesc)), NumEnums: 0, - NumMessages: 12, + NumMessages: 6, NumExtensions: 0, NumServices: 1, }, diff --git a/server/internal/services/user/v1/user.pb.go b/server/internal/services/user/v1/user.pb.go index e46824b..e21b671 100644 --- a/server/internal/services/user/v1/user.pb.go +++ b/server/internal/services/user/v1/user.pb.go @@ -22,12 +22,12 @@ const ( ) type User struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` - ProfilePicture *string `protobuf:"bytes,3,opt,name=profile_picture,json=profilePicture,proto3,oneof" json:"profile_picture,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + ProfilePictureId *int64 `protobuf:"varint,3,opt,name=profile_picture_id,json=profilePictureId,proto3,oneof" json:"profile_picture_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *User) Reset() { @@ -60,7 +60,7 @@ func (*User) Descriptor() ([]byte, []int) { return file_user_v1_user_proto_rawDescGZIP(), []int{0} } -func (x *User) GetId() uint32 { +func (x *User) GetId() int64 { if x != nil { return x.Id } @@ -74,11 +74,11 @@ func (x *User) GetUsername() string { return "" } -func (x *User) GetProfilePicture() string { - if x != nil && x.ProfilePicture != nil { - return *x.ProfilePicture +func (x *User) GetProfilePictureId() int64 { + if x != nil && x.ProfilePictureId != nil { + return *x.ProfilePictureId } - return "" + return 0 } type GetUserRequest struct { @@ -457,252 +457,85 @@ func (x *UpdateProfilePictureResponse) GetUser() *User { return nil } -type BeginPasskeyRegistrationRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BeginPasskeyRegistrationRequest) Reset() { - *x = BeginPasskeyRegistrationRequest{} - mi := &file_user_v1_user_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BeginPasskeyRegistrationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BeginPasskeyRegistrationRequest) ProtoMessage() {} - -func (x *BeginPasskeyRegistrationRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_user_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BeginPasskeyRegistrationRequest.ProtoReflect.Descriptor instead. -func (*BeginPasskeyRegistrationRequest) Descriptor() ([]byte, []int) { - return file_user_v1_user_proto_rawDescGZIP(), []int{9} -} - -type BeginPasskeyRegistrationResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BeginPasskeyRegistrationResponse) Reset() { - *x = BeginPasskeyRegistrationResponse{} - mi := &file_user_v1_user_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BeginPasskeyRegistrationResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BeginPasskeyRegistrationResponse) ProtoMessage() {} - -func (x *BeginPasskeyRegistrationResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_user_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BeginPasskeyRegistrationResponse.ProtoReflect.Descriptor instead. -func (*BeginPasskeyRegistrationResponse) Descriptor() ([]byte, []int) { - return file_user_v1_user_proto_rawDescGZIP(), []int{10} -} - -type FinishPasskeyRegistrationRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FinishPasskeyRegistrationRequest) Reset() { - *x = FinishPasskeyRegistrationRequest{} - mi := &file_user_v1_user_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FinishPasskeyRegistrationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FinishPasskeyRegistrationRequest) ProtoMessage() {} - -func (x *FinishPasskeyRegistrationRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_user_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FinishPasskeyRegistrationRequest.ProtoReflect.Descriptor instead. -func (*FinishPasskeyRegistrationRequest) Descriptor() ([]byte, []int) { - return file_user_v1_user_proto_rawDescGZIP(), []int{11} -} - -type FinishPasskeyRegistrationResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FinishPasskeyRegistrationResponse) Reset() { - *x = FinishPasskeyRegistrationResponse{} - mi := &file_user_v1_user_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FinishPasskeyRegistrationResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FinishPasskeyRegistrationResponse) ProtoMessage() {} - -func (x *FinishPasskeyRegistrationResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_v1_user_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FinishPasskeyRegistrationResponse.ProtoReflect.Descriptor instead. -func (*FinishPasskeyRegistrationResponse) Descriptor() ([]byte, []int) { - return file_user_v1_user_proto_rawDescGZIP(), []int{12} -} - var File_user_v1_user_proto protoreflect.FileDescriptor var file_user_v1_user_proto_rawDesc = string([]byte{ 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x22, 0x74, 0x0a, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x22, 0x7c, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x2c, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x69, 0x63, - 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x42, - 0x12, 0x0a, 0x10, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x69, 0x63, 0x74, - 0x75, 0x72, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x34, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x15, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x6c, 0x64, - 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, - 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3b, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x65, 0x12, 0x31, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x69, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, + 0x10, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x49, + 0x64, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x5f, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x34, 0x0a, + 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x73, 0x65, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x6c, 0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x5f, 0x70, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x25, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4e, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x41, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x21, 0x0a, 0x1f, 0x42, 0x65, 0x67, 0x69, - 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x22, 0x0a, 0x20, 0x42, - 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x22, 0x0a, 0x20, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x23, 0x0a, 0x21, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, - 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb8, 0x04, 0x0a, 0x0b, 0x55, 0x73, 0x65, - 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, - 0x09, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x24, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x25, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x18, 0x42, 0x65, - 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, - 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, - 0x19, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, - 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, - 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, - 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, - 0x07, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, - 0x56, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3b, + 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x25, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4e, 0x0a, + 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x41, 0x0a, + 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x32, 0xcf, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x53, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, + 0x65, 0x79, 0x12, 0x19, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, + 0x75, 0x72, 0x65, 0x12, 0x24, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, + 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, + 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, + 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, + 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, + 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, + 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -717,43 +550,35 @@ func file_user_v1_user_proto_rawDescGZIP() []byte { return file_user_v1_user_proto_rawDescData } -var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_user_v1_user_proto_goTypes = []any{ - (*User)(nil), // 0: user.v1.User - (*GetUserRequest)(nil), // 1: user.v1.GetUserRequest - (*GetUserResponse)(nil), // 2: user.v1.GetUserResponse - (*UpdatePasswordRequest)(nil), // 3: user.v1.UpdatePasswordRequest - (*UpdatePasswordResponse)(nil), // 4: user.v1.UpdatePasswordResponse - (*GetAPIKeyRequest)(nil), // 5: user.v1.GetAPIKeyRequest - (*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse - (*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest - (*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse - (*BeginPasskeyRegistrationRequest)(nil), // 9: user.v1.BeginPasskeyRegistrationRequest - (*BeginPasskeyRegistrationResponse)(nil), // 10: user.v1.BeginPasskeyRegistrationResponse - (*FinishPasskeyRegistrationRequest)(nil), // 11: user.v1.FinishPasskeyRegistrationRequest - (*FinishPasskeyRegistrationResponse)(nil), // 12: user.v1.FinishPasskeyRegistrationResponse + (*User)(nil), // 0: user.v1.User + (*GetUserRequest)(nil), // 1: user.v1.GetUserRequest + (*GetUserResponse)(nil), // 2: user.v1.GetUserResponse + (*UpdatePasswordRequest)(nil), // 3: user.v1.UpdatePasswordRequest + (*UpdatePasswordResponse)(nil), // 4: user.v1.UpdatePasswordResponse + (*GetAPIKeyRequest)(nil), // 5: user.v1.GetAPIKeyRequest + (*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse + (*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest + (*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse } var file_user_v1_user_proto_depIdxs = []int32{ - 0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User - 0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User - 0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User - 1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest - 3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest - 5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest - 7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest - 9, // 7: user.v1.UserService.BeginPasskeyRegistration:input_type -> user.v1.BeginPasskeyRegistrationRequest - 11, // 8: user.v1.UserService.FinishPasskeyRegistration:input_type -> user.v1.FinishPasskeyRegistrationRequest - 2, // 9: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse - 4, // 10: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse - 6, // 11: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse - 8, // 12: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse - 10, // 13: user.v1.UserService.BeginPasskeyRegistration:output_type -> user.v1.BeginPasskeyRegistrationResponse - 12, // 14: user.v1.UserService.FinishPasskeyRegistration:output_type -> user.v1.FinishPasskeyRegistrationResponse - 9, // [9:15] is the sub-list for method output_type - 3, // [3:9] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User + 0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User + 0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User + 1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest + 3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest + 5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest + 7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest + 2, // 7: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse + 4, // 8: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse + 6, // 9: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse + 8, // 10: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse + 7, // [7:11] is the sub-list for method output_type + 3, // [3:7] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_user_v1_user_proto_init() } @@ -768,7 +593,7 @@ func file_user_v1_user_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_user_proto_rawDesc), len(file_user_v1_user_proto_rawDesc)), NumEnums: 0, - NumMessages: 13, + NumMessages: 9, NumExtensions: 0, NumServices: 1, }, diff --git a/server/internal/services/user/v1/userv1connect/auth.connect.go b/server/internal/services/user/v1/userv1connect/auth.connect.go index db25f9b..6d42016 100644 --- a/server/internal/services/user/v1/userv1connect/auth.connect.go +++ b/server/internal/services/user/v1/userv1connect/auth.connect.go @@ -39,15 +39,6 @@ const ( AuthServiceSignUpProcedure = "/user.v1.AuthService/SignUp" // AuthServiceLogoutProcedure is the fully-qualified name of the AuthService's Logout RPC. AuthServiceLogoutProcedure = "/user.v1.AuthService/Logout" - // AuthServiceGetPasskeyIDsProcedure is the fully-qualified name of the AuthService's GetPasskeyIDs - // RPC. - AuthServiceGetPasskeyIDsProcedure = "/user.v1.AuthService/GetPasskeyIDs" - // AuthServiceBeginPasskeyLoginProcedure is the fully-qualified name of the AuthService's - // BeginPasskeyLogin RPC. - AuthServiceBeginPasskeyLoginProcedure = "/user.v1.AuthService/BeginPasskeyLogin" - // AuthServiceFinishPasskeyLoginProcedure is the fully-qualified name of the AuthService's - // FinishPasskeyLogin RPC. - AuthServiceFinishPasskeyLoginProcedure = "/user.v1.AuthService/FinishPasskeyLogin" ) // AuthServiceClient is a client for the user.v1.AuthService service. @@ -55,9 +46,6 @@ type AuthServiceClient interface { Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error) SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) - GetPasskeyIDs(context.Context, *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) - BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) - FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) } // NewAuthServiceClient constructs a client for the user.v1.AuthService service. By default, it uses @@ -89,35 +77,14 @@ func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. connect.WithSchema(authServiceMethods.ByName("Logout")), connect.WithClientOptions(opts...), ), - getPasskeyIDs: connect.NewClient[v1.GetPasskeyIDsRequest, v1.GetPasskeyIDsResponse]( - httpClient, - baseURL+AuthServiceGetPasskeyIDsProcedure, - connect.WithSchema(authServiceMethods.ByName("GetPasskeyIDs")), - connect.WithClientOptions(opts...), - ), - beginPasskeyLogin: connect.NewClient[v1.BeginPasskeyLoginRequest, v1.BeginPasskeyLoginResponse]( - httpClient, - baseURL+AuthServiceBeginPasskeyLoginProcedure, - connect.WithSchema(authServiceMethods.ByName("BeginPasskeyLogin")), - connect.WithClientOptions(opts...), - ), - finishPasskeyLogin: connect.NewClient[v1.FinishPasskeyLoginRequest, v1.FinishPasskeyLoginResponse]( - httpClient, - baseURL+AuthServiceFinishPasskeyLoginProcedure, - connect.WithSchema(authServiceMethods.ByName("FinishPasskeyLogin")), - connect.WithClientOptions(opts...), - ), } } // authServiceClient implements AuthServiceClient. type authServiceClient struct { - login *connect.Client[v1.LoginRequest, v1.LoginResponse] - signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse] - logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse] - getPasskeyIDs *connect.Client[v1.GetPasskeyIDsRequest, v1.GetPasskeyIDsResponse] - beginPasskeyLogin *connect.Client[v1.BeginPasskeyLoginRequest, v1.BeginPasskeyLoginResponse] - finishPasskeyLogin *connect.Client[v1.FinishPasskeyLoginRequest, v1.FinishPasskeyLoginResponse] + login *connect.Client[v1.LoginRequest, v1.LoginResponse] + signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse] + logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse] } // Login calls user.v1.AuthService.Login. @@ -135,29 +102,11 @@ func (c *authServiceClient) Logout(ctx context.Context, req *connect.Request[v1. return c.logout.CallUnary(ctx, req) } -// GetPasskeyIDs calls user.v1.AuthService.GetPasskeyIDs. -func (c *authServiceClient) GetPasskeyIDs(ctx context.Context, req *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) { - return c.getPasskeyIDs.CallUnary(ctx, req) -} - -// BeginPasskeyLogin calls user.v1.AuthService.BeginPasskeyLogin. -func (c *authServiceClient) BeginPasskeyLogin(ctx context.Context, req *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) { - return c.beginPasskeyLogin.CallUnary(ctx, req) -} - -// FinishPasskeyLogin calls user.v1.AuthService.FinishPasskeyLogin. -func (c *authServiceClient) FinishPasskeyLogin(ctx context.Context, req *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) { - return c.finishPasskeyLogin.CallUnary(ctx, req) -} - // AuthServiceHandler is an implementation of the user.v1.AuthService service. type AuthServiceHandler interface { Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error) SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) - GetPasskeyIDs(context.Context, *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) - BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) - FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) } // NewAuthServiceHandler builds an HTTP handler from the service implementation. It returns the path @@ -185,24 +134,6 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption connect.WithSchema(authServiceMethods.ByName("Logout")), connect.WithHandlerOptions(opts...), ) - authServiceGetPasskeyIDsHandler := connect.NewUnaryHandler( - AuthServiceGetPasskeyIDsProcedure, - svc.GetPasskeyIDs, - connect.WithSchema(authServiceMethods.ByName("GetPasskeyIDs")), - connect.WithHandlerOptions(opts...), - ) - authServiceBeginPasskeyLoginHandler := connect.NewUnaryHandler( - AuthServiceBeginPasskeyLoginProcedure, - svc.BeginPasskeyLogin, - connect.WithSchema(authServiceMethods.ByName("BeginPasskeyLogin")), - connect.WithHandlerOptions(opts...), - ) - authServiceFinishPasskeyLoginHandler := connect.NewUnaryHandler( - AuthServiceFinishPasskeyLoginProcedure, - svc.FinishPasskeyLogin, - connect.WithSchema(authServiceMethods.ByName("FinishPasskeyLogin")), - connect.WithHandlerOptions(opts...), - ) return "/user.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case AuthServiceLoginProcedure: @@ -211,12 +142,6 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption authServiceSignUpHandler.ServeHTTP(w, r) case AuthServiceLogoutProcedure: authServiceLogoutHandler.ServeHTTP(w, r) - case AuthServiceGetPasskeyIDsProcedure: - authServiceGetPasskeyIDsHandler.ServeHTTP(w, r) - case AuthServiceBeginPasskeyLoginProcedure: - authServiceBeginPasskeyLoginHandler.ServeHTTP(w, r) - case AuthServiceFinishPasskeyLoginProcedure: - authServiceFinishPasskeyLoginHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -237,15 +162,3 @@ func (UnimplementedAuthServiceHandler) SignUp(context.Context, *connect.Request[ func (UnimplementedAuthServiceHandler) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.Logout is not implemented")) } - -func (UnimplementedAuthServiceHandler) GetPasskeyIDs(context.Context, *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.GetPasskeyIDs is not implemented")) -} - -func (UnimplementedAuthServiceHandler) BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.BeginPasskeyLogin is not implemented")) -} - -func (UnimplementedAuthServiceHandler) FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.FinishPasskeyLogin is not implemented")) -} diff --git a/server/internal/services/user/v1/userv1connect/user.connect.go b/server/internal/services/user/v1/userv1connect/user.connect.go index 6eea8c3..7f25e60 100644 --- a/server/internal/services/user/v1/userv1connect/user.connect.go +++ b/server/internal/services/user/v1/userv1connect/user.connect.go @@ -43,12 +43,6 @@ const ( // UserServiceUpdateProfilePictureProcedure is the fully-qualified name of the UserService's // UpdateProfilePicture RPC. UserServiceUpdateProfilePictureProcedure = "/user.v1.UserService/UpdateProfilePicture" - // UserServiceBeginPasskeyRegistrationProcedure is the fully-qualified name of the UserService's - // BeginPasskeyRegistration RPC. - UserServiceBeginPasskeyRegistrationProcedure = "/user.v1.UserService/BeginPasskeyRegistration" - // UserServiceFinishPasskeyRegistrationProcedure is the fully-qualified name of the UserService's - // FinishPasskeyRegistration RPC. - UserServiceFinishPasskeyRegistrationProcedure = "/user.v1.UserService/FinishPasskeyRegistration" ) // UserServiceClient is a client for the user.v1.UserService service. @@ -57,8 +51,6 @@ type UserServiceClient interface { UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error) GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) - BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) - FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) } // NewUserServiceClient constructs a client for the user.v1.UserService service. By default, it uses @@ -96,29 +88,15 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")), connect.WithClientOptions(opts...), ), - beginPasskeyRegistration: connect.NewClient[v1.BeginPasskeyRegistrationRequest, v1.BeginPasskeyRegistrationResponse]( - httpClient, - baseURL+UserServiceBeginPasskeyRegistrationProcedure, - connect.WithSchema(userServiceMethods.ByName("BeginPasskeyRegistration")), - connect.WithClientOptions(opts...), - ), - finishPasskeyRegistration: connect.NewClient[v1.FinishPasskeyRegistrationRequest, v1.FinishPasskeyRegistrationResponse]( - httpClient, - baseURL+UserServiceFinishPasskeyRegistrationProcedure, - connect.WithSchema(userServiceMethods.ByName("FinishPasskeyRegistration")), - connect.WithClientOptions(opts...), - ), } } // userServiceClient implements UserServiceClient. type userServiceClient struct { - getUser *connect.Client[v1.GetUserRequest, v1.GetUserResponse] - updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse] - getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse] - updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse] - beginPasskeyRegistration *connect.Client[v1.BeginPasskeyRegistrationRequest, v1.BeginPasskeyRegistrationResponse] - finishPasskeyRegistration *connect.Client[v1.FinishPasskeyRegistrationRequest, v1.FinishPasskeyRegistrationResponse] + getUser *connect.Client[v1.GetUserRequest, v1.GetUserResponse] + updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse] + getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse] + updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse] } // GetUser calls user.v1.UserService.GetUser. @@ -141,24 +119,12 @@ func (c *userServiceClient) UpdateProfilePicture(ctx context.Context, req *conne return c.updateProfilePicture.CallUnary(ctx, req) } -// BeginPasskeyRegistration calls user.v1.UserService.BeginPasskeyRegistration. -func (c *userServiceClient) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) { - return c.beginPasskeyRegistration.CallUnary(ctx, req) -} - -// FinishPasskeyRegistration calls user.v1.UserService.FinishPasskeyRegistration. -func (c *userServiceClient) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) { - return c.finishPasskeyRegistration.CallUnary(ctx, req) -} - // UserServiceHandler is an implementation of the user.v1.UserService service. type UserServiceHandler interface { GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.GetUserResponse], error) UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error) GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) - BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) - FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) } // NewUserServiceHandler builds an HTTP handler from the service implementation. It returns the path @@ -192,18 +158,6 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")), connect.WithHandlerOptions(opts...), ) - userServiceBeginPasskeyRegistrationHandler := connect.NewUnaryHandler( - UserServiceBeginPasskeyRegistrationProcedure, - svc.BeginPasskeyRegistration, - connect.WithSchema(userServiceMethods.ByName("BeginPasskeyRegistration")), - connect.WithHandlerOptions(opts...), - ) - userServiceFinishPasskeyRegistrationHandler := connect.NewUnaryHandler( - UserServiceFinishPasskeyRegistrationProcedure, - svc.FinishPasskeyRegistration, - connect.WithSchema(userServiceMethods.ByName("FinishPasskeyRegistration")), - connect.WithHandlerOptions(opts...), - ) return "/user.v1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case UserServiceGetUserProcedure: @@ -214,10 +168,6 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption userServiceGetAPIKeyHandler.ServeHTTP(w, r) case UserServiceUpdateProfilePictureProcedure: userServiceUpdateProfilePictureHandler.ServeHTTP(w, r) - case UserServiceBeginPasskeyRegistrationProcedure: - userServiceBeginPasskeyRegistrationHandler.ServeHTTP(w, r) - case UserServiceFinishPasskeyRegistrationProcedure: - userServiceFinishPasskeyRegistrationHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -242,11 +192,3 @@ func (UnimplementedUserServiceHandler) GetAPIKey(context.Context, *connect.Reque func (UnimplementedUserServiceHandler) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.UpdateProfilePicture is not implemented")) } - -func (UnimplementedUserServiceHandler) BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.BeginPasskeyRegistration is not implemented")) -} - -func (UnimplementedUserServiceHandler) FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.FinishPasskeyRegistration is not implemented")) -} diff --git a/server/main.go b/server/main.go index 898c914..ef22539 100644 --- a/server/main.go +++ b/server/main.go @@ -3,65 +3,41 @@ package main import ( "context" + "errors" "fmt" "log" + "log/slog" "net/http" "os" "os/signal" "syscall" "time" - connectcors "connectrpc.com/cors" "github.com/joho/godotenv" - "github.com/rs/cors" + "github.com/stephenafamo/bob" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" - "gorm.io/gorm" "github.com/spotdemo4/trevstack/server/internal/database" - "github.com/spotdemo4/trevstack/server/internal/handlers" "github.com/spotdemo4/trevstack/server/internal/handlers/client" + "github.com/spotdemo4/trevstack/server/internal/handlers/file" "github.com/spotdemo4/trevstack/server/internal/handlers/item/v1" "github.com/spotdemo4/trevstack/server/internal/handlers/user/v1" + "github.com/spotdemo4/trevstack/server/internal/interceptors" ) -type env struct { - DBType string - DBUser string - DBPass string - DBHost string - DBPort string - DBName string - Port string - Key string -} - func main() { - err := godotenv.Load() + logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + slog.SetDefault(logger) + + // Get env + env, err := getEnv() if err != nil { - log.Println("Failed to load .env file, using environment variables") + log.Fatal(err.Error()) } - // Get environment variables for server - env := env{ - DBType: os.Getenv("DB_TYPE"), - DBUser: os.Getenv("DB_USER"), - DBPass: os.Getenv("DB_PASS"), - DBHost: os.Getenv("DB_HOST"), - DBPort: os.Getenv("DB_PORT"), - DBName: os.Getenv("DB_NAME"), - Port: os.Getenv("PORT"), - Key: os.Getenv("KEY"), - } - if env.Port == "" { - env.Port = "8080" - } - if env.Key == "" { - log.Fatal("KEY is required") - } - - // Get environment variables for database - db := &gorm.DB{} + // Get database + db := &bob.DB{} switch env.DBType { case "postgres": log.Println("Using Postgres") @@ -103,21 +79,16 @@ func main() { log.Fatal("DB_TYPE must be either postgres or sqlite") } - // Init database - if err := database.Migrate(db); err != nil { - log.Fatalf("failed to migrate database: %v", err) - } - // Serve GRPC Handlers api := http.NewServeMux() - api.Handle(withCORS(user.NewAuthHandler(db, env.Key))) - api.Handle(withCORS(user.NewHandler(db, env.Key))) - api.Handle(withCORS(item.NewHandler(db, env.Key))) + api.Handle(interceptors.WithCORS(user.NewAuthHandler(db, env.Key))) + api.Handle(interceptors.WithCORS(user.NewHandler(db, env.Key))) + api.Handle(interceptors.WithCORS(item.NewHandler(db, env.Key))) // Serve web interface mux := http.NewServeMux() mux.Handle("/", client.NewClientHandler(env.Key)) - mux.Handle("/file/", handlers.NewFileHandler(db, env.Key)) + mux.Handle("/file/", file.NewFileHandler(db, env.Key)) mux.Handle("/grpc/", http.StripPrefix("/grpc", api)) // Start server @@ -132,8 +103,7 @@ func main() { signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-sigs - log.Printf("Received signal %s", sig) - log.Println("Exiting") + slog.Warn(fmt.Sprintf("Received signal %s, exiting", sig)) // Close HTTP server ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) @@ -143,10 +113,7 @@ func main() { cancel() // Close database connection - sqlDB, err := db.DB() // Get underlying SQL database - if err == nil { - sqlDB.Close() - } + db.Close() }() if err := server.ListenAndServe(); err != nil { @@ -154,13 +121,42 @@ func main() { } } -// withCORS adds CORS support to a Connect HTTP handler. -func withCORS(pattern string, h http.Handler) (string, http.Handler) { - middleware := cors.New(cors.Options{ - AllowedOrigins: []string{"*"}, - AllowedMethods: connectcors.AllowedMethods(), - AllowedHeaders: connectcors.AllowedHeaders(), - ExposedHeaders: connectcors.ExposedHeaders(), - }) - return pattern, middleware.Handler(h) +type env struct { + DBType string + DBUser string + DBPass string + DBHost string + DBPort string + DBName string + Port string + Key string +} + +func getEnv() (*env, error) { + err := godotenv.Load() + if err != nil { + slog.Warn("Failed to load .env file, using environment variables") + } + + // Create + env := env{ + DBType: os.Getenv("DB_TYPE"), + DBUser: os.Getenv("DB_USER"), + DBPass: os.Getenv("DB_PASS"), + DBHost: os.Getenv("DB_HOST"), + DBPort: os.Getenv("DB_PORT"), + DBName: os.Getenv("DB_NAME"), + Port: os.Getenv("PORT"), + Key: os.Getenv("KEY"), + } + + // Validate + if env.Port == "" { + env.Port = "8080" + } + if env.Key == "" { + return nil, errors.New("env 'key' not found") + } + + return &env, nil }