feat: bob

This commit is contained in:
trev 2025-04-10 00:59:28 -04:00
parent dfd6789aa9
commit e9c44cbc94
58 changed files with 1649 additions and 3104 deletions

View File

@ -20,12 +20,6 @@ jobs:
with: with:
nix_path: nixpkgs=channel:nixos-unstable 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 - name: Install NPM Packages
working-directory: ./client working-directory: ./client
run: npm ci --legacy-peer-deps run: npm ci --legacy-peer-deps

View File

@ -20,12 +20,6 @@ jobs:
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
- name: Use Cachix
uses: cachix/cachix-action@v16
with:
name: trevstack
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build - name: Build
run: nix develop --command ts-build run: nix develop --command ts-build

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"go.lintTool": "revive",
"go.lintFlags": [
"--config=server/revive.toml"
],
}

View File

@ -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)
}
}

View File

@ -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
)

View File

@ -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=

View File

@ -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
}

View File

@ -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),
})
}
}
}
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -1 +0,0 @@
// place files you want to import through the `$lib` alias in this folder.

View File

@ -12,16 +12,16 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file item/v1/item.proto. * Describes the file item/v1/item.proto.
*/ */
export const file_item_v1_item: GenFile = /*@__PURE__*/ 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 * @generated from message item.v1.Item
*/ */
export type Item = 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; * @generated from field: string name = 2;
@ -39,7 +39,7 @@ export type Item = Message<"item.v1.Item"> & {
price: number; price: number;
/** /**
* @generated from field: uint32 quantity = 5; * @generated from field: int32 quantity = 5;
*/ */
quantity: number; quantity: number;
@ -61,9 +61,9 @@ export const ItemSchema: GenMessage<Item> = /*@__PURE__*/
*/ */
export type GetItemRequest = Message<"item.v1.GetItemRequest"> & { 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; filter?: string;
/** /**
* @generated from field: optional uint32 limit = 4; * @generated from field: optional int32 limit = 4;
*/ */
limit?: number; limit?: number;
/** /**
* @generated from field: optional uint32 offset = 5; * @generated from field: optional int32 offset = 5;
*/ */
offset?: number; offset?: number;
}; };
@ -137,7 +137,7 @@ export type GetItemsResponse = Message<"item.v1.GetItemsResponse"> & {
items: Item[]; items: Item[];
/** /**
* @generated from field: uint64 count = 2; * @generated from field: int64 count = 2;
*/ */
count: bigint; count: bigint;
}; };
@ -222,9 +222,9 @@ export const UpdateItemResponseSchema: GenMessage<UpdateItemResponse> = /*@__PUR
*/ */
export type DeleteItemRequest = Message<"item.v1.DeleteItemRequest"> & { export type DeleteItemRequest = Message<"item.v1.DeleteItemRequest"> & {
/** /**
* @generated from field: uint32 id = 1; * @generated from field: int64 id = 1;
*/ */
id: number; id: bigint;
}; };
/** /**

View File

@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file user/v1/auth.proto. * Describes the file user/v1/auth.proto.
*/ */
export const file_user_v1_auth: GenFile = /*@__PURE__*/ export const file_user_v1_auth: GenFile = /*@__PURE__*/
fileDesc("ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UiKAoUR2V0UGFzc2tleUlEc1JlcXVlc3QSEAoIdXNlcm5hbWUYASABKAkiLAoVR2V0UGFzc2tleUlEc1Jlc3BvbnNlEhMKC3Bhc3NrZXlfaWRzGAEgAygJIhoKGEJlZ2luUGFzc2tleUxvZ2luUmVxdWVzdCIbChlCZWdpblBhc3NrZXlMb2dpblJlc3BvbnNlIhsKGUZpbmlzaFBhc3NrZXlMb2dpblJlcXVlc3QiHAoaRmluaXNoUGFzc2tleUxvZ2luUmVzcG9uc2Uy0gMKC0F1dGhTZXJ2aWNlEjgKBUxvZ2luEhUudXNlci52MS5Mb2dpblJlcXVlc3QaFi51c2VyLnYxLkxvZ2luUmVzcG9uc2UiABI7CgZTaWduVXASFi51c2VyLnYxLlNpZ25VcFJlcXVlc3QaFy51c2VyLnYxLlNpZ25VcFJlc3BvbnNlIgASOwoGTG9nb3V0EhYudXNlci52MS5Mb2dvdXRSZXF1ZXN0GhcudXNlci52MS5Mb2dvdXRSZXNwb25zZSIAElAKDUdldFBhc3NrZXlJRHMSHS51c2VyLnYxLkdldFBhc3NrZXlJRHNSZXF1ZXN0Gh4udXNlci52MS5HZXRQYXNza2V5SURzUmVzcG9uc2UiABJcChFCZWdpblBhc3NrZXlMb2dpbhIhLnVzZXIudjEuQmVnaW5QYXNza2V5TG9naW5SZXF1ZXN0GiIudXNlci52MS5CZWdpblBhc3NrZXlMb2dpblJlc3BvbnNlIgASXwoSRmluaXNoUGFzc2tleUxvZ2luEiIudXNlci52MS5GaW5pc2hQYXNza2V5TG9naW5SZXF1ZXN0GiMudXNlci52MS5GaW5pc2hQYXNza2V5TG9naW5SZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJQXV0aFByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z"); fileDesc("ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UywQEKC0F1dGhTZXJ2aWNlEjgKBUxvZ2luEhUudXNlci52MS5Mb2dpblJlcXVlc3QaFi51c2VyLnYxLkxvZ2luUmVzcG9uc2UiABI7CgZTaWduVXASFi51c2VyLnYxLlNpZ25VcFJlcXVlc3QaFy51c2VyLnYxLlNpZ25VcFJlc3BvbnNlIgASOwoGTG9nb3V0EhYudXNlci52MS5Mb2dvdXRSZXF1ZXN0GhcudXNlci52MS5Mb2dvdXRSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJQXV0aFByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z");
/** /**
* @generated from message user.v1.LoginRequest * @generated from message user.v1.LoginRequest
@ -117,92 +117,6 @@ export type LogoutResponse = Message<"user.v1.LogoutResponse"> & {
export const LogoutResponseSchema: GenMessage<LogoutResponse> = /*@__PURE__*/ export const LogoutResponseSchema: GenMessage<LogoutResponse> = /*@__PURE__*/
messageDesc(file_user_v1_auth, 5); 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<GetPasskeyIDsRequest> = /*@__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<GetPasskeyIDsResponse> = /*@__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<BeginPasskeyLoginRequest> = /*@__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<BeginPasskeyLoginResponse> = /*@__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<FinishPasskeyLoginRequest> = /*@__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<FinishPasskeyLoginResponse> = /*@__PURE__*/
messageDesc(file_user_v1_auth, 11);
/** /**
* @generated from service user.v1.AuthService * @generated from service user.v1.AuthService
*/ */
@ -231,30 +145,6 @@ export const AuthService: GenService<{
input: typeof LogoutRequestSchema; input: typeof LogoutRequestSchema;
output: typeof LogoutResponseSchema; 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__*/ }> = /*@__PURE__*/
serviceDesc(file_user_v1_auth, 0); serviceDesc(file_user_v1_auth, 0);

View File

@ -10,16 +10,16 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file user/v1/user.proto. * Describes the file user/v1/user.proto.
*/ */
export const file_user_v1_user: GenFile = /*@__PURE__*/ export const file_user_v1_user: GenFile = /*@__PURE__*/
fileDesc("ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiVgoEVXNlchIKCgJpZBgBIAEoDRIQCgh1c2VybmFtZRgCIAEoCRIcCg9wcm9maWxlX3BpY3R1cmUYAyABKAlIAIgBAUISChBfcHJvZmlsZV9waWN0dXJlIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlciIhCh9CZWdpblBhc3NrZXlSZWdpc3RyYXRpb25SZXF1ZXN0IiIKIEJlZ2luUGFzc2tleVJlZ2lzdHJhdGlvblJlc3BvbnNlIiIKIEZpbmlzaFBhc3NrZXlSZWdpc3RyYXRpb25SZXF1ZXN0IiMKIUZpbmlzaFBhc3NrZXlSZWdpc3RyYXRpb25SZXNwb25zZTK4BAoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAEnEKGEJlZ2luUGFzc2tleVJlZ2lzdHJhdGlvbhIoLnVzZXIudjEuQmVnaW5QYXNza2V5UmVnaXN0cmF0aW9uUmVxdWVzdBopLnVzZXIudjEuQmVnaW5QYXNza2V5UmVnaXN0cmF0aW9uUmVzcG9uc2UiABJ0ChlGaW5pc2hQYXNza2V5UmVnaXN0cmF0aW9uEikudXNlci52MS5GaW5pc2hQYXNza2V5UmVnaXN0cmF0aW9uUmVxdWVzdBoqLnVzZXIudjEuRmluaXNoUGFzc2tleVJlZ2lzdHJhdGlvblJlc3BvbnNlIgBCnQEKC2NvbS51c2VyLnYxQglVc2VyUHJvdG9QAVpGZ2l0aHViLmNvbS9zcG90ZGVtbzQvdHJldnN0YWNrL3NlcnZlci9pbnRlcm5hbC9zZXJ2aWNlcy91c2VyL3YxO3VzZXJ2MaICA1VYWKoCB1VzZXIuVjHKAgdVc2VyXFYx4gITVXNlclxWMVxHUEJNZXRhZGF0YeoCCFVzZXI6OlYxYgZwcm90bzM"); fileDesc("ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiXAoEVXNlchIKCgJpZBgBIAEoAxIQCgh1c2VybmFtZRgCIAEoCRIfChJwcm9maWxlX3BpY3R1cmVfaWQYAyABKANIAIgBAUIVChNfcHJvZmlsZV9waWN0dXJlX2lkIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlcjLPAgoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJVXNlclByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z");
/** /**
* @generated from message user.v1.User * @generated from message user.v1.User
*/ */
export type User = 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; * @generated from field: string username = 2;
@ -27,9 +27,9 @@ export type User = Message<"user.v1.User"> & {
username: string; 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<UpdateProfilePictureResponse> = /*@__PURE__*/ export const UpdateProfilePictureResponseSchema: GenMessage<UpdateProfilePictureResponse> = /*@__PURE__*/
messageDesc(file_user_v1_user, 8); 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<BeginPasskeyRegistrationRequest> = /*@__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<BeginPasskeyRegistrationResponse> = /*@__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<FinishPasskeyRegistrationRequest> = /*@__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<FinishPasskeyRegistrationResponse> = /*@__PURE__*/
messageDesc(file_user_v1_user, 12);
/** /**
* @generated from service user.v1.UserService * @generated from service user.v1.UserService
*/ */
@ -279,22 +227,6 @@ export const UserService: GenService<{
input: typeof UpdateProfilePictureRequestSchema; input: typeof UpdateProfilePictureRequestSchema;
output: typeof UpdateProfilePictureResponseSchema; 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__*/ }> = /*@__PURE__*/
serviceDesc(file_user_v1_user, 0); serviceDesc(file_user_v1_user, 0);

View File

@ -5,7 +5,7 @@
<Avatar.Root class="flex h-full w-full items-center justify-center"> <Avatar.Root class="flex h-full w-full items-center justify-center">
<Avatar.Image <Avatar.Image
src={userState.user?.profilePicture} src={userState.user?.profilePictureId ? "/file/" + userState.user.profilePictureId : null}
alt={`${userState.user?.username}'s avatar`} alt={`${userState.user?.username}'s avatar`}
class="rounded-full" class="rounded-full"
/> />

View File

@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
import { Button } from 'bits-ui'; import { Button } from 'bits-ui';
import { cn } from '$lib/utils'; import { cn } from '$lib/utils';
import type { MouseEventHandler } from 'svelte/elements';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
type me = MouseEvent & { currentTarget: EventTarget & HTMLButtonElement; }
let { let {
className, className,
type, type,
@ -12,7 +13,7 @@
}: { }: {
className?: string; className?: string;
type?: 'submit' | 'reset' | 'button' | null; type?: 'submit' | 'reset' | 'button' | null;
onclick?: () => void; onclick?: (e: me) => void;
children?: Snippet<[]>; children?: Snippet<[]>;
} = $props(); } = $props();
</script> </script>
@ -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', '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 className
)} )}
onclick={() => { onclick={(e: me) => {
onclick?.(); onclick?.(e);
}} }}
> >
{@render children?.()} {@render children?.()}

View File

@ -3,7 +3,7 @@
import { ChevronLeft, ChevronRight } from '@lucide/svelte'; import { ChevronLeft, ChevronRight } from '@lucide/svelte';
import { Pagination } from 'bits-ui'; import { Pagination } from 'bits-ui';
import { pushState, replaceState } from '$app/navigation'; import { pushState, replaceState } from '$app/navigation';
import { onMount } from 'svelte'; import { onMount, tick } from 'svelte';
let { let {
count = $bindable(), count = $bindable(),
@ -21,7 +21,8 @@
let page: number = $state(1); let page: number = $state(1);
onMount(() => { onMount(async() => {
await tick();
replaceState('', `${page}`); replaceState('', `${page}`);
}); });
</script> </script>

View File

@ -26,8 +26,8 @@
// Open // Open
let addedOpen = $state(false); let addedOpen = $state(false);
let deletesOpen: SvelteMap<number, boolean> = new SvelteMap(); let deletesOpen: SvelteMap<bigint, boolean> = new SvelteMap();
let editsOpen: SvelteMap<number, boolean> = new SvelteMap(); let editsOpen: SvelteMap<bigint, boolean> = new SvelteMap();
async function getItems() { async function getItems() {
return await ItemClient.getItems({ return await ItemClient.getItems({

View File

@ -140,8 +140,7 @@
className="bg-text" className="bg-text"
onclick={async () => { onclick={async () => {
if (userState.user) { if (userState.user) {
await createPasskey(userState.user.username, userState.user.id, "what"); //await createPasskey(userState.user.username, userState.user.id, "what");
} }
}}>Register Device</Button }}>Register Device</Button
> >

View File

@ -128,8 +128,11 @@ components:
type: object type: object
properties: properties:
id: id:
type: integer type:
- integer
- string
title: id title: id
format: int64
title: DeleteItemRequest title: DeleteItemRequest
additionalProperties: false additionalProperties: false
item.v1.DeleteItemResponse: item.v1.DeleteItemResponse:
@ -140,8 +143,11 @@ components:
type: object type: object
properties: properties:
id: id:
type: integer type:
- integer
- string
title: id title: id
format: int64
title: GetItemRequest title: GetItemRequest
additionalProperties: false additionalProperties: false
item.v1.GetItemResponse: item.v1.GetItemResponse:
@ -170,10 +176,12 @@ components:
limit: limit:
type: integer type: integer
title: limit title: limit
format: int32
nullable: true nullable: true
offset: offset:
type: integer type: integer
title: offset title: offset
format: int32
nullable: true nullable: true
title: GetItemsRequest title: GetItemsRequest
additionalProperties: false additionalProperties: false
@ -197,8 +205,11 @@ components:
type: object type: object
properties: properties:
id: id:
type: integer type:
- integer
- string
title: id title: id
format: int64
nullable: true nullable: true
name: name:
type: string type: string
@ -213,6 +224,7 @@ components:
quantity: quantity:
type: integer type: integer
title: quantity title: quantity
format: int32
added: added:
title: added title: added
nullable: true nullable: true
@ -292,40 +304,6 @@ components:
additionalProperties: true additionalProperties: true
additionalProperties: true additionalProperties: true
description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. 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: user.v1.LoginRequest:
type: object type: object
properties: properties:
@ -371,22 +349,6 @@ components:
type: object type: object
title: SignUpResponse title: SignUpResponse
additionalProperties: false 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: user.v1.GetAPIKeyRequest:
type: object type: object
properties: properties:
@ -464,14 +426,20 @@ components:
type: object type: object
properties: properties:
id: id:
type: integer type:
- integer
- string
title: id title: id
format: int64
username: username:
type: string type: string
title: username title: username
profilePicture: profilePictureId:
type: string type:
title: profile_picture - integer
- string
title: profile_picture_id
format: int64
nullable: true nullable: true
title: User title: User
additionalProperties: false additionalProperties: false
@ -758,111 +726,6 @@ paths:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/user.v1.LogoutResponse' $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: /user.v1.UserService/GetUser:
post: post:
tags: tags:
@ -1003,76 +866,6 @@ paths:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/user.v1.UpdateProfilePictureResponse' $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: tags:
- name: item.v1.ItemService - name: item.v1.ItemService
- name: user.v1.AuthService - name: user.v1.AuthService

View File

@ -16,5 +16,5 @@ export default defineConfig({
} }
}, },
host: '0.0.0.0' host: '0.0.0.0'
} },
}); });

93
flake.lock generated
View File

@ -18,6 +18,24 @@
"type": "github" "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": { "gitignore": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -38,6 +56,27 @@
"type": "github" "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": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1741513245, "lastModified": 1741513245,
@ -54,11 +93,28 @@
"type": "github" "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": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"gitignore": "gitignore", "gitignore": "gitignore",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs",
"treli": "treli"
} }
}, },
"systems": { "systems": {
@ -75,6 +131,41 @@
"repo": "default", "repo": "default",
"type": "github" "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", "root": "root",

View File

@ -4,13 +4,14 @@
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
treli.url = "github:spotdemo4/treli";
gitignore = { gitignore = {
url = "github:hercules-ci/gitignore.nix"; url = "github:hercules-ci/gitignore.nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
outputs = { self, nixpkgs, flake-utils, gitignore }: outputs = { self, nixpkgs, flake-utils, gitignore, treli }:
flake-utils.lib.eachDefaultSystem (system: flake-utils.lib.eachDefaultSystem (system:
let let
@ -80,12 +81,12 @@
packages = with pkgs; [ packages = with pkgs; [
git git
nix-update nix-update
treli.packages."${system}".default
# Go backend # Go backend
go go
gotools gotools
gopls gopls
air
revive revive
bobgen bobgen
@ -95,11 +96,11 @@
protoc-gen-connect-go protoc-gen-connect-go
protoc-gen-es protoc-gen-es
protoc-gen-connect-openapi protoc-gen-connect-openapi
inotify-tools
# Svelte frontend # Svelte frontend
nodejs_22 nodejs_22
# Update
(writeShellApplication { (writeShellApplication {
name = "ts-update"; name = "ts-update";
@ -132,6 +133,7 @@
''; '';
}) })
# Bump version
(writeShellApplication { (writeShellApplication {
name = "ts-bump"; name = "ts-bump";
@ -155,6 +157,7 @@
''; '';
}) })
# Lint
(writeShellApplication { (writeShellApplication {
name = "ts-lint"; name = "ts-lint";
@ -177,10 +180,11 @@
cd "''${git_root}/server" cd "''${git_root}/server"
echo "Linting server" echo "Linting server"
revive -config revive.toml -formatter friendly ./... revive -config revive.toml -set_exit_status ./...
''; '';
}) })
# Build
(writeShellApplication { (writeShellApplication {
name = "ts-build"; name = "ts-build";

View File

@ -5,11 +5,11 @@ package item.v1;
import "google/protobuf/timestamp.proto"; import "google/protobuf/timestamp.proto";
message Item { message Item {
optional uint32 id = 1; optional int64 id = 1;
string name = 2; string name = 2;
string description = 3; string description = 3;
float price = 4; float price = 4;
uint32 quantity = 5; int32 quantity = 5;
optional google.protobuf.Timestamp added = 6; optional google.protobuf.Timestamp added = 6;
} }
@ -22,7 +22,7 @@ service ItemService {
} }
message GetItemRequest { message GetItemRequest {
uint32 id = 1; int64 id = 1;
} }
message GetItemResponse { message GetItemResponse {
Item item = 1; Item item = 1;
@ -32,12 +32,12 @@ message GetItemsRequest {
optional google.protobuf.Timestamp start = 1; optional google.protobuf.Timestamp start = 1;
optional google.protobuf.Timestamp end = 2; optional google.protobuf.Timestamp end = 2;
optional string filter = 3; optional string filter = 3;
optional uint32 limit = 4; optional int32 limit = 4;
optional uint32 offset = 5; optional int32 offset = 5;
} }
message GetItemsResponse { message GetItemsResponse {
repeated Item items = 1; repeated Item items = 1;
uint64 count = 2; int64 count = 2;
} }
message CreateItemRequest { message CreateItemRequest {
@ -55,6 +55,6 @@ message UpdateItemResponse {
} }
message DeleteItemRequest { message DeleteItemRequest {
uint32 id = 1; int64 id = 1;
} }
message DeleteItemResponse {} message DeleteItemResponse {}

View File

@ -6,9 +6,9 @@ service AuthService {
rpc Login (LoginRequest) returns (LoginResponse) {} rpc Login (LoginRequest) returns (LoginResponse) {}
rpc SignUp (SignUpRequest) returns (SignUpResponse) {} rpc SignUp (SignUpRequest) returns (SignUpResponse) {}
rpc Logout (LogoutRequest) returns (LogoutResponse) {} rpc Logout (LogoutRequest) returns (LogoutResponse) {}
rpc GetPasskeyIDs (GetPasskeyIDsRequest) returns (GetPasskeyIDsResponse) {} // rpc GetPasskeyIDs (GetPasskeyIDsRequest) returns (GetPasskeyIDsResponse) {}
rpc BeginPasskeyLogin (BeginPasskeyLoginRequest) returns (BeginPasskeyLoginResponse) {} // rpc BeginPasskeyLogin (BeginPasskeyLoginRequest) returns (BeginPasskeyLoginResponse) {}
rpc FinishPasskeyLogin (FinishPasskeyLoginRequest) returns (FinishPasskeyLoginResponse) {} // rpc FinishPasskeyLogin (FinishPasskeyLoginRequest) returns (FinishPasskeyLoginResponse) {}
} }
message LoginRequest { message LoginRequest {
@ -29,15 +29,15 @@ message SignUpResponse {}
message LogoutRequest {} message LogoutRequest {}
message LogoutResponse {} message LogoutResponse {}
message GetPasskeyIDsRequest { // message GetPasskeyIDsRequest {
string username = 1; // string username = 1;
} // }
message GetPasskeyIDsResponse { // message GetPasskeyIDsResponse {
repeated string passkey_ids = 1; // repeated string passkey_ids = 1;
} // }
message BeginPasskeyLoginRequest {} // message BeginPasskeyLoginRequest {}
message BeginPasskeyLoginResponse {} // message BeginPasskeyLoginResponse {}
message FinishPasskeyLoginRequest {} // message FinishPasskeyLoginRequest {}
message FinishPasskeyLoginResponse {} // message FinishPasskeyLoginResponse {}

View File

@ -3,9 +3,9 @@ syntax = "proto3";
package user.v1; package user.v1;
message User { message User {
uint32 id = 1; int64 id = 1;
string username = 2; string username = 2;
optional string profile_picture = 3; optional int64 profile_picture_id = 3;
} }
service UserService { service UserService {
@ -13,8 +13,8 @@ service UserService {
rpc UpdatePassword (UpdatePasswordRequest) returns (UpdatePasswordResponse) {} rpc UpdatePassword (UpdatePasswordRequest) returns (UpdatePasswordResponse) {}
rpc GetAPIKey (GetAPIKeyRequest) returns (GetAPIKeyResponse) {} rpc GetAPIKey (GetAPIKeyRequest) returns (GetAPIKeyResponse) {}
rpc UpdateProfilePicture (UpdateProfilePictureRequest) returns (UpdateProfilePictureResponse) {} rpc UpdateProfilePicture (UpdateProfilePictureRequest) returns (UpdateProfilePictureResponse) {}
rpc BeginPasskeyRegistration (BeginPasskeyRegistrationRequest) returns (BeginPasskeyRegistrationResponse) {} // rpc BeginPasskeyRegistration (BeginPasskeyRegistrationRequest) returns (BeginPasskeyRegistrationResponse) {}
rpc FinishPasskeyRegistration (FinishPasskeyRegistrationRequest) returns (FinishPasskeyRegistrationResponse) {} // rpc FinishPasskeyRegistration (FinishPasskeyRegistrationRequest) returns (FinishPasskeyRegistrationResponse) {}
} }
message GetUserRequest {} message GetUserRequest {}
@ -47,8 +47,8 @@ message UpdateProfilePictureResponse {
User user = 1; User user = 1;
} }
message BeginPasskeyRegistrationRequest {} // message BeginPasskeyRegistrationRequest {}
message BeginPasskeyRegistrationResponse {} // message BeginPasskeyRegistrationResponse {}
message FinishPasskeyRegistrationRequest {} // message FinishPasskeyRegistrationRequest {}
message FinishPasskeyRegistrationResponse {} // message FinishPasskeyRegistrationResponse {}

View File

@ -1,5 +1,15 @@
wipe: true wipe: true
replacements:
- match:
db_type: "INTEGER"
replace: "int64"
- match:
db_type: "INTEGER"
nullable: true
replace: "int64"
sqlite: sqlite:
output: internal/models output: internal/models

View File

@ -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
},
)
}

View File

@ -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
}

View File

@ -1,18 +1,20 @@
package database package database
import ( import (
"gorm.io/driver/postgres" "database/sql"
"gorm.io/gorm"
_ "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" dsn := "host=" + host + " user=" + user + " password=" + pass + " dbname=" + name + " port=" + port + " sslmode=disable TimeZone=UTC"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ db, err := sql.Open("postgres", dsn)
Logger: NewLogger(),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return db, nil bobdb := bob.NewDB(db)
return &bobdb, nil
} }

View File

@ -1,14 +1,15 @@
package database package database
import ( import (
"database/sql"
"os" "os"
"path/filepath" "path/filepath"
"github.com/glebarez/sqlite" "github.com/stephenafamo/bob"
"gorm.io/gorm" _ "modernc.org/sqlite" // Sqlite
) )
func NewSQLiteConnection(name string) (*gorm.DB, error) { func NewSQLiteConnection(name string) (*bob.DB, error) {
// Find config diretory // Find config diretory
configDir, err := os.UserConfigDir() configDir, err := os.UserConfigDir()
if err != nil { if err != nil {
@ -24,12 +25,13 @@ func NewSQLiteConnection(name string) (*gorm.DB, error) {
// Open database // Open database
dbPath := filepath.Join(settingsPath, name) dbPath := filepath.Join(settingsPath, name)
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ db, err := sql.Open("sqlite", dbPath)
Logger: NewLogger(),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return db, nil // Create new bob db
bobdb := bob.NewDB(db)
return &bobdb, nil
} }

View File

@ -1,18 +1,21 @@
package handlers package file
import ( import (
"context"
"database/sql"
"errors" "errors"
"log"
"net/http" "net/http"
"strconv"
"strings" "strings"
"github.com/spotdemo4/trevstack/server/internal/interceptors" "github.com/spotdemo4/trevstack/server/internal/interceptors"
"github.com/spotdemo4/trevstack/server/internal/models" "github.com/spotdemo4/trevstack/server/internal/models"
"gorm.io/gorm" "github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/dialect/sqlite"
) )
type FileHandler struct { type FileHandler struct {
db *gorm.DB db *bob.DB
key []byte key []byte
} }
@ -23,37 +26,45 @@ func (h *FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return 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 // Get the file id from the path
pathItems := strings.Split(r.URL.Path, "/") pathItems := strings.Split(r.URL.Path, "/")
if len(pathItems) < 3 { if len(pathItems) < 3 {
http.Redirect(w, r, "/auth", http.StatusFound) http.Redirect(w, r, "/auth", http.StatusFound)
return return
} }
id := pathItems[2] id, err := strconv.Atoi(pathItems[2])
if err != nil {
// 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)
http.Error(w, "Internal Server Error", http.StatusInternalServerError) http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return return
} }
// Serve the file // Get the file from the database
if r.Method == http.MethodGet { file, err := models.Files.Query(
w.Header().Set("Content-Type", http.DetectContentType(file.Data)) sqlite.WhereAnd(
w.Write(file.Data) models.SelectWhere.Files.ID.EQ(int64(id)),
} else { models.SelectWhere.Files.UserID.EQ(userid),
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) ),
} ).One(context.Background(), h.db)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
http.Error(w, "Not Found", http.StatusNotFound)
} }
func NewFileHandler(db *gorm.DB, key string) http.Handler { 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 *bob.DB, key string) http.Handler {
return interceptors.WithAuthRedirect( return interceptors.WithAuthRedirect(
&FileHandler{ &FileHandler{
db: db, db: db,

View File

@ -2,21 +2,39 @@ package item
import ( import (
"context" "context"
"database/sql"
"errors" "errors"
"fmt"
"net/http" "net/http"
"time" "time"
"connectrpc.com/connect" "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/interceptors"
"github.com/spotdemo4/trevstack/server/internal/models" "github.com/spotdemo4/trevstack/server/internal/models"
itemv1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1" itemv1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1"
"github.com/spotdemo4/trevstack/server/internal/services/item/v1/itemv1connect" "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 { type Handler struct {
db *gorm.DB db *bob.DB
key []byte key []byte
} }
@ -27,13 +45,22 @@ func (h *Handler) GetItem(ctx context.Context, req *connect.Request[itemv1.GetIt
} }
// Get item // Get item
item := models.Item{} item, err := models.Items.Query(
if err := h.db.First(&item, "id = ? AND user_id = ?", req.Msg.Id, userid).Error; err != nil { 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.CodeNotFound, err)
} }
return nil, connect.NewError(connect.CodeInternal, err)
}
res := connect.NewResponse(&itemv1.GetItemResponse{ res := connect.NewResponse(&itemv1.GetItemResponse{
Item: item.ToConnectV1(), Item: itemToConnect(item),
}) })
return res, nil return res, nil
} }
@ -45,45 +72,55 @@ func (h *Handler) GetItems(ctx context.Context, req *connect.Request[itemv1.GetI
} }
// Filters // 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 { 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 { 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 { if req.Msg.Filter != nil && *req.Msg.Filter != "" {
sql = sql.Where("name LIKE ?", fmt.Sprintf("%%%s%%", *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 // Uncounted filters
sqlu := sql.Session(&gorm.Session{})
if req.Msg.Limit != nil { if req.Msg.Limit != nil {
sqlu = sqlu.Limit(int(*req.Msg.Limit)) query.Apply(sm.Limit(*req.Msg.Limit))
} }
if req.Msg.Offset != nil { if req.Msg.Offset != nil {
sqlu = sqlu.Offset(int(*req.Msg.Offset)) query.Apply(sm.Offset(*req.Msg.Offset))
} }
// Get items & count // Get items & count
items := []models.Item{} items, err := query.All(ctx, h.db)
var count int64 if err != nil {
if err := sqlu.Order("added desc").Find(&items).Error; err != nil { if errors.Is(err, sql.ErrNoRows) {
return nil, connect.NewError(connect.CodeNotFound, err) return nil, connect.NewError(connect.CodeNotFound, err)
} }
if err := sql.Model(&items).Count(&count).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
count, err := query.Count(ctx, h.db)
if err != nil {
return nil, connect.NewError(connect.CodeInternal, err) return nil, connect.NewError(connect.CodeInternal, err)
} }
// Convert to connect v1 items // Convert to connect v1 items
resItems := []*itemv1.Item{} resItems := []*itemv1.Item{}
for _, item := range items { for _, item := range items {
resItems = append(resItems, item.ToConnectV1()) resItems = append(resItems, itemToConnect(item))
} }
res := connect.NewResponse(&itemv1.GetItemsResponse{ res := connect.NewResponse(&itemv1.GetItemsResponse{
Items: resItems, Items: resItems,
Count: uint64(count), Count: count,
}) })
return res, nil 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")) return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
} }
// Create item item, err := models.Items.Insert(&models.ItemSetter{
item := models.Item{ Name: omit.From(req.Msg.Item.Name),
Name: req.Msg.Item.Name, Description: omitnull.From(req.Msg.Item.Description),
Description: req.Msg.Item.Description, Price: omitnull.From(req.Msg.Item.Price),
Price: req.Msg.Item.Price, Quantity: omitnull.From(int64(req.Msg.Item.Quantity)),
Quantity: int(req.Msg.Item.Quantity), Added: omit.From(time.Now()),
Added: time.Now(), UserID: omit.From(userid),
UserID: uint(userid), }).One(ctx, h.db)
} if err != nil {
if err := h.db.Create(&item).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err) return nil, connect.NewError(connect.CodeInternal, err)
} }
res := connect.NewResponse(&itemv1.CreateItemResponse{ res := connect.NewResponse(&itemv1.CreateItemResponse{
Item: item.ToConnectV1(), Item: itemToConnect(item),
}) })
return res, nil return res, nil
} }
@ -125,20 +161,27 @@ func (h *Handler) UpdateItem(ctx context.Context, req *connect.Request[itemv1.Up
} }
// Update item // Update item
item := models.Item{ item, err := models.Items.Update(
ID: *req.Msg.Item.Id, // Set col
Name: req.Msg.Item.Name, models.ItemSetter{
Description: req.Msg.Item.Description, Name: omit.From(req.Msg.Item.Name),
Price: req.Msg.Item.Price, Description: omitnull.From(req.Msg.Item.Description),
Quantity: int(req.Msg.Item.Quantity), Price: omitnull.From(req.Msg.Item.Price),
UserID: uint(userid), Quantity: omitnull.From(int64(req.Msg.Item.Quantity)),
} }.UpdateMod(),
if err := h.db.Where("id = ? AND user_id = ?", req.Msg.Item.Id, userid).Updates(&item).Error; err != nil {
// 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) return nil, connect.NewError(connect.CodeInternal, err)
} }
res := connect.NewResponse(&itemv1.UpdateItemResponse{ res := connect.NewResponse(&itemv1.UpdateItemResponse{
Item: item.ToConnectV1(), Item: itemToConnect(item),
}) })
return res, nil return res, nil
} }
@ -150,7 +193,13 @@ func (h *Handler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.De
} }
// Delete item // 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) 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 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)) interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key))
return itemv1connect.NewItemServiceHandler( return itemv1connect.NewItemServiceHandler(

View File

@ -2,38 +2,44 @@ package user
import ( import (
"context" "context"
"database/sql"
"errors" "errors"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
_ "crypto/sha256" _ "crypto/sha256" // Crypto
"connectrpc.com/connect" "connectrpc.com/connect"
"github.com/aarondl/opt/omit"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/spotdemo4/trevstack/server/internal/interceptors" "github.com/spotdemo4/trevstack/server/internal/interceptors"
"github.com/spotdemo4/trevstack/server/internal/models" "github.com/spotdemo4/trevstack/server/internal/models"
userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1"
"github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect" "github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect"
"github.com/veraison/go-cose" "github.com/stephenafamo/bob"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
) )
type AuthHandler struct { type AuthHandler struct {
db *gorm.DB db *bob.DB
key []byte key []byte
} }
func (h *AuthHandler) Login(_ context.Context, req *connect.Request[userv1.LoginRequest]) (*connect.Response[userv1.LoginResponse], error) { func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.LoginRequest]) (*connect.Response[userv1.LoginResponse], error) {
// Validate // Get user
user := models.User{} user, err := models.Users.Query(
if err := h.db.First(&user, "username = ?", req.Msg.Username).Error; err != nil { models.SelectWhere.Users.Username.EQ(req.Msg.Username),
if errors.Is(err, gorm.ErrRecordNotFound) { ).One(ctx, h.db)
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("invalid username or password")) if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, connect.NewError(connect.CodePermissionDenied, err)
} }
return nil, connect.NewError(connect.CodeInternal, err) return nil, connect.NewError(connect.CodeInternal, err)
} }
// Check password
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Msg.Password)); err != nil { 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")) 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 return res, nil
} }
func (h *AuthHandler) SignUp(_ context.Context, req *connect.Request[userv1.SignUpRequest]) (*connect.Response[userv1.SignUpResponse], error) { func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.SignUpRequest]) (*connect.Response[userv1.SignUpResponse], error) {
// Validate // Get user
if err := h.db.First(&models.User{}, "username = ?", req.Msg.Username).Error; err != nil { user, err := models.Users.Query(
if !errors.Is(err, gorm.ErrRecordNotFound) { 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) return nil, connect.NewError(connect.CodeInternal, err)
} }
} else { }
if user != nil {
return nil, connect.NewError(connect.CodeAlreadyExists, errors.New("username already exists")) return nil, connect.NewError(connect.CodeAlreadyExists, errors.New("username already exists"))
} }
// Check if confirmation passwords match
if req.Msg.Password != req.Msg.ConfirmPassword { if req.Msg.Password != req.Msg.ConfirmPassword {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("passwords do not match")) 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 // Create user
user := models.User{ _, err = models.Users.Insert(&models.UserSetter{
Username: req.Msg.Username, Username: omit.From(req.Msg.Username),
Password: string(hash), Password: omit.From(string(hash)),
} }).One(ctx, h.db)
if err := h.db.Create(&user).Error; err != nil { if err != nil {
return nil, connect.NewError(connect.CodeInternal, err) return nil, connect.NewError(connect.CodeInternal, err)
} }
@ -121,95 +133,95 @@ func (h *AuthHandler) Logout(_ context.Context, _ *connect.Request[userv1.Logout
return res, nil return res, nil
} }
func (h *AuthHandler) GetPasskeyIDs(_ context.Context, req *connect.Request[userv1.GetPasskeyIDsRequest]) (*connect.Response[userv1.GetPasskeyIDsResponse], error) { // func (h *AuthHandler) GetPasskeyIDs(_ context.Context, req *connect.Request[userv1.GetPasskeyIDsRequest]) (*connect.Response[userv1.GetPasskeyIDsResponse], error) {
// Get user // // Get user
user := models.User{} // user := models.User{}
if err := h.db.Preload("Passkeys").First(&user, "username = ?", req.Msg.Username).Error; err != nil { // if err := h.db.Preload("Passkeys").First(&user, "username = ?", req.Msg.Username).Error; err != nil {
return nil, connect.NewError(connect.CodeNotFound, err) // return nil, connect.NewError(connect.CodeNotFound, err)
} // }
// Get IDs // // Get IDs
ids := []string{} // ids := []string{}
for _, passkey := range user.Passkeys { // for _, passkey := range user.Passkeys {
ids = append(ids, passkey.ID) // ids = append(ids, passkey.ID)
} // }
return connect.NewResponse(&userv1.GetPasskeyIDsResponse{ // return connect.NewResponse(&userv1.GetPasskeyIDsResponse{
PasskeyIds: ids, // PasskeyIds: ids,
}), nil // }), nil
} // }
func (h *AuthHandler) PasskeyLogin(_ context.Context, req *connect.Request[userv1.PasskeyLoginRequest]) (*connect.Response[userv1.PasskeyLoginResponse], error) { // func (h *AuthHandler) PasskeyLogin(_ context.Context, req *connect.Request[userv1.PasskeyLoginRequest]) (*connect.Response[userv1.PasskeyLoginResponse], error) {
// Get passkey // // Get passkey
passkey := models.Passkey{} // passkey := models.Passkey{}
if err := h.db.First(&passkey, "id = ?", req.Msg.Id).Error; err != nil { // if err := h.db.First(&passkey, "id = ?", req.Msg.Id).Error; err != nil {
return nil, connect.NewError(connect.CodeNotFound, err) // return nil, connect.NewError(connect.CodeNotFound, err)
} // }
// create a verifier from a trusted private key // // create a verifier from a trusted private key
var verifier cose.Verifier // var verifier cose.Verifier
var err error // var err error
switch req.Msg.Algorithm { // switch req.Msg.Algorithm {
case -7: // case -7:
verifier, err = cose.NewVerifier(cose.AlgorithmES256, passkey.PublicKey) // verifier, err = cose.NewVerifier(cose.AlgorithmES256, passkey.PublicKey)
case -257: // case -257:
verifier, err = cose.NewVerifier(cose.AlgorithmRS256, passkey.PublicKey) // verifier, err = cose.NewVerifier(cose.AlgorithmRS256, passkey.PublicKey)
default: // default:
return nil, connect.NewError(connect.CodeInternal, errors.New("decode algorithm not implemented")) // return nil, connect.NewError(connect.CodeInternal, errors.New("decode algorithm not implemented"))
} // }
if err != nil { // if err != nil {
return nil, connect.NewError(connect.CodeInternal, err) // return nil, connect.NewError(connect.CodeInternal, err)
} // }
// create a sign message from a raw signature payload // // create a sign message from a raw signature payload
var msg cose.Sign1Message // var msg cose.Sign1Message
if err = msg.UnmarshalCBOR(req.Msg.Signature); err != nil { // if err = msg.UnmarshalCBOR(req.Msg.Signature); err != nil {
return nil, connect.NewError(connect.CodeInternal, err) // return nil, connect.NewError(connect.CodeInternal, err)
} // }
// Validate passkey // // Validate passkey
err = msg.Verify(nil, verifier) // err = msg.Verify(nil, verifier)
if err != nil { // if err != nil {
return nil, connect.NewError(connect.CodeUnauthenticated, err) // return nil, connect.NewError(connect.CodeUnauthenticated, err)
} // }
// Generate JWT // // Generate JWT
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ // t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Issuer: "trevstack", // Issuer: "trevstack",
Subject: strconv.FormatUint(uint64(passkey.UserID), 10), // Subject: strconv.FormatUint(uint64(passkey.UserID), 10),
IssuedAt: &jwt.NumericDate{ // IssuedAt: &jwt.NumericDate{
Time: time.Now(), // Time: time.Now(),
}, // },
ExpiresAt: &jwt.NumericDate{ // ExpiresAt: &jwt.NumericDate{
Time: time.Now().Add(time.Hour * 24), // Time: time.Now().Add(time.Hour * 24),
}, // },
}) // })
ss, err := t.SignedString(h.key) // ss, err := t.SignedString(h.key)
if err != nil { // if err != nil {
return nil, connect.NewError(connect.CodeInternal, err) // return nil, connect.NewError(connect.CodeInternal, err)
} // }
// Create cookie // // Create cookie
cookie := http.Cookie{ // cookie := http.Cookie{
Name: "token", // Name: "token",
Value: ss, // Value: ss,
Path: "/", // Path: "/",
MaxAge: 86400, // MaxAge: 86400,
HttpOnly: true, // HttpOnly: true,
Secure: true, // Secure: true,
SameSite: http.SameSiteStrictMode, // SameSite: http.SameSiteStrictMode,
} // }
res := connect.NewResponse(&userv1.PasskeyLoginResponse{ // res := connect.NewResponse(&userv1.PasskeyLoginResponse{
Token: ss, // Token: ss,
}) // })
res.Header().Set("Set-Cookie", cookie.String()) // res.Header().Set("Set-Cookie", cookie.String())
return res, nil // 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)) interceptors := connect.WithInterceptors(interceptors.NewRateLimitInterceptor(key))
return userv1connect.NewAuthServiceHandler( return userv1connect.NewAuthServiceHandler(

View File

@ -2,23 +2,34 @@ package user
import ( import (
"context" "context"
"database/sql"
"errors" "errors"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
"connectrpc.com/connect" "connectrpc.com/connect"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/spotdemo4/trevstack/server/internal/interceptors" "github.com/spotdemo4/trevstack/server/internal/interceptors"
"github.com/spotdemo4/trevstack/server/internal/models" "github.com/spotdemo4/trevstack/server/internal/models"
userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1"
"github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect" "github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect"
"github.com/stephenafamo/bob"
"golang.org/x/crypto/bcrypt" "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 { type Handler struct {
db *gorm.DB db *bob.DB
key []byte key []byte
} }
@ -29,13 +40,19 @@ func (h *Handler) GetUser(ctx context.Context, _ *connect.Request[userv1.GetUser
} }
// Get user // Get user
user := models.User{} user, err := models.Users.Query(
if err := h.db.Preload("ProfilePicture").First(&user, "id = ?", userid).Error; err != nil { 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) return nil, connect.NewError(connect.CodeInternal, err)
} }
res := connect.NewResponse(&userv1.GetUserResponse{ res := connect.NewResponse(&userv1.GetUserResponse{
User: user.ToConnectV1(), User: userToConnect(user),
}) })
return res, nil return res, nil
} }
@ -47,8 +64,14 @@ func (h *Handler) UpdatePassword(ctx context.Context, req *connect.Request[userv
} }
// Get user // Get user
user := models.User{} user, err := models.Users.Query(
if err := h.db.First(&user, "id = ?", userid).Error; err != nil { 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) return nil, connect.NewError(connect.CodeInternal, err)
} }
@ -67,7 +90,10 @@ func (h *Handler) UpdatePassword(ctx context.Context, req *connect.Request[userv
} }
// Update password // 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) 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 // Get user
user := models.User{} user, err := models.Users.Query(
if err := h.db.First(&user, "id = ?", userid).Error; err != nil { 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) 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 // Generate JWT
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Issuer: "trevstack", Issuer: "trevstack",
Subject: strconv.FormatUint(uint64(user.ID), 10), Subject: strconv.FormatInt(user.ID, 10),
IssuedAt: &jwt.NumericDate{ IssuedAt: &jwt.NumericDate{
Time: time.Now(), Time: time.Now(),
}, },
@ -127,79 +159,88 @@ func (h *Handler) UpdateProfilePicture(ctx context.Context, req *connect.Request
} }
// Save bytes into file // Save bytes into file
file := models.File{ file, err := models.Files.Insert(&models.FileSetter{
Name: req.Msg.FileName, Name: omit.From(req.Msg.FileName),
Data: req.Msg.Data, Data: omit.From(req.Msg.Data),
UserID: uint(userid), UserID: omit.From(userid),
} }).One(ctx, h.db)
if err := h.db.Create(&file).Error; err != nil { if err != nil {
return nil, connect.NewError(connect.CodeInternal, err) return nil, connect.NewError(connect.CodeInternal, err)
} }
// Get user info // Get user
user := models.User{} user, err := models.Users.Query(
if err := h.db.First(&user, "id = ?", userid).Error; err != nil { 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) return nil, connect.NewError(connect.CodeInternal, err)
} }
// Get old profile picture ID // Get old profile picture ID
var ppid *uint32 var ppid *int64
if user.ProfilePicture != nil { if user.ProfilePictureID.Ptr() != nil {
ppid = &user.ProfilePicture.ID ppid = user.ProfilePictureID.Ptr()
} }
// Update user profile picture // Update user profile picture
fid := uint(file.ID) err = user.Update(ctx, h.db, &models.UserSetter{
user.ProfilePictureID = &fid ProfilePictureID: omitnull.From(file.ID),
user.ProfilePicture = &file })
if err := h.db.Save(&user).Error; err != nil { if err != nil {
return nil, connect.NewError(connect.CodeInternal, err) return nil, connect.NewError(connect.CodeInternal, err)
} }
// Delete old profile picture if exists // Delete old profile picture if exists
if ppid != nil { 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) return nil, connect.NewError(connect.CodeInternal, err)
} }
} }
res := connect.NewResponse(&userv1.UpdateProfilePictureResponse{ res := connect.NewResponse(&userv1.UpdateProfilePictureResponse{
User: user.ToConnectV1(), User: userToConnect(user),
}) })
return res, nil return res, nil
} }
func (h *Handler) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) { // func (h *Handler) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) {
// Get user ID from context // // Get user ID from context
userid, ok := interceptors.GetUserContext(ctx) // userid, ok := interceptors.GetUserContext(ctx)
if !ok { // if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated")) // return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated"))
} // }
// Get user // // Get user
user := models.User{} // user := models.User{}
if err := h.db.First(&user, "id = ?", userid).Error; err != nil { // if err := h.db.First(&user, "id = ?", userid).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err) // 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) { // func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.FinishPasskeyRegistrationRequest]) (*connect.Response[userv1.FinishPasskeyRegistrationResponse], error) {
// Get user ID from context // // Get user ID from context
userid, ok := interceptors.GetUserContext(ctx) // userid, ok := interceptors.GetUserContext(ctx)
if !ok { // if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated")) // return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated"))
} // }
// Get user // // Get user
user := models.User{} // user := models.User{}
if err := h.db.First(&user, "id = ?", userid).Error; err != nil { // if err := h.db.First(&user, "id = ?", userid).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err) // 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 { // func BeginRegistration(ctx context.Context) error {
// userid, ok := interceptors.GetUserContext(ctx) // userid, ok := interceptors.GetUserContext(ctx)
@ -231,7 +272,7 @@ func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Re
// return nil // 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)) interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key))
return userv1connect.NewUserServiceHandler( return userv1connect.NewUserServiceHandler(

View File

@ -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. // key is an unexported type for keys defined in this package.
// This prevents collisions with keys defined in other packages. // 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 // userKey is the key for user.User values in Contexts. It is
// unexported; clients use user.NewContext and user.FromContext // 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 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. // getUserContext returns the User value stored in ctx, if any.
func GetUserContext(ctx context.Context) (int, bool) { func GetUserContext(ctx context.Context) (int64, bool) {
u, ok := ctx.Value(userKey).(int) u, ok := ctx.Value(userKey).(int64)
return u, ok return u, ok
} }

View File

@ -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)
}

View File

@ -46,7 +46,7 @@ func (i *RatelimitInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFu
// Get user agent // Get user agent
limiter := i.getVisitor(req.Header().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")) return nil, connect.NewError(connect.CodeResourceExhausted, errors.New("rate limit exceeded"))
} }
@ -70,7 +70,7 @@ func (i *RatelimitInterceptor) WrapStreamingHandler(next connect.StreamingHandle
) error { ) error {
// Get user agent // Get user agent
limiter := i.getVisitor(conn.RequestHeader().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")) return connect.NewError(connect.CodeResourceExhausted, errors.New("rate limit exceeded"))
} }

View File

@ -19,9 +19,9 @@ var TableNames = struct {
Items string Items string
Users string Users string
}{ }{
Files: "files", Files: "file",
Items: "items", Items: "item",
Users: "users", Users: "user",
} }
var ColumnNames = struct { var ColumnNames = struct {
@ -38,10 +38,10 @@ var ColumnNames = struct {
Items: itemColumnNames{ Items: itemColumnNames{
ID: "id", ID: "id",
Name: "name", Name: "name",
Added: "added",
Description: "description", Description: "description",
Price: "price", Price: "price",
Quantity: "quantity", Quantity: "quantity",
Added: "added",
UserID: "user_id", UserID: "user_id",
}, },
Users: userColumnNames{ Users: userColumnNames{
@ -49,7 +49,6 @@ var ColumnNames = struct {
Username: "username", Username: "username",
Password: "password", Password: "password",
ProfilePictureID: "profile_picture_id", ProfilePictureID: "profile_picture_id",
Challenge: "challenge",
}, },
} }

View File

@ -28,12 +28,12 @@ func random_float32(f *faker.Faker) float32 {
return f.Float32(10, -1_000_000, 1_000_000) 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 { if f == nil {
f = &defaultFaker f = &defaultFaker
} }
return f.Int32() return f.Int64()
} }
func random_string(f *faker.Faker) string { func random_string(f *faker.Faker) string {

View File

@ -8,14 +8,14 @@ import (
"testing" "testing"
) )
func TestRandom_int32(t *testing.T) { func TestRandom_int64(t *testing.T) {
t.Parallel() t.Parallel()
val1 := random_int32(nil) val1 := random_int64(nil)
val2 := random_int32(nil) val2 := random_int64(nil)
if val1 == val2 { 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) { func TestRandom_time_Time(t *testing.T) {
t.Parallel() 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) 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)
}
}

View File

@ -9,7 +9,6 @@ import (
"github.com/aarondl/opt/null" "github.com/aarondl/opt/null"
"github.com/aarondl/opt/omit" "github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/jaswdr/faker/v2" "github.com/jaswdr/faker/v2"
models "github.com/spotdemo4/trevstack/server/internal/models" models "github.com/spotdemo4/trevstack/server/internal/models"
"github.com/stephenafamo/bob" "github.com/stephenafamo/bob"
@ -36,10 +35,10 @@ func (mods FileModSlice) Apply(n *FileTemplate) {
// FileTemplate is an object representing the database table. // FileTemplate is an object representing the database table.
// all columns are optional and should be set by mods // all columns are optional and should be set by mods
type FileTemplate struct { type FileTemplate struct {
ID func() int32 ID func() int64
Name func() null.Val[string] Name func() string
Data func() null.Val[[]byte] Data func() []byte
UserID func() null.Val[int32] UserID func() int64
r fileR r fileR
f *Factory f *Factory
@ -47,11 +46,16 @@ type FileTemplate struct {
type fileR struct { type fileR struct {
User *fileRUserR User *fileRUserR
ProfilePictureUsers []*fileRProfilePictureUsersR
} }
type fileRUserR struct { type fileRUserR struct {
o *UserTemplate o *UserTemplate
} }
type fileRProfilePictureUsersR struct {
number int
o *UserTemplate
}
// Apply mods to the FileTemplate // Apply mods to the FileTemplate
func (o *FileTemplate) Apply(mods ...FileMod) { func (o *FileTemplate) Apply(mods ...FileMod) {
@ -99,9 +103,22 @@ func (t FileTemplate) setModelRels(o *models.File) {
if t.r.User != nil { if t.r.User != nil {
rel := t.r.User.o.toModel() rel := t.r.User.o.toModel()
rel.R.Files = append(rel.R.Files, o) rel.R.Files = append(rel.R.Files, o)
o.UserID = null.From(rel.ID) o.UserID = rel.ID
o.R.User = rel 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 // BuildSetter returns an *models.FileSetter
@ -113,13 +130,13 @@ func (o FileTemplate) BuildSetter() *models.FileSetter {
m.ID = omit.From(o.ID()) m.ID = omit.From(o.ID())
} }
if o.Name != nil { if o.Name != nil {
m.Name = omitnull.FromNull(o.Name()) m.Name = omit.From(o.Name())
} }
if o.Data != nil { if o.Data != nil {
m.Data = omitnull.FromNull(o.Data()) m.Data = omit.From(o.Data())
} }
if o.UserID != nil { if o.UserID != nil {
m.UserID = omitnull.FromNull(o.UserID()) m.UserID = omit.From(o.UserID())
} }
return m return m
@ -161,6 +178,15 @@ func (o FileTemplate) BuildMany(number int) models.FileSlice {
} }
func ensureCreatableFile(m *models.FileSetter) { 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 // insertOptRels creates and inserts any optional the relationships on *models.File
@ -169,17 +195,20 @@ func ensureCreatableFile(m *models.FileSetter) {
func (o *FileTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.File) (context.Context, error) { func (o *FileTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.File) (context.Context, error) {
var err error var err error
if o.r.User != nil { if o.r.ProfilePictureUsers != nil {
var rel0 *models.User for _, r := range o.r.ProfilePictureUsers {
ctx, rel0, err = o.r.User.o.create(ctx, exec) var rel1 models.UserSlice
ctx, rel1, err = r.o.createMany(ctx, exec, r.number)
if err != nil { if err != nil {
return ctx, err return ctx, err
} }
err = m.AttachUser(ctx, exec, rel0)
err = m.AttachProfilePictureUsers(ctx, exec, rel1...)
if err != nil { if err != nil {
return ctx, err return ctx, err
} }
} }
}
return ctx, err return ctx, err
} }
@ -223,12 +252,30 @@ func (o *FileTemplate) create(ctx context.Context, exec bob.Executor) (context.C
opt := o.BuildSetter() opt := o.BuildSetter()
ensureCreatableFile(opt) 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) m, err := models.Files.Insert(opt).One(ctx, exec)
if err != nil { if err != nil {
return ctx, nil, err return ctx, nil, err
} }
ctx = fileCtx.WithValue(ctx, m) ctx = fileCtx.WithValue(ctx, m)
m.R.User = rel0
ctx, err = o.insertOptRels(ctx, exec, m) ctx, err = o.insertOptRels(ctx, exec, m)
return ctx, m, err return ctx, m, err
} }
@ -296,14 +343,14 @@ func (m fileMods) RandomizeAllColumns(f *faker.Faker) FileMod {
} }
// Set the model columns to this value // 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) { return FileModFunc(func(o *FileTemplate) {
o.ID = func() int32 { return val } o.ID = func() int64 { return val }
}) })
} }
// Set the Column from the function // 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) { return FileModFunc(func(o *FileTemplate) {
o.ID = f o.ID = f
}) })
@ -320,21 +367,21 @@ func (m fileMods) UnsetID() FileMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m fileMods) RandomID(f *faker.Faker) FileMod { func (m fileMods) RandomID(f *faker.Faker) FileMod {
return FileModFunc(func(o *FileTemplate) { return FileModFunc(func(o *FileTemplate) {
o.ID = func() int32 { o.ID = func() int64 {
return random_int32(f) return random_int64(f)
} }
}) })
} }
// Set the model columns to this value // 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) { 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 // 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) { return FileModFunc(func(o *FileTemplate) {
o.Name = f o.Name = f
}) })
@ -351,29 +398,21 @@ func (m fileMods) UnsetName() FileMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m fileMods) RandomName(f *faker.Faker) FileMod { func (m fileMods) RandomName(f *faker.Faker) FileMod {
return FileModFunc(func(o *FileTemplate) { return FileModFunc(func(o *FileTemplate) {
o.Name = func() null.Val[string] { o.Name = func() string {
if f == nil { return random_string(f)
f = &defaultFaker
}
if f.Bool() {
return null.FromPtr[string](nil)
}
return null.From(random_string(f))
} }
}) })
} }
// Set the model columns to this value // 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) { 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 // 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) { return FileModFunc(func(o *FileTemplate) {
o.Data = f o.Data = f
}) })
@ -390,29 +429,21 @@ func (m fileMods) UnsetData() FileMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m fileMods) RandomData(f *faker.Faker) FileMod { func (m fileMods) RandomData(f *faker.Faker) FileMod {
return FileModFunc(func(o *FileTemplate) { return FileModFunc(func(o *FileTemplate) {
o.Data = func() null.Val[[]byte] { o.Data = func() []byte {
if f == nil { return random___byte(f)
f = &defaultFaker
}
if f.Bool() {
return null.FromPtr[[]byte](nil)
}
return null.From(random___byte(f))
} }
}) })
} }
// Set the model columns to this value // 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) { 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 // 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) { return FileModFunc(func(o *FileTemplate) {
o.UserID = f o.UserID = f
}) })
@ -429,16 +460,8 @@ func (m fileMods) UnsetUserID() FileMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m fileMods) RandomUserID(f *faker.Faker) FileMod { func (m fileMods) RandomUserID(f *faker.Faker) FileMod {
return FileModFunc(func(o *FileTemplate) { return FileModFunc(func(o *FileTemplate) {
o.UserID = func() null.Val[int32] { o.UserID = func() int64 {
if f == nil { return random_int64(f)
f = &defaultFaker
}
if f.Bool() {
return null.FromPtr[int32](nil)
}
return null.From(random_int32(f))
} }
}) })
} }
@ -464,3 +487,41 @@ func (m fileMods) WithoutUser() FileMod {
o.r.User = nil 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
})
}

View File

@ -37,13 +37,13 @@ func (mods ItemModSlice) Apply(n *ItemTemplate) {
// ItemTemplate is an object representing the database table. // ItemTemplate is an object representing the database table.
// all columns are optional and should be set by mods // all columns are optional and should be set by mods
type ItemTemplate struct { type ItemTemplate struct {
ID func() int32 ID func() int64
Name func() null.Val[string] Name func() string
Added func() time.Time
Description func() null.Val[string] Description func() null.Val[string]
Price func() null.Val[float32] Price func() null.Val[float32]
Quantity func() null.Val[int32] Quantity func() null.Val[int64]
Added func() null.Val[time.Time] UserID func() int64
UserID func() null.Val[int32]
r itemR r itemR
f *Factory f *Factory
@ -75,6 +75,9 @@ func (o ItemTemplate) toModel() *models.Item {
if o.Name != nil { if o.Name != nil {
m.Name = o.Name() m.Name = o.Name()
} }
if o.Added != nil {
m.Added = o.Added()
}
if o.Description != nil { if o.Description != nil {
m.Description = o.Description() m.Description = o.Description()
} }
@ -84,9 +87,6 @@ func (o ItemTemplate) toModel() *models.Item {
if o.Quantity != nil { if o.Quantity != nil {
m.Quantity = o.Quantity() m.Quantity = o.Quantity()
} }
if o.Added != nil {
m.Added = o.Added()
}
if o.UserID != nil { if o.UserID != nil {
m.UserID = o.UserID() m.UserID = o.UserID()
} }
@ -112,7 +112,7 @@ func (t ItemTemplate) setModelRels(o *models.Item) {
if t.r.User != nil { if t.r.User != nil {
rel := t.r.User.o.toModel() rel := t.r.User.o.toModel()
rel.R.Items = append(rel.R.Items, o) rel.R.Items = append(rel.R.Items, o)
o.UserID = null.From(rel.ID) o.UserID = rel.ID
o.R.User = rel o.R.User = rel
} }
} }
@ -126,7 +126,10 @@ func (o ItemTemplate) BuildSetter() *models.ItemSetter {
m.ID = omit.From(o.ID()) m.ID = omit.From(o.ID())
} }
if o.Name != nil { 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 { if o.Description != nil {
m.Description = omitnull.FromNull(o.Description()) m.Description = omitnull.FromNull(o.Description())
@ -137,11 +140,8 @@ func (o ItemTemplate) BuildSetter() *models.ItemSetter {
if o.Quantity != nil { if o.Quantity != nil {
m.Quantity = omitnull.FromNull(o.Quantity()) m.Quantity = omitnull.FromNull(o.Quantity())
} }
if o.Added != nil {
m.Added = omitnull.FromNull(o.Added())
}
if o.UserID != nil { if o.UserID != nil {
m.UserID = omitnull.FromNull(o.UserID()) m.UserID = omit.From(o.UserID())
} }
return m return m
@ -183,6 +183,15 @@ func (o ItemTemplate) BuildMany(number int) models.ItemSlice {
} }
func ensureCreatableItem(m *models.ItemSetter) { 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 // 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) { func (o *ItemTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.Item) (context.Context, error) {
var err 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 return ctx, err
} }
@ -245,12 +242,30 @@ func (o *ItemTemplate) create(ctx context.Context, exec bob.Executor) (context.C
opt := o.BuildSetter() opt := o.BuildSetter()
ensureCreatableItem(opt) 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) m, err := models.Items.Insert(opt).One(ctx, exec)
if err != nil { if err != nil {
return ctx, nil, err return ctx, nil, err
} }
ctx = itemCtx.WithValue(ctx, m) ctx = itemCtx.WithValue(ctx, m)
m.R.User = rel0
ctx, err = o.insertOptRels(ctx, exec, m) ctx, err = o.insertOptRels(ctx, exec, m)
return ctx, m, err return ctx, m, err
} }
@ -312,23 +327,23 @@ func (m itemMods) RandomizeAllColumns(f *faker.Faker) ItemMod {
return ItemModSlice{ return ItemModSlice{
ItemMods.RandomID(f), ItemMods.RandomID(f),
ItemMods.RandomName(f), ItemMods.RandomName(f),
ItemMods.RandomAdded(f),
ItemMods.RandomDescription(f), ItemMods.RandomDescription(f),
ItemMods.RandomPrice(f), ItemMods.RandomPrice(f),
ItemMods.RandomQuantity(f), ItemMods.RandomQuantity(f),
ItemMods.RandomAdded(f),
ItemMods.RandomUserID(f), ItemMods.RandomUserID(f),
} }
} }
// Set the model columns to this value // 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) { return ItemModFunc(func(o *ItemTemplate) {
o.ID = func() int32 { return val } o.ID = func() int64 { return val }
}) })
} }
// Set the Column from the function // 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) { return ItemModFunc(func(o *ItemTemplate) {
o.ID = f o.ID = f
}) })
@ -345,21 +360,21 @@ func (m itemMods) UnsetID() ItemMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m itemMods) RandomID(f *faker.Faker) ItemMod { func (m itemMods) RandomID(f *faker.Faker) ItemMod {
return ItemModFunc(func(o *ItemTemplate) { return ItemModFunc(func(o *ItemTemplate) {
o.ID = func() int32 { o.ID = func() int64 {
return random_int32(f) return random_int64(f)
} }
}) })
} }
// Set the model columns to this value // 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) { 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 // 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) { return ItemModFunc(func(o *ItemTemplate) {
o.Name = f o.Name = f
}) })
@ -376,16 +391,39 @@ func (m itemMods) UnsetName() ItemMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m itemMods) RandomName(f *faker.Faker) ItemMod { func (m itemMods) RandomName(f *faker.Faker) ItemMod {
return ItemModFunc(func(o *ItemTemplate) { return ItemModFunc(func(o *ItemTemplate) {
o.Name = func() null.Val[string] { o.Name = func() string {
if f == nil { return random_string(f)
f = &defaultFaker }
})
} }
if f.Bool() { // Set the model columns to this value
return null.FromPtr[string](nil) 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 // 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) { 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 // 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) { return ItemModFunc(func(o *ItemTemplate) {
o.Quantity = f o.Quantity = f
}) })
@ -493,68 +531,29 @@ func (m itemMods) UnsetQuantity() ItemMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m itemMods) RandomQuantity(f *faker.Faker) ItemMod { func (m itemMods) RandomQuantity(f *faker.Faker) ItemMod {
return ItemModFunc(func(o *ItemTemplate) { return ItemModFunc(func(o *ItemTemplate) {
o.Quantity = func() null.Val[int32] { o.Quantity = func() null.Val[int64] {
if f == nil { if f == nil {
f = &defaultFaker f = &defaultFaker
} }
if f.Bool() { 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 // 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) { 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 // Set the Column from the function
func (m itemMods) AddedFunc(f func() null.Val[time.Time]) ItemMod { func (m itemMods) UserIDFunc(f func() int64) 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 {
return ItemModFunc(func(o *ItemTemplate) { return ItemModFunc(func(o *ItemTemplate) {
o.UserID = f o.UserID = f
}) })
@ -571,16 +570,8 @@ func (m itemMods) UnsetUserID() ItemMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m itemMods) RandomUserID(f *faker.Faker) ItemMod { func (m itemMods) RandomUserID(f *faker.Faker) ItemMod {
return ItemModFunc(func(o *ItemTemplate) { return ItemModFunc(func(o *ItemTemplate) {
o.UserID = func() null.Val[int32] { o.UserID = func() int64 {
if f == nil { return random_int64(f)
f = &defaultFaker
}
if f.Bool() {
return null.FromPtr[int32](nil)
}
return null.From(random_int32(f))
} }
}) })
} }

View File

@ -36,11 +36,10 @@ func (mods UserModSlice) Apply(n *UserTemplate) {
// UserTemplate is an object representing the database table. // UserTemplate is an object representing the database table.
// all columns are optional and should be set by mods // all columns are optional and should be set by mods
type UserTemplate struct { type UserTemplate struct {
ID func() int32 ID func() int64
Username func() null.Val[string] Username func() string
Password func() null.Val[string] Password func() string
ProfilePictureID func() null.Val[int32] ProfilePictureID func() null.Val[int64]
Challenge func() null.Val[string]
r userR r userR
f *Factory f *Factory
@ -49,6 +48,7 @@ type UserTemplate struct {
type userR struct { type userR struct {
Files []*userRFilesR Files []*userRFilesR
Items []*userRItemsR Items []*userRItemsR
ProfilePictureFile *userRProfilePictureFileR
} }
type userRFilesR struct { type userRFilesR struct {
@ -59,6 +59,9 @@ type userRItemsR struct {
number int number int
o *ItemTemplate o *ItemTemplate
} }
type userRProfilePictureFileR struct {
o *FileTemplate
}
// Apply mods to the UserTemplate // Apply mods to the UserTemplate
func (o *UserTemplate) Apply(mods ...UserMod) { func (o *UserTemplate) Apply(mods ...UserMod) {
@ -84,9 +87,6 @@ func (o UserTemplate) toModel() *models.User {
if o.ProfilePictureID != nil { if o.ProfilePictureID != nil {
m.ProfilePictureID = o.ProfilePictureID() m.ProfilePictureID = o.ProfilePictureID()
} }
if o.Challenge != nil {
m.Challenge = o.Challenge()
}
return m return m
} }
@ -111,7 +111,7 @@ func (t UserTemplate) setModelRels(o *models.User) {
for _, r := range t.r.Files { for _, r := range t.r.Files {
related := r.o.toModels(r.number) related := r.o.toModels(r.number)
for _, rel := range related { for _, rel := range related {
rel.UserID = null.From(o.ID) rel.UserID = o.ID
rel.R.User = o rel.R.User = o
} }
rel = append(rel, related...) rel = append(rel, related...)
@ -124,13 +124,20 @@ func (t UserTemplate) setModelRels(o *models.User) {
for _, r := range t.r.Items { for _, r := range t.r.Items {
related := r.o.toModels(r.number) related := r.o.toModels(r.number)
for _, rel := range related { for _, rel := range related {
rel.UserID = null.From(o.ID) rel.UserID = o.ID
rel.R.User = o rel.R.User = o
} }
rel = append(rel, related...) rel = append(rel, related...)
} }
o.R.Items = rel 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 // BuildSetter returns an *models.UserSetter
@ -142,17 +149,14 @@ func (o UserTemplate) BuildSetter() *models.UserSetter {
m.ID = omit.From(o.ID()) m.ID = omit.From(o.ID())
} }
if o.Username != nil { if o.Username != nil {
m.Username = omitnull.FromNull(o.Username()) m.Username = omit.From(o.Username())
} }
if o.Password != nil { if o.Password != nil {
m.Password = omitnull.FromNull(o.Password()) m.Password = omit.From(o.Password())
} }
if o.ProfilePictureID != nil { if o.ProfilePictureID != nil {
m.ProfilePictureID = omitnull.FromNull(o.ProfilePictureID()) m.ProfilePictureID = omitnull.FromNull(o.ProfilePictureID())
} }
if o.Challenge != nil {
m.Challenge = omitnull.FromNull(o.Challenge())
}
return m return m
} }
@ -193,6 +197,12 @@ func (o UserTemplate) BuildMany(number int) models.UserSlice {
} }
func ensureCreatableUser(m *models.UserSetter) { 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 // 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 return ctx, err
} }
@ -342,19 +364,18 @@ func (m userMods) RandomizeAllColumns(f *faker.Faker) UserMod {
UserMods.RandomUsername(f), UserMods.RandomUsername(f),
UserMods.RandomPassword(f), UserMods.RandomPassword(f),
UserMods.RandomProfilePictureID(f), UserMods.RandomProfilePictureID(f),
UserMods.RandomChallenge(f),
} }
} }
// Set the model columns to this value // 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) { return UserModFunc(func(o *UserTemplate) {
o.ID = func() int32 { return val } o.ID = func() int64 { return val }
}) })
} }
// Set the Column from the function // 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) { return UserModFunc(func(o *UserTemplate) {
o.ID = f o.ID = f
}) })
@ -371,21 +392,21 @@ func (m userMods) UnsetID() UserMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m userMods) RandomID(f *faker.Faker) UserMod { func (m userMods) RandomID(f *faker.Faker) UserMod {
return UserModFunc(func(o *UserTemplate) { return UserModFunc(func(o *UserTemplate) {
o.ID = func() int32 { o.ID = func() int64 {
return random_int32(f) return random_int64(f)
} }
}) })
} }
// Set the model columns to this value // 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) { 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 // 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) { return UserModFunc(func(o *UserTemplate) {
o.Username = f o.Username = f
}) })
@ -402,29 +423,21 @@ func (m userMods) UnsetUsername() UserMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m userMods) RandomUsername(f *faker.Faker) UserMod { func (m userMods) RandomUsername(f *faker.Faker) UserMod {
return UserModFunc(func(o *UserTemplate) { return UserModFunc(func(o *UserTemplate) {
o.Username = func() null.Val[string] { o.Username = func() string {
if f == nil { return random_string(f)
f = &defaultFaker
}
if f.Bool() {
return null.FromPtr[string](nil)
}
return null.From(random_string(f))
} }
}) })
} }
// Set the model columns to this value // 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) { 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 // 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) { return UserModFunc(func(o *UserTemplate) {
o.Password = f o.Password = f
}) })
@ -441,29 +454,21 @@ func (m userMods) UnsetPassword() UserMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m userMods) RandomPassword(f *faker.Faker) UserMod { func (m userMods) RandomPassword(f *faker.Faker) UserMod {
return UserModFunc(func(o *UserTemplate) { return UserModFunc(func(o *UserTemplate) {
o.Password = func() null.Val[string] { o.Password = func() string {
if f == nil { return random_string(f)
f = &defaultFaker
}
if f.Bool() {
return null.FromPtr[string](nil)
}
return null.From(random_string(f))
} }
}) })
} }
// Set the model columns to this value // 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) { 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 // 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) { return UserModFunc(func(o *UserTemplate) {
o.ProfilePictureID = f o.ProfilePictureID = f
}) })
@ -480,56 +485,39 @@ func (m userMods) UnsetProfilePictureID() UserMod {
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
func (m userMods) RandomProfilePictureID(f *faker.Faker) UserMod { func (m userMods) RandomProfilePictureID(f *faker.Faker) UserMod {
return UserModFunc(func(o *UserTemplate) { return UserModFunc(func(o *UserTemplate) {
o.ProfilePictureID = func() null.Val[int32] { o.ProfilePictureID = func() null.Val[int64] {
if f == nil { if f == nil {
f = &defaultFaker f = &defaultFaker
} }
if f.Bool() { 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) WithProfilePictureFile(rel *FileTemplate) UserMod {
func (m userMods) Challenge(val null.Val[string]) UserMod {
return UserModFunc(func(o *UserTemplate) { return UserModFunc(func(o *UserTemplate) {
o.Challenge = func() null.Val[string] { return val } o.r.ProfilePictureFile = &userRProfilePictureFileR{
o: rel,
}
}) })
} }
// Set the Column from the function func (m userMods) WithNewProfilePictureFile(mods ...FileMod) UserMod {
func (m userMods) ChallengeFunc(f func() null.Val[string]) UserMod {
return UserModFunc(func(o *UserTemplate) { return UserModFunc(func(o *UserTemplate) {
o.Challenge = f related := o.f.NewFile(mods...)
m.WithProfilePictureFile(related).Apply(o)
}) })
} }
// Clear any values for the column func (m userMods) WithoutProfilePictureFile() UserMod {
func (m userMods) UnsetChallenge() UserMod {
return UserModFunc(func(o *UserTemplate) { return UserModFunc(func(o *UserTemplate) {
o.Challenge = nil o.r.ProfilePictureFile = 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))
}
}) })
} }

View File

@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/aarondl/opt/null"
"github.com/aarondl/opt/omit" "github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull" "github.com/aarondl/opt/omitnull"
"github.com/stephenafamo/bob" "github.com/stephenafamo/bob"
@ -26,10 +25,10 @@ import (
// File is an object representing the database table. // File is an object representing the database table.
type File struct { type File struct {
ID int32 `db:"id,pk" ` ID int64 `db:"id,pk" `
Name null.Val[string] `db:"name" ` Name string `db:"name" `
Data null.Val[[]byte] `db:"data" ` Data []byte `db:"data" `
UserID null.Val[int32] `db:"user_id" ` UserID int64 `db:"user_id" `
R fileR `db:"-" ` R fileR `db:"-" `
} }
@ -38,15 +37,16 @@ type File struct {
// This should almost always be used instead of []*File. // This should almost always be used instead of []*File.
type FileSlice []*File type FileSlice []*File
// Files contains methods to work with the files table // Files contains methods to work with the file table
var Files = sqlite.NewTablex[*File, FileSlice, *FileSetter]("", "files") 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] type FilesQuery = *sqlite.ViewQuery[*File, FileSlice]
// fileR is where relationships are stored. // fileR is where relationships are stored.
type fileR struct { type fileR struct {
User *User // fk_files_0 User *User // fk_file_0
ProfilePictureUsers UserSlice // fk_user_0
} }
type fileColumnNames struct { type fileColumnNames struct {
@ -56,7 +56,7 @@ type fileColumnNames struct {
UserID string UserID string
} }
var FileColumns = buildFileColumns("files") var FileColumns = buildFileColumns("file")
type fileColumns struct { type fileColumns struct {
tableAlias string tableAlias string
@ -85,10 +85,10 @@ func buildFileColumns(alias string) fileColumns {
} }
type fileWhere[Q sqlite.Filterable] struct { type fileWhere[Q sqlite.Filterable] struct {
ID sqlite.WhereMod[Q, int32] ID sqlite.WhereMod[Q, int64]
Name sqlite.WhereNullMod[Q, string] Name sqlite.WhereMod[Q, string]
Data sqlite.WhereNullMod[Q, []byte] Data sqlite.WhereMod[Q, []byte]
UserID sqlite.WhereNullMod[Q, int32] UserID sqlite.WhereMod[Q, int64]
} }
func (fileWhere[Q]) AliasedAs(alias string) fileWhere[Q] { 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] { func buildFileWhere[Q sqlite.Filterable](cols fileColumns) fileWhere[Q] {
return fileWhere[Q]{ return fileWhere[Q]{
ID: sqlite.Where[Q, int32](cols.ID), ID: sqlite.Where[Q, int64](cols.ID),
Name: sqlite.WhereNull[Q, string](cols.Name), Name: sqlite.Where[Q, string](cols.Name),
Data: sqlite.WhereNull[Q, []byte](cols.Data), Data: sqlite.Where[Q, []byte](cols.Data),
UserID: sqlite.WhereNull[Q, int32](cols.UserID), UserID: sqlite.Where[Q, int64](cols.UserID),
} }
} }
var FileErrors = &fileErrors{ var FileErrors = &fileErrors{
ErrUniquePkMainFiles: &UniqueConstraintError{s: "pk_main_files"}, ErrUniquePkMainFile: &UniqueConstraintError{s: "pk_main_file"},
} }
type fileErrors struct { type fileErrors struct {
ErrUniquePkMainFiles *UniqueConstraintError ErrUniquePkMainFile *UniqueConstraintError
} }
// FileSetter is used for insert/upsert/update operations // FileSetter is used for insert/upsert/update operations
// All values are optional, and do not have to be set // All values are optional, and do not have to be set
// Generated columns are not included // Generated columns are not included
type FileSetter struct { type FileSetter struct {
ID omit.Val[int32] `db:"id,pk" ` ID omit.Val[int64] `db:"id,pk" `
Name omitnull.Val[string] `db:"name" ` Name omit.Val[string] `db:"name" `
Data omitnull.Val[[]byte] `db:"data" ` Data omit.Val[[]byte] `db:"data" `
UserID omitnull.Val[int32] `db:"user_id" ` UserID omit.Val[int64] `db:"user_id" `
} }
func (s FileSetter) SetColumns() []string { func (s FileSetter) SetColumns() []string {
@ -148,13 +148,13 @@ func (s FileSetter) Overwrite(t *File) {
t.ID, _ = s.ID.Get() t.ID, _ = s.ID.Get()
} }
if !s.Name.IsUnset() { if !s.Name.IsUnset() {
t.Name, _ = s.Name.GetNull() t.Name, _ = s.Name.Get()
} }
if !s.Data.IsUnset() { if !s.Data.IsUnset() {
t.Data, _ = s.Data.GetNull() t.Data, _ = s.Data.Get()
} }
if !s.UserID.IsUnset() { 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 // FindFile retrieves a single record by primary key
// If cols is empty Find will return all columns. // 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 { if len(cols) == 0 {
return Files.Query( return Files.Query(
SelectWhere.Files.ID.EQ(IDPK), 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 // 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( return Files.Query(
SelectWhere.Files.ID.EQ(IDPK), SelectWhere.Files.ID.EQ(IDPK),
).Exists(ctx, exec) ).Exists(ctx, exec)
@ -273,7 +273,7 @@ func (o *File) PrimaryKeyVals() bob.Expression {
} }
func (o *File) pkEQ() dialect.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) return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start)
})) }))
} }
@ -334,7 +334,7 @@ func (o FileSlice) pkIN() dialect.Expression {
return sqlite.Raw("NULL") 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)) pkPairs := make([]bob.Expression, len(o))
for i, row := range o { for i, row := range o {
pkPairs[i] = row.PrimaryKeyVals() pkPairs[i] = row.PrimaryKeyVals()
@ -453,6 +453,7 @@ func (o FileSlice) ReloadAll(ctx context.Context, exec bob.Executor) error {
type fileJoins[Q dialect.Joinable] struct { type fileJoins[Q dialect.Joinable] struct {
typ string typ string
User func(context.Context) modAs[Q, userColumns] User func(context.Context) modAs[Q, userColumns]
ProfilePictureUsers func(context.Context) modAs[Q, userColumns]
} }
func (j fileJoins[Q]) aliasedAs(alias string) fileJoins[Q] { func (j fileJoins[Q]) aliasedAs(alias string) fileJoins[Q] {
@ -463,6 +464,7 @@ func buildFileJoins[Q dialect.Joinable](cols fileColumns, typ string) fileJoins[
return fileJoins[Q]{ return fileJoins[Q]{
typ: typ, typ: typ,
User: filesJoinUser[Q](cols, 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 { func (o *File) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery {
return Users.Query(append(mods, return Users.Query(append(mods,
sm.Where(UserColumns.ID.EQ(sqlite.Arg(o.UserID))), 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 { func (o *File) Preload(name string, retrieved any) error {
if o == nil { if o == nil {
return nil return nil
@ -521,6 +560,20 @@ func (o *File) Preload(name string, retrieved any) error {
rel.R.Files = FileSlice{o} rel.R.Files = FileSlice{o}
} }
return nil 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: default:
return fmt.Errorf("file has no relationship %q", name) 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 _, o := range os {
for _, rel := range users { for _, rel := range users {
if o.UserID.GetOrZero() != rel.ID { if o.UserID != rel.ID {
continue continue
} }
@ -611,9 +664,81 @@ func (os FileSlice) LoadFileUser(ctx context.Context, exec bob.Executor, mods ..
return nil 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) { func attachFileUser0(ctx context.Context, exec bob.Executor, count int, file0 *File, user1 *User) (*File, error) {
setter := &FileSetter{ setter := &FileSetter{
UserID: omitnull.From(user1.ID), UserID: omit.From(user1.ID),
} }
err := file0.Update(ctx, exec, setter) err := file0.Update(ctx, exec, setter)
@ -656,3 +781,71 @@ func (file0 *File) AttachUser(ctx context.Context, exec bob.Executor, user1 *Use
return nil 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
}

View File

@ -27,13 +27,13 @@ import (
// Item is an object representing the database table. // Item is an object representing the database table.
type Item struct { type Item struct {
ID int32 `db:"id,pk" ` ID int64 `db:"id,pk" `
Name null.Val[string] `db:"name" ` Name string `db:"name" `
Added time.Time `db:"added" `
Description null.Val[string] `db:"description" ` Description null.Val[string] `db:"description" `
Price null.Val[float32] `db:"price" ` Price null.Val[float32] `db:"price" `
Quantity null.Val[int32] `db:"quantity" ` Quantity null.Val[int64] `db:"quantity" `
Added null.Val[time.Time] `db:"added" ` UserID int64 `db:"user_id" `
UserID null.Val[int32] `db:"user_id" `
R itemR `db:"-" ` R itemR `db:"-" `
} }
@ -42,37 +42,37 @@ type Item struct {
// This should almost always be used instead of []*Item. // This should almost always be used instead of []*Item.
type ItemSlice []*Item type ItemSlice []*Item
// Items contains methods to work with the items table // Items contains methods to work with the item table
var Items = sqlite.NewTablex[*Item, ItemSlice, *ItemSetter]("", "items") 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] type ItemsQuery = *sqlite.ViewQuery[*Item, ItemSlice]
// itemR is where relationships are stored. // itemR is where relationships are stored.
type itemR struct { type itemR struct {
User *User // fk_items_0 User *User // fk_item_0
} }
type itemColumnNames struct { type itemColumnNames struct {
ID string ID string
Name string Name string
Added string
Description string Description string
Price string Price string
Quantity string Quantity string
Added string
UserID string UserID string
} }
var ItemColumns = buildItemColumns("items") var ItemColumns = buildItemColumns("item")
type itemColumns struct { type itemColumns struct {
tableAlias string tableAlias string
ID sqlite.Expression ID sqlite.Expression
Name sqlite.Expression Name sqlite.Expression
Added sqlite.Expression
Description sqlite.Expression Description sqlite.Expression
Price sqlite.Expression Price sqlite.Expression
Quantity sqlite.Expression Quantity sqlite.Expression
Added sqlite.Expression
UserID sqlite.Expression UserID sqlite.Expression
} }
@ -89,22 +89,22 @@ func buildItemColumns(alias string) itemColumns {
tableAlias: alias, tableAlias: alias,
ID: sqlite.Quote(alias, "id"), ID: sqlite.Quote(alias, "id"),
Name: sqlite.Quote(alias, "name"), Name: sqlite.Quote(alias, "name"),
Added: sqlite.Quote(alias, "added"),
Description: sqlite.Quote(alias, "description"), Description: sqlite.Quote(alias, "description"),
Price: sqlite.Quote(alias, "price"), Price: sqlite.Quote(alias, "price"),
Quantity: sqlite.Quote(alias, "quantity"), Quantity: sqlite.Quote(alias, "quantity"),
Added: sqlite.Quote(alias, "added"),
UserID: sqlite.Quote(alias, "user_id"), UserID: sqlite.Quote(alias, "user_id"),
} }
} }
type itemWhere[Q sqlite.Filterable] struct { type itemWhere[Q sqlite.Filterable] struct {
ID sqlite.WhereMod[Q, int32] ID sqlite.WhereMod[Q, int64]
Name sqlite.WhereNullMod[Q, string] Name sqlite.WhereMod[Q, string]
Added sqlite.WhereMod[Q, time.Time]
Description sqlite.WhereNullMod[Q, string] Description sqlite.WhereNullMod[Q, string]
Price sqlite.WhereNullMod[Q, float32] Price sqlite.WhereNullMod[Q, float32]
Quantity sqlite.WhereNullMod[Q, int32] Quantity sqlite.WhereNullMod[Q, int64]
Added sqlite.WhereNullMod[Q, time.Time] UserID sqlite.WhereMod[Q, int64]
UserID sqlite.WhereNullMod[Q, int32]
} }
func (itemWhere[Q]) AliasedAs(alias string) itemWhere[Q] { 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] { func buildItemWhere[Q sqlite.Filterable](cols itemColumns) itemWhere[Q] {
return itemWhere[Q]{ return itemWhere[Q]{
ID: sqlite.Where[Q, int32](cols.ID), ID: sqlite.Where[Q, int64](cols.ID),
Name: sqlite.WhereNull[Q, string](cols.Name), Name: sqlite.Where[Q, string](cols.Name),
Added: sqlite.Where[Q, time.Time](cols.Added),
Description: sqlite.WhereNull[Q, string](cols.Description), Description: sqlite.WhereNull[Q, string](cols.Description),
Price: sqlite.WhereNull[Q, float32](cols.Price), Price: sqlite.WhereNull[Q, float32](cols.Price),
Quantity: sqlite.WhereNull[Q, int32](cols.Quantity), Quantity: sqlite.WhereNull[Q, int64](cols.Quantity),
Added: sqlite.WhereNull[Q, time.Time](cols.Added), UserID: sqlite.Where[Q, int64](cols.UserID),
UserID: sqlite.WhereNull[Q, int32](cols.UserID),
} }
} }
var ItemErrors = &itemErrors{ var ItemErrors = &itemErrors{
ErrUniquePkMainItems: &UniqueConstraintError{s: "pk_main_items"}, ErrUniquePkMainItem: &UniqueConstraintError{s: "pk_main_item"},
} }
type itemErrors struct { type itemErrors struct {
ErrUniquePkMainItems *UniqueConstraintError ErrUniquePkMainItem *UniqueConstraintError
} }
// ItemSetter is used for insert/upsert/update operations // ItemSetter is used for insert/upsert/update operations
// All values are optional, and do not have to be set // All values are optional, and do not have to be set
// Generated columns are not included // Generated columns are not included
type ItemSetter struct { type ItemSetter struct {
ID omit.Val[int32] `db:"id,pk" ` ID omit.Val[int64] `db:"id,pk" `
Name omitnull.Val[string] `db:"name" ` Name omit.Val[string] `db:"name" `
Added omit.Val[time.Time] `db:"added" `
Description omitnull.Val[string] `db:"description" ` Description omitnull.Val[string] `db:"description" `
Price omitnull.Val[float32] `db:"price" ` Price omitnull.Val[float32] `db:"price" `
Quantity omitnull.Val[int32] `db:"quantity" ` Quantity omitnull.Val[int64] `db:"quantity" `
Added omitnull.Val[time.Time] `db:"added" ` UserID omit.Val[int64] `db:"user_id" `
UserID omitnull.Val[int32] `db:"user_id" `
} }
func (s ItemSetter) SetColumns() []string { func (s ItemSetter) SetColumns() []string {
@ -154,6 +154,10 @@ func (s ItemSetter) SetColumns() []string {
vals = append(vals, "name") vals = append(vals, "name")
} }
if !s.Added.IsUnset() {
vals = append(vals, "added")
}
if !s.Description.IsUnset() { if !s.Description.IsUnset() {
vals = append(vals, "description") vals = append(vals, "description")
} }
@ -166,10 +170,6 @@ func (s ItemSetter) SetColumns() []string {
vals = append(vals, "quantity") vals = append(vals, "quantity")
} }
if !s.Added.IsUnset() {
vals = append(vals, "added")
}
if !s.UserID.IsUnset() { if !s.UserID.IsUnset() {
vals = append(vals, "user_id") vals = append(vals, "user_id")
} }
@ -182,7 +182,10 @@ func (s ItemSetter) Overwrite(t *Item) {
t.ID, _ = s.ID.Get() t.ID, _ = s.ID.Get()
} }
if !s.Name.IsUnset() { 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() { if !s.Description.IsUnset() {
t.Description, _ = s.Description.GetNull() t.Description, _ = s.Description.GetNull()
@ -193,11 +196,8 @@ func (s ItemSetter) Overwrite(t *Item) {
if !s.Quantity.IsUnset() { if !s.Quantity.IsUnset() {
t.Quantity, _ = s.Quantity.GetNull() t.Quantity, _ = s.Quantity.GetNull()
} }
if !s.Added.IsUnset() {
t.Added, _ = s.Added.GetNull()
}
if !s.UserID.IsUnset() { 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)) vals = append(vals, sqlite.Arg(s.Name))
} }
if !s.Added.IsUnset() {
vals = append(vals, sqlite.Arg(s.Added))
}
if !s.Description.IsUnset() { if !s.Description.IsUnset() {
vals = append(vals, sqlite.Arg(s.Description)) 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)) vals = append(vals, sqlite.Arg(s.Quantity))
} }
if !s.Added.IsUnset() {
vals = append(vals, sqlite.Arg(s.Added))
}
if !s.UserID.IsUnset() { if !s.UserID.IsUnset() {
vals = append(vals, sqlite.Arg(s.UserID)) 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() { if !s.Description.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
sqlite.Quote(append(prefix, "description")...), 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() { if !s.UserID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
sqlite.Quote(append(prefix, "user_id")...), 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 // FindItem retrieves a single record by primary key
// If cols is empty Find will return all columns. // 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 { if len(cols) == 0 {
return Items.Query( return Items.Query(
SelectWhere.Items.ID.EQ(IDPK), 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 // 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( return Items.Query(
SelectWhere.Items.ID.EQ(IDPK), SelectWhere.Items.ID.EQ(IDPK),
).Exists(ctx, exec) ).Exists(ctx, exec)
@ -349,7 +349,7 @@ func (o *Item) PrimaryKeyVals() bob.Expression {
} }
func (o *Item) pkEQ() dialect.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) return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start)
})) }))
} }
@ -410,7 +410,7 @@ func (o ItemSlice) pkIN() dialect.Expression {
return sqlite.Raw("NULL") 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)) pkPairs := make([]bob.Expression, len(o))
for i, row := range o { for i, row := range o {
pkPairs[i] = row.PrimaryKeyVals() 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 { func (o *Item) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery {
return Users.Query(append(mods, return Users.Query(append(mods,
sm.Where(UserColumns.ID.EQ(sqlite.Arg(o.UserID))), 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 _, o := range os {
for _, rel := range users { for _, rel := range users {
if o.UserID.GetOrZero() != rel.ID { if o.UserID != rel.ID {
continue 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) { func attachItemUser0(ctx context.Context, exec bob.Executor, count int, item0 *Item, user1 *User) (*Item, error) {
setter := &ItemSetter{ setter := &ItemSetter{
UserID: omitnull.From(user1.ID), UserID: omit.From(user1.ID),
} }
err := item0.Update(ctx, exec, setter) err := item0.Update(ctx, exec, setter)

View File

@ -21,15 +21,15 @@ import (
"github.com/stephenafamo/bob/dialect/sqlite/um" "github.com/stephenafamo/bob/dialect/sqlite/um"
"github.com/stephenafamo/bob/expr" "github.com/stephenafamo/bob/expr"
"github.com/stephenafamo/bob/mods" "github.com/stephenafamo/bob/mods"
"github.com/stephenafamo/bob/orm"
) )
// User is an object representing the database table. // User is an object representing the database table.
type User struct { type User struct {
ID int32 `db:"id,pk" ` ID int64 `db:"id,pk" `
Username null.Val[string] `db:"username" ` Username string `db:"username" `
Password null.Val[string] `db:"password" ` Password string `db:"password" `
ProfilePictureID null.Val[int32] `db:"profile_picture_id" ` ProfilePictureID null.Val[int64] `db:"profile_picture_id" `
Challenge null.Val[string] `db:"challenge" `
R userR `db:"-" ` R userR `db:"-" `
} }
@ -38,16 +38,17 @@ type User struct {
// This should almost always be used instead of []*User. // This should almost always be used instead of []*User.
type UserSlice []*User type UserSlice []*User
// Users contains methods to work with the users table // Users contains methods to work with the user table
var Users = sqlite.NewTablex[*User, UserSlice, *UserSetter]("", "users") 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] type UsersQuery = *sqlite.ViewQuery[*User, UserSlice]
// userR is where relationships are stored. // userR is where relationships are stored.
type userR struct { type userR struct {
Files FileSlice // fk_files_0 Files FileSlice // fk_file_0
Items ItemSlice // fk_items_0 Items ItemSlice // fk_item_0
ProfilePictureFile *File // fk_user_0
} }
type userColumnNames struct { type userColumnNames struct {
@ -55,10 +56,9 @@ type userColumnNames struct {
Username string Username string
Password string Password string
ProfilePictureID string ProfilePictureID string
Challenge string
} }
var UserColumns = buildUserColumns("users") var UserColumns = buildUserColumns("user")
type userColumns struct { type userColumns struct {
tableAlias string tableAlias string
@ -66,7 +66,6 @@ type userColumns struct {
Username sqlite.Expression Username sqlite.Expression
Password sqlite.Expression Password sqlite.Expression
ProfilePictureID sqlite.Expression ProfilePictureID sqlite.Expression
Challenge sqlite.Expression
} }
func (c userColumns) Alias() string { func (c userColumns) Alias() string {
@ -84,16 +83,14 @@ func buildUserColumns(alias string) userColumns {
Username: sqlite.Quote(alias, "username"), Username: sqlite.Quote(alias, "username"),
Password: sqlite.Quote(alias, "password"), Password: sqlite.Quote(alias, "password"),
ProfilePictureID: sqlite.Quote(alias, "profile_picture_id"), ProfilePictureID: sqlite.Quote(alias, "profile_picture_id"),
Challenge: sqlite.Quote(alias, "challenge"),
} }
} }
type userWhere[Q sqlite.Filterable] struct { type userWhere[Q sqlite.Filterable] struct {
ID sqlite.WhereMod[Q, int32] ID sqlite.WhereMod[Q, int64]
Username sqlite.WhereNullMod[Q, string] Username sqlite.WhereMod[Q, string]
Password sqlite.WhereNullMod[Q, string] Password sqlite.WhereMod[Q, string]
ProfilePictureID sqlite.WhereNullMod[Q, int32] ProfilePictureID sqlite.WhereNullMod[Q, int64]
Challenge sqlite.WhereNullMod[Q, string]
} }
func (userWhere[Q]) AliasedAs(alias string) userWhere[Q] { 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] { func buildUserWhere[Q sqlite.Filterable](cols userColumns) userWhere[Q] {
return userWhere[Q]{ return userWhere[Q]{
ID: sqlite.Where[Q, int32](cols.ID), ID: sqlite.Where[Q, int64](cols.ID),
Username: sqlite.WhereNull[Q, string](cols.Username), Username: sqlite.Where[Q, string](cols.Username),
Password: sqlite.WhereNull[Q, string](cols.Password), Password: sqlite.Where[Q, string](cols.Password),
ProfilePictureID: sqlite.WhereNull[Q, int32](cols.ProfilePictureID), ProfilePictureID: sqlite.WhereNull[Q, int64](cols.ProfilePictureID),
Challenge: sqlite.WhereNull[Q, string](cols.Challenge),
} }
} }
var UserErrors = &userErrors{ var UserErrors = &userErrors{
ErrUniquePkMainUsers: &UniqueConstraintError{s: "pk_main_users"}, ErrUniquePkMainUser: &UniqueConstraintError{s: "pk_main_user"},
} }
type userErrors struct { type userErrors struct {
ErrUniquePkMainUsers *UniqueConstraintError ErrUniquePkMainUser *UniqueConstraintError
} }
// UserSetter is used for insert/upsert/update operations // UserSetter is used for insert/upsert/update operations
// All values are optional, and do not have to be set // All values are optional, and do not have to be set
// Generated columns are not included // Generated columns are not included
type UserSetter struct { type UserSetter struct {
ID omit.Val[int32] `db:"id,pk" ` ID omit.Val[int64] `db:"id,pk" `
Username omitnull.Val[string] `db:"username" ` Username omit.Val[string] `db:"username" `
Password omitnull.Val[string] `db:"password" ` Password omit.Val[string] `db:"password" `
ProfilePictureID omitnull.Val[int32] `db:"profile_picture_id" ` ProfilePictureID omitnull.Val[int64] `db:"profile_picture_id" `
Challenge omitnull.Val[string] `db:"challenge" `
} }
func (s UserSetter) SetColumns() []string { func (s UserSetter) SetColumns() []string {
vals := make([]string, 0, 5) vals := make([]string, 0, 4)
if !s.ID.IsUnset() { if !s.ID.IsUnset() {
vals = append(vals, "id") vals = append(vals, "id")
} }
@ -147,10 +142,6 @@ func (s UserSetter) SetColumns() []string {
vals = append(vals, "profile_picture_id") vals = append(vals, "profile_picture_id")
} }
if !s.Challenge.IsUnset() {
vals = append(vals, "challenge")
}
return vals return vals
} }
@ -159,17 +150,14 @@ func (s UserSetter) Overwrite(t *User) {
t.ID, _ = s.ID.Get() t.ID, _ = s.ID.Get()
} }
if !s.Username.IsUnset() { if !s.Username.IsUnset() {
t.Username, _ = s.Username.GetNull() t.Username, _ = s.Username.Get()
} }
if !s.Password.IsUnset() { if !s.Password.IsUnset() {
t.Password, _ = s.Password.GetNull() t.Password, _ = s.Password.Get()
} }
if !s.ProfilePictureID.IsUnset() { if !s.ProfilePictureID.IsUnset() {
t.ProfilePictureID, _ = s.ProfilePictureID.GetNull() t.ProfilePictureID, _ = s.ProfilePictureID.GetNull()
} }
if !s.Challenge.IsUnset() {
t.Challenge, _ = s.Challenge.GetNull()
}
} }
func (s *UserSetter) Apply(q *dialect.InsertQuery) { 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) { 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() { if !s.ID.IsUnset() {
vals = append(vals, sqlite.Arg(s.ID)) 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)) 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, "", ", ", "") 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 { func (s UserSetter) Expressions(prefix ...string) []bob.Expression {
exprs := make([]bob.Expression, 0, 5) exprs := make([]bob.Expression, 0, 4)
if !s.ID.IsUnset() { if !s.ID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ 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 return exprs
} }
// FindUser retrieves a single record by primary key // FindUser retrieves a single record by primary key
// If cols is empty Find will return all columns. // 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 { if len(cols) == 0 {
return Users.Query( return Users.Query(
SelectWhere.Users.ID.EQ(IDPK), 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 // 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( return Users.Query(
SelectWhere.Users.ID.EQ(IDPK), SelectWhere.Users.ID.EQ(IDPK),
).Exists(ctx, exec) ).Exists(ctx, exec)
@ -298,7 +275,7 @@ func (o *User) PrimaryKeyVals() bob.Expression {
} }
func (o *User) pkEQ() dialect.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) return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start)
})) }))
} }
@ -359,7 +336,7 @@ func (o UserSlice) pkIN() dialect.Expression {
return sqlite.Raw("NULL") 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)) pkPairs := make([]bob.Expression, len(o))
for i, row := range o { for i, row := range o {
pkPairs[i] = row.PrimaryKeyVals() pkPairs[i] = row.PrimaryKeyVals()
@ -479,6 +456,7 @@ type userJoins[Q dialect.Joinable] struct {
typ string typ string
Files func(context.Context) modAs[Q, fileColumns] Files func(context.Context) modAs[Q, fileColumns]
Items func(context.Context) modAs[Q, itemColumns] Items func(context.Context) modAs[Q, itemColumns]
ProfilePictureFile func(context.Context) modAs[Q, fileColumns]
} }
func (j userJoins[Q]) aliasedAs(alias string) userJoins[Q] { func (j userJoins[Q]) aliasedAs(alias string) userJoins[Q] {
@ -490,6 +468,7 @@ func buildUserJoins[Q dialect.Joinable](cols userColumns, typ string) userJoins[
typ: typ, typ: typ,
Files: usersJoinFiles[Q](cols, typ), Files: usersJoinFiles[Q](cols, typ),
Items: usersJoinItems[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 { func (o *User) Files(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery {
return Files.Query(append(mods, return Files.Query(append(mods,
sm.Where(FileColumns.UserID.EQ(sqlite.Arg(o.ID))), 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 { func (o *User) Items(mods ...bob.Mod[*dialect.SelectQuery]) ItemsQuery {
return Items.Query(append(mods, return Items.Query(append(mods,
sm.Where(ItemColumns.UserID.EQ(sqlite.Arg(o.ID))), 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 { func (o *User) Preload(name string, retrieved any) error {
if o == nil { if o == nil {
return nil return nil
@ -601,6 +617,18 @@ func (o *User) Preload(name string, retrieved any) error {
} }
} }
return nil 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: default:
return fmt.Errorf("user has no relationship %q", name) 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 _, o := range os {
for _, rel := range files { for _, rel := range files {
if o.ID != rel.UserID.GetOrZero() { if o.ID != rel.UserID {
continue continue
} }
@ -737,7 +765,7 @@ func (os UserSlice) LoadUserItems(ctx context.Context, exec bob.Executor, mods .
for _, o := range os { for _, o := range os {
for _, rel := range items { for _, rel := range items {
if o.ID != rel.UserID.GetOrZero() { if o.ID != rel.UserID {
continue continue
} }
@ -750,9 +778,94 @@ func (os UserSlice) LoadUserItems(ctx context.Context, exec bob.Executor, mods .
return nil 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) { func insertUserFiles0(ctx context.Context, exec bob.Executor, files1 []*FileSetter, user0 *User) (FileSlice, error) {
for i := range files1 { 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) 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) { func attachUserFiles0(ctx context.Context, exec bob.Executor, count int, files1 FileSlice, user0 *User) (FileSlice, error) {
setter := &FileSetter{ setter := &FileSetter{
UserID: omitnull.From(user0.ID), UserID: omit.From(user0.ID),
} }
err := files1.UpdateAll(ctx, exec, *setter) 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) { func insertUserItems0(ctx context.Context, exec bob.Executor, items1 []*ItemSetter, user0 *User) (ItemSlice, error) {
for i := range items1 { 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) 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) { func attachUserItems0(ctx context.Context, exec bob.Executor, count int, items1 ItemSlice, user0 *User) (ItemSlice, error) {
setter := &ItemSetter{ setter := &ItemSetter{
UserID: omitnull.From(user0.ID), UserID: omit.From(user0.ID),
} }
err := items1.UpdateAll(ctx, exec, *setter) err := items1.UpdateAll(ctx, exec, *setter)
@ -885,3 +998,49 @@ func (user0 *User) AttachItems(ctx context.Context, exec bob.Executor, related .
return nil 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
}

View File

@ -24,11 +24,11 @@ const (
type Item struct { type Item struct {
state protoimpl.MessageState `protogen:"open.v1"` 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"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,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"` 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"` Added *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=added,proto3,oneof" json:"added,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -64,7 +64,7 @@ func (*Item) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{0} 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 { if x != nil && x.Id != nil {
return *x.Id return *x.Id
} }
@ -92,7 +92,7 @@ func (x *Item) GetPrice() float32 {
return 0 return 0
} }
func (x *Item) GetQuantity() uint32 { func (x *Item) GetQuantity() int32 {
if x != nil { if x != nil {
return x.Quantity return x.Quantity
} }
@ -108,7 +108,7 @@ func (x *Item) GetAdded() *timestamppb.Timestamp {
type GetItemRequest struct { type GetItemRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -143,7 +143,7 @@ func (*GetItemRequest) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{1} return file_item_v1_item_proto_rawDescGZIP(), []int{1}
} }
func (x *GetItemRequest) GetId() uint32 { func (x *GetItemRequest) GetId() int64 {
if x != nil { if x != nil {
return x.Id return x.Id
} }
@ -199,8 +199,8 @@ type GetItemsRequest struct {
Start *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start,proto3,oneof" json:"start,omitempty"` 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"` 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"` 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"` Limit *int32 `protobuf:"varint,4,opt,name=limit,proto3,oneof" json:"limit,omitempty"`
Offset *uint32 `protobuf:"varint,5,opt,name=offset,proto3,oneof" json:"offset,omitempty"` Offset *int32 `protobuf:"varint,5,opt,name=offset,proto3,oneof" json:"offset,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -256,14 +256,14 @@ func (x *GetItemsRequest) GetFilter() string {
return "" return ""
} }
func (x *GetItemsRequest) GetLimit() uint32 { func (x *GetItemsRequest) GetLimit() int32 {
if x != nil && x.Limit != nil { if x != nil && x.Limit != nil {
return *x.Limit return *x.Limit
} }
return 0 return 0
} }
func (x *GetItemsRequest) GetOffset() uint32 { func (x *GetItemsRequest) GetOffset() int32 {
if x != nil && x.Offset != nil { if x != nil && x.Offset != nil {
return *x.Offset return *x.Offset
} }
@ -273,7 +273,7 @@ func (x *GetItemsRequest) GetOffset() uint32 {
type GetItemsResponse struct { type GetItemsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Items []*Item `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -315,7 +315,7 @@ func (x *GetItemsResponse) GetItems() []*Item {
return nil return nil
} }
func (x *GetItemsResponse) GetCount() uint64 { func (x *GetItemsResponse) GetCount() int64 {
if x != nil { if x != nil {
return x.Count return x.Count
} }
@ -500,7 +500,7 @@ func (x *UpdateItemResponse) GetItem() *Item {
type DeleteItemRequest struct { type DeleteItemRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` 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 unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -535,7 +535,7 @@ func (*DeleteItemRequest) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{9} return file_item_v1_item_proto_rawDescGZIP(), []int{9}
} }
func (x *DeleteItemRequest) GetId() uint32 { func (x *DeleteItemRequest) GetId() int64 {
if x != nil { if x != nil {
return x.Id 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d,

View File

@ -285,238 +285,6 @@ func (*LogoutResponse) Descriptor() ([]byte, []int) {
return file_user_v1_auth_proto_rawDescGZIP(), []int{5} 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 protoreflect.FileDescriptor
var file_user_v1_auth_proto_rawDesc = string([]byte{ 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, 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, 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, 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, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc1, 0x01, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65,
0x6b, 0x65, 0x79, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x15,
0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65,
0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x38, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e,
0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x3b, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x49, 0x64, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e,
0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06,
0x1b, 0x0a, 0x19, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52,
0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1c, 0x0a, 0x1a, 0x46, 0x69, 0x6e, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f,
0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68, 0x50,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd2, 0x03, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65,
0x12, 0x15, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f,
0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02,
0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02,
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, 0x5c,
0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02,
0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x33,
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,
}) })
var ( var (
@ -608,7 +345,7 @@ func file_user_v1_auth_proto_rawDescGZIP() []byte {
return file_user_v1_auth_proto_rawDescData 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{ var file_user_v1_auth_proto_goTypes = []any{
(*LoginRequest)(nil), // 0: user.v1.LoginRequest (*LoginRequest)(nil), // 0: user.v1.LoginRequest
(*LoginResponse)(nil), // 1: user.v1.LoginResponse (*LoginResponse)(nil), // 1: user.v1.LoginResponse
@ -616,28 +353,16 @@ var file_user_v1_auth_proto_goTypes = []any{
(*SignUpResponse)(nil), // 3: user.v1.SignUpResponse (*SignUpResponse)(nil), // 3: user.v1.SignUpResponse
(*LogoutRequest)(nil), // 4: user.v1.LogoutRequest (*LogoutRequest)(nil), // 4: user.v1.LogoutRequest
(*LogoutResponse)(nil), // 5: user.v1.LogoutResponse (*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
} }
var file_user_v1_auth_proto_depIdxs = []int32{ var file_user_v1_auth_proto_depIdxs = []int32{
0, // 0: user.v1.AuthService.Login:input_type -> user.v1.LoginRequest 0, // 0: user.v1.AuthService.Login:input_type -> user.v1.LoginRequest
2, // 1: user.v1.AuthService.SignUp:input_type -> user.v1.SignUpRequest 2, // 1: user.v1.AuthService.SignUp:input_type -> user.v1.SignUpRequest
4, // 2: user.v1.AuthService.Logout:input_type -> user.v1.LogoutRequest 4, // 2: user.v1.AuthService.Logout:input_type -> user.v1.LogoutRequest
6, // 3: user.v1.AuthService.GetPasskeyIDs:input_type -> user.v1.GetPasskeyIDsRequest 1, // 3: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse
8, // 4: user.v1.AuthService.BeginPasskeyLogin:input_type -> user.v1.BeginPasskeyLoginRequest 3, // 4: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse
10, // 5: user.v1.AuthService.FinishPasskeyLogin:input_type -> user.v1.FinishPasskeyLoginRequest 5, // 5: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse
1, // 6: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse 3, // [3:6] is the sub-list for method output_type
3, // 7: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse 0, // [0:3] is the sub-list for method input_type
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 type_name
0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name 0, // [0:0] is the sub-list for field type_name
@ -654,7 +379,7 @@ func file_user_v1_auth_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_auth_proto_rawDesc), len(file_user_v1_auth_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_auth_proto_rawDesc), len(file_user_v1_auth_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 12, NumMessages: 6,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -23,9 +23,9 @@ const (
type User struct { type User struct {
state protoimpl.MessageState `protogen:"open.v1"` 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"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,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"` ProfilePictureId *int64 `protobuf:"varint,3,opt,name=profile_picture_id,json=profilePictureId,proto3,oneof" json:"profile_picture_id,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -60,7 +60,7 @@ func (*User) Descriptor() ([]byte, []int) {
return file_user_v1_user_proto_rawDescGZIP(), []int{0} return file_user_v1_user_proto_rawDescGZIP(), []int{0}
} }
func (x *User) GetId() uint32 { func (x *User) GetId() int64 {
if x != nil { if x != nil {
return x.Id return x.Id
} }
@ -74,11 +74,11 @@ func (x *User) GetUsername() string {
return "" return ""
} }
func (x *User) GetProfilePicture() string { func (x *User) GetProfilePictureId() int64 {
if x != nil && x.ProfilePicture != nil { if x != nil && x.ProfilePictureId != nil {
return *x.ProfilePicture return *x.ProfilePictureId
} }
return "" return 0
} }
type GetUserRequest struct { type GetUserRequest struct {
@ -457,252 +457,85 @@ func (x *UpdateProfilePictureResponse) GetUser() *User {
return nil 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 protoreflect.FileDescriptor
var file_user_v1_user_proto_rawDesc = string([]byte{ 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, 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, 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, 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, 0x65, 0x12, 0x31, 0x0a, 0x12, 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, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x49,
0x12, 0x0a, 0x10, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x69, 0x63, 0x74, 0x64, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x75, 0x72, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x5f, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x47,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x34, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x34, 0x0a,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
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,
0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 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, 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, 0x73, 0x65, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a,
0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 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, 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, 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, 0x25, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3b,
0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72,
0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4e, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x10, 0x47,
0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63,
0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x41, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x25, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49,
0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x21, 0x0a, 0x1f, 0x42, 0x65, 0x67, 0x69, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4e, 0x0a,
0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x22, 0x0a, 0x20, 0x42, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09,
0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74,
0x22, 0x0a, 0x20, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x41, 0x0a,
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69,
0x65, 0x73, 0x74, 0x22, 0x23, 0x0a, 0x21, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a,
0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb8, 0x04, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72,
0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x32, 0xcf, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73,
0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x75, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71,
0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64,
0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64,
0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x09, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x75, 0x73, 0x65, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b,
0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e,
0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65,
0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x55,
0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x24, 0x2e, 0x75, 0x73, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74,
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x75, 0x72, 0x65, 0x12, 0x24, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70,
0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75,
0x74, 0x1a, 0x25, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x75, 0x73, 0x65, 0x72,
0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x18, 0x42, 0x65, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a,
0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74,
0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f,
0x1a, 0x29, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07,
0x19, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56,
0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x75, 0x73, 0x65, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a,
0x65, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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,
}) })
var ( var (
@ -717,7 +550,7 @@ func file_user_v1_user_proto_rawDescGZIP() []byte {
return file_user_v1_user_proto_rawDescData 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{ var file_user_v1_user_proto_goTypes = []any{
(*User)(nil), // 0: user.v1.User (*User)(nil), // 0: user.v1.User
(*GetUserRequest)(nil), // 1: user.v1.GetUserRequest (*GetUserRequest)(nil), // 1: user.v1.GetUserRequest
@ -728,10 +561,6 @@ var file_user_v1_user_proto_goTypes = []any{
(*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse (*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse
(*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest (*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest
(*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse (*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
} }
var file_user_v1_user_proto_depIdxs = []int32{ var file_user_v1_user_proto_depIdxs = []int32{
0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User 0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User
@ -741,16 +570,12 @@ var file_user_v1_user_proto_depIdxs = []int32{
3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest 3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest
5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest 5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest
7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest 7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest
9, // 7: user.v1.UserService.BeginPasskeyRegistration:input_type -> user.v1.BeginPasskeyRegistrationRequest 2, // 7: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse
11, // 8: user.v1.UserService.FinishPasskeyRegistration:input_type -> user.v1.FinishPasskeyRegistrationRequest 4, // 8: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse
2, // 9: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse 6, // 9: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse
4, // 10: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse 8, // 10: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse
6, // 11: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse 7, // [7:11] is the sub-list for method output_type
8, // 12: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse 3, // [3:7] is the sub-list for method input_type
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 type_name
3, // [3:3] is the sub-list for extension extendee 3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name 0, // [0:3] is the sub-list for field type_name
@ -768,7 +593,7 @@ func file_user_v1_user_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_user_proto_rawDesc), len(file_user_v1_user_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_user_proto_rawDesc), len(file_user_v1_user_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 13, NumMessages: 9,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -39,15 +39,6 @@ const (
AuthServiceSignUpProcedure = "/user.v1.AuthService/SignUp" AuthServiceSignUpProcedure = "/user.v1.AuthService/SignUp"
// AuthServiceLogoutProcedure is the fully-qualified name of the AuthService's Logout RPC. // AuthServiceLogoutProcedure is the fully-qualified name of the AuthService's Logout RPC.
AuthServiceLogoutProcedure = "/user.v1.AuthService/Logout" 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. // 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) Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error)
SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], 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) 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 // NewAuthServiceClient constructs a client for the user.v1.AuthService service. By default, it uses
@ -89,24 +77,6 @@ func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
connect.WithSchema(authServiceMethods.ByName("Logout")), connect.WithSchema(authServiceMethods.ByName("Logout")),
connect.WithClientOptions(opts...), 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...),
),
} }
} }
@ -115,9 +85,6 @@ type authServiceClient struct {
login *connect.Client[v1.LoginRequest, v1.LoginResponse] login *connect.Client[v1.LoginRequest, v1.LoginResponse]
signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse] signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse]
logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse] 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 calls user.v1.AuthService.Login. // 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) 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. // AuthServiceHandler is an implementation of the user.v1.AuthService service.
type AuthServiceHandler interface { type AuthServiceHandler interface {
Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error) Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error)
SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], 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) 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 // 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.WithSchema(authServiceMethods.ByName("Logout")),
connect.WithHandlerOptions(opts...), 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) { return "/user.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path { switch r.URL.Path {
case AuthServiceLoginProcedure: case AuthServiceLoginProcedure:
@ -211,12 +142,6 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption
authServiceSignUpHandler.ServeHTTP(w, r) authServiceSignUpHandler.ServeHTTP(w, r)
case AuthServiceLogoutProcedure: case AuthServiceLogoutProcedure:
authServiceLogoutHandler.ServeHTTP(w, r) authServiceLogoutHandler.ServeHTTP(w, r)
case AuthServiceGetPasskeyIDsProcedure:
authServiceGetPasskeyIDsHandler.ServeHTTP(w, r)
case AuthServiceBeginPasskeyLoginProcedure:
authServiceBeginPasskeyLoginHandler.ServeHTTP(w, r)
case AuthServiceFinishPasskeyLoginProcedure:
authServiceFinishPasskeyLoginHandler.ServeHTTP(w, r)
default: default:
http.NotFound(w, r) 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) { 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")) 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"))
}

View File

@ -43,12 +43,6 @@ const (
// UserServiceUpdateProfilePictureProcedure is the fully-qualified name of the UserService's // UserServiceUpdateProfilePictureProcedure is the fully-qualified name of the UserService's
// UpdateProfilePicture RPC. // UpdateProfilePicture RPC.
UserServiceUpdateProfilePictureProcedure = "/user.v1.UserService/UpdateProfilePicture" 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. // 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) UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error)
GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], 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) 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 // NewUserServiceClient constructs a client for the user.v1.UserService service. By default, it uses
@ -96,18 +88,6 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")), connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")),
connect.WithClientOptions(opts...), 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...),
),
} }
} }
@ -117,8 +97,6 @@ type userServiceClient struct {
updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse] updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse]
getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse] getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse]
updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse] updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse]
beginPasskeyRegistration *connect.Client[v1.BeginPasskeyRegistrationRequest, v1.BeginPasskeyRegistrationResponse]
finishPasskeyRegistration *connect.Client[v1.FinishPasskeyRegistrationRequest, v1.FinishPasskeyRegistrationResponse]
} }
// GetUser calls user.v1.UserService.GetUser. // 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) 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. // UserServiceHandler is an implementation of the user.v1.UserService service.
type UserServiceHandler interface { type UserServiceHandler interface {
GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.GetUserResponse], error) GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.GetUserResponse], error)
UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], 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) GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error)
UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], 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 // 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.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")),
connect.WithHandlerOptions(opts...), 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) { return "/user.v1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path { switch r.URL.Path {
case UserServiceGetUserProcedure: case UserServiceGetUserProcedure:
@ -214,10 +168,6 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
userServiceGetAPIKeyHandler.ServeHTTP(w, r) userServiceGetAPIKeyHandler.ServeHTTP(w, r)
case UserServiceUpdateProfilePictureProcedure: case UserServiceUpdateProfilePictureProcedure:
userServiceUpdateProfilePictureHandler.ServeHTTP(w, r) userServiceUpdateProfilePictureHandler.ServeHTTP(w, r)
case UserServiceBeginPasskeyRegistrationProcedure:
userServiceBeginPasskeyRegistrationHandler.ServeHTTP(w, r)
case UserServiceFinishPasskeyRegistrationProcedure:
userServiceFinishPasskeyRegistrationHandler.ServeHTTP(w, r)
default: default:
http.NotFound(w, r) 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) { 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")) 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"))
}

View File

@ -3,65 +3,41 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log" "log"
"log/slog"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"time" "time"
connectcors "connectrpc.com/cors"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/rs/cors" "github.com/stephenafamo/bob"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"golang.org/x/net/http2/h2c" "golang.org/x/net/http2/h2c"
"gorm.io/gorm"
"github.com/spotdemo4/trevstack/server/internal/database" "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/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/item/v1"
"github.com/spotdemo4/trevstack/server/internal/handlers/user/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() { func main() {
err := godotenv.Load() logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
slog.SetDefault(logger)
// Get env
env, err := getEnv()
if err != nil { if err != nil {
log.Println("Failed to load .env file, using environment variables") log.Fatal(err.Error())
} }
// Get environment variables for server // Get database
env := env{ db := &bob.DB{}
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{}
switch env.DBType { switch env.DBType {
case "postgres": case "postgres":
log.Println("Using Postgres") log.Println("Using Postgres")
@ -103,21 +79,16 @@ func main() {
log.Fatal("DB_TYPE must be either postgres or sqlite") 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 // Serve GRPC Handlers
api := http.NewServeMux() api := http.NewServeMux()
api.Handle(withCORS(user.NewAuthHandler(db, env.Key))) api.Handle(interceptors.WithCORS(user.NewAuthHandler(db, env.Key)))
api.Handle(withCORS(user.NewHandler(db, env.Key))) api.Handle(interceptors.WithCORS(user.NewHandler(db, env.Key)))
api.Handle(withCORS(item.NewHandler(db, env.Key))) api.Handle(interceptors.WithCORS(item.NewHandler(db, env.Key)))
// Serve web interface // Serve web interface
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/", client.NewClientHandler(env.Key)) 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)) mux.Handle("/grpc/", http.StripPrefix("/grpc", api))
// Start server // Start server
@ -132,8 +103,7 @@ func main() {
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() { go func() {
sig := <-sigs sig := <-sigs
log.Printf("Received signal %s", sig) slog.Warn(fmt.Sprintf("Received signal %s, exiting", sig))
log.Println("Exiting")
// Close HTTP server // Close HTTP server
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@ -143,10 +113,7 @@ func main() {
cancel() cancel()
// Close database connection // Close database connection
sqlDB, err := db.DB() // Get underlying SQL database db.Close()
if err == nil {
sqlDB.Close()
}
}() }()
if err := server.ListenAndServe(); err != nil { if err := server.ListenAndServe(); err != nil {
@ -154,13 +121,42 @@ func main() {
} }
} }
// withCORS adds CORS support to a Connect HTTP handler. type env struct {
func withCORS(pattern string, h http.Handler) (string, http.Handler) { DBType string
middleware := cors.New(cors.Options{ DBUser string
AllowedOrigins: []string{"*"}, DBPass string
AllowedMethods: connectcors.AllowedMethods(), DBHost string
AllowedHeaders: connectcors.AllowedHeaders(), DBPort string
ExposedHeaders: connectcors.ExposedHeaders(), DBName string
}) Port string
return pattern, middleware.Handler(h) 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
} }