From 0e9746bbeefe03ad47fefc6da41965fdafbbc816 Mon Sep 17 00:00:00 2001 From: gearnode Date: Sun, 2 Feb 2025 11:37:35 -0800 Subject: [PATCH] Bootstrap user manager Signed-off-by: gearnode --- pkg/api/api.go | 2 + pkg/probod/probod.go | 7 ++ pkg/usrmgr/coredata/migrations.go | 24 ++++++ .../coredata/migrations/20250202T110500Z.sql | 12 +++ pkg/usrmgr/coredata/session.go | 81 +++++++++++++++++++ pkg/usrmgr/coredata/user.go | 80 ++++++++++++++++++ pkg/usrmgr/usrmgr.go | 56 +++++++++++++ 7 files changed, 262 insertions(+) create mode 100644 pkg/usrmgr/coredata/migrations.go create mode 100644 pkg/usrmgr/coredata/migrations/20250202T110500Z.sql create mode 100644 pkg/usrmgr/coredata/session.go create mode 100644 pkg/usrmgr/coredata/user.go create mode 100644 pkg/usrmgr/usrmgr.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 4e2d06d27..548689366 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -20,6 +20,7 @@ import ( console_v1 "github.com/getprobo/probo/pkg/api/console/v1" "github.com/getprobo/probo/pkg/probo" + "github.com/getprobo/probo/pkg/usrmgr" "github.com/go-chi/chi/v5" "github.com/go-chi/cors" "go.gearno.de/kit/httpserver" @@ -29,6 +30,7 @@ type ( Config struct { AllowedOrigins []string Probo *probo.Service + Usrmgr *usrmgr.Service } Server struct { diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go index bda7fe491..51c451acc 100644 --- a/pkg/probod/probod.go +++ b/pkg/probod/probod.go @@ -25,6 +25,7 @@ import ( "github.com/getprobo/probo/pkg/api" "github.com/getprobo/probo/pkg/probo" + "github.com/getprobo/probo/pkg/usrmgr" "github.com/prometheus/client_golang/prometheus" "go.gearno.de/kit/httpserver" "go.gearno.de/kit/log" @@ -94,6 +95,11 @@ func (impl *Implm) Run( return fmt.Errorf("cannot create pg client: %w", err) } + usrmgr, err := usrmgr.NewService(ctx, pgClient) + if err != nil { + return fmt.Errorf("cannot create usrmgr service: %w", err) + } + probo, err := probo.NewService(ctx, pgClient) if err != nil { return fmt.Errorf("cannot create probo service: %w", err) @@ -102,6 +108,7 @@ func (impl *Implm) Run( apiServer, err := api.NewServer( api.Config{ Probo: probo, + Usrmgr: usrmgr, AllowedOrigins: impl.cfg.Api.Cors.AllowedOrigins, }, ) diff --git a/pkg/usrmgr/coredata/migrations.go b/pkg/usrmgr/coredata/migrations.go new file mode 100644 index 000000000..1f81ee44f --- /dev/null +++ b/pkg/usrmgr/coredata/migrations.go @@ -0,0 +1,24 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "embed" +) + +var ( + //go:embed migrations/*.sql + Migrations embed.FS +) diff --git a/pkg/usrmgr/coredata/migrations/20250202T110500Z.sql b/pkg/usrmgr/coredata/migrations/20250202T110500Z.sql new file mode 100644 index 000000000..b5dff7f25 --- /dev/null +++ b/pkg/usrmgr/coredata/migrations/20250202T110500Z.sql @@ -0,0 +1,12 @@ +CREATE TABLE usrmgr_users ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT now(), + updated_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE TABLE usrmgr_sessions ( + id TEXT PRIMARY KEY, + user_id TEXT NOT NULL REFERENCES usrmgr_users(id), + created_at TIMESTAMP NOT NULL DEFAULT now(), + updated_at TIMESTAMP NOT NULL DEFAULT now() +); diff --git a/pkg/usrmgr/coredata/session.go b/pkg/usrmgr/coredata/session.go new file mode 100644 index 000000000..321de7942 --- /dev/null +++ b/pkg/usrmgr/coredata/session.go @@ -0,0 +1,81 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "fmt" + "time" + + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "github.com/jackc/pgx/v5" + "go.gearno.de/crypto/uuid" + "go.gearno.de/kit/pg" +) + +type ( + Session struct { + ID gid.GID + UserID gid.GID + CreatedAt time.Time + UpdatedAt time.Time + } +) + +func (s Session) CursorKey() page.CursorKey { + return page.NewCursorKey(uuid.UUID(s.ID), s.CreatedAt) +} + +func (s *Session) scan(r pgx.Row) error { + return r.Scan( + &s.ID, + &s.CreatedAt, + &s.UpdatedAt, + ) +} + +func (s *Session) LoadByID( + ctx context.Context, + conn pg.Conn, + sessionID gid.GID, +) error { + q := ` +SELECT + id, + created_at, + updated_at +FROM + sessions +WHERE + id = @session_id +LIMIT 1; +` + + q = fmt.Sprintf(q) + + args := pgx.NamedArgs{"session_id": sessionID} + + r := conn.QueryRow(ctx, q, args) + + s2 := Session{} + if err := s2.scan(r); err != nil { + return err + } + + *s = s2 + + return nil +} diff --git a/pkg/usrmgr/coredata/user.go b/pkg/usrmgr/coredata/user.go new file mode 100644 index 000000000..ce07550f9 --- /dev/null +++ b/pkg/usrmgr/coredata/user.go @@ -0,0 +1,80 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "fmt" + "time" + + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "github.com/jackc/pgx/v5" + "go.gearno.de/crypto/uuid" + "go.gearno.de/kit/pg" +) + +type ( + User struct { + ID gid.GID + CreatedAt time.Time + UpdatedAt time.Time + } +) + +func (u User) CursorKey() page.CursorKey { + return page.NewCursorKey(uuid.UUID(u.ID), u.CreatedAt) +} + +func (u *User) scan(r pgx.Row) error { + return r.Scan( + &u.ID, + &u.CreatedAt, + &u.UpdatedAt, + ) +} + +func (u *User) LoadByID( + ctx context.Context, + conn pg.Conn, + userID gid.GID, +) error { + q := ` +SELECT + id, + created_at, + updated_at +FROM + users +WHERE + id = @user_id +LIMIT 1; +` + + q = fmt.Sprintf(q) + + args := pgx.NamedArgs{"user_id": userID} + + r := conn.QueryRow(ctx, q, args) + + u2 := User{} + if err := u2.scan(r); err != nil { + return err + } + + *u = u2 + + return nil +} diff --git a/pkg/usrmgr/usrmgr.go b/pkg/usrmgr/usrmgr.go new file mode 100644 index 000000000..5ad6939de --- /dev/null +++ b/pkg/usrmgr/usrmgr.go @@ -0,0 +1,56 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package usrmgr + +import ( + "context" + "fmt" + + "github.com/getprobo/probo/pkg/usrmgr/coredata" + "go.gearno.de/kit/migrator" + "go.gearno.de/kit/pg" +) + +type ( + Service struct { + pg *pg.Client + } +) + +func NewService( + ctx context.Context, + pgClient *pg.Client, +) (*Service, error) { + err := migrator.NewMigrator(pgClient, coredata.Migrations).Run(ctx, "migrations") + if err != nil { + return nil, fmt.Errorf("cannot migrate database schema: %w", err) + } + + return &Service{ + pg: pgClient, + }, nil +} + +func (s Service) Login(email, password string) (*coredata.Session, error) { + return nil, nil +} + +func (s Service) Logout(sessionID string) error { + return nil +} + +func (s Service) GetSession(sessionID string) (*coredata.Session, error) { + return nil, nil +}