add working signup + login

This commit is contained in:
sam 2023-09-04 03:33:13 +02:00
parent bc85b7c340
commit d8cb8c8fa8
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
27 changed files with 600 additions and 39 deletions

View file

@ -37,8 +37,13 @@ func (s *AccountStore) ByID(ctx context.Context, id ulid.ULID) (a database.Accou
}
// ByUsername gets an account by its username.
func (s *AccountStore) ByUsername(ctx context.Context, username string, host *string) (a database.Account, err error) {
q := sqlf.Sprintf("SELECT * FROM accounts WHERE username = %s AND host = %v", username, host)
func (s *AccountStore) ByUsername(ctx context.Context, username, domain string) (a database.Account, err error) {
q := sqlf.Sprintf("SELECT * FROM accounts WHERE username = %s", username)
if domain == "" {
q = sqlf.Sprintf("%v AND domain IS NULL", q)
} else {
q = sqlf.Sprintf("%v AND domain = %s", q, domain)
}
a, err = Get[database.Account](ctx, s.q, q)
if err != nil {
@ -64,7 +69,7 @@ func (s *AccountStore) CreateLocal(
}
q := sqlf.Sprintf(
"INSERT INTO accounts (id, username, host, email, password) VALUES (%s, %v, NULL, %v, %v) RETURNING *",
"INSERT INTO accounts (id, username, domain, email, password) VALUES (%s, %v, NULL, %v, %v) RETURNING *",
makeULID(), username, email, hash)
a, err = Get[database.Account](ctx, s.q, q)

View file

@ -0,0 +1,51 @@
package sql
import (
"context"
"emperror.dev/errors"
"git.sleepycat.moe/sam/mercury/internal/database"
"github.com/keegancsmith/sqlf"
)
// ConfigStore is the interface to configs in the database.
type ConfigStore struct {
q Querier
}
// NewConfigStore creates a new ConfigStore instance.
func NewConfigStore(q Querier) *ConfigStore {
return &ConfigStore{q: q}
}
func (s *ConfigStore) initConfig(ctx context.Context) error {
q := sqlf.Sprintf(`INSERT INTO config
(id, name)
VALUES (1, %s)
ON CONFLICT (id) DO NOTHING`, database.DefaultConfig.Name)
err := Exec(ctx, s.q, q)
return errors.Wrap(err, "executing query")
}
func (s *ConfigStore) Get(ctx context.Context) (database.Config, error) {
q := sqlf.Sprintf("SELECT * FROM config WHERE id = 1")
return Get[database.Config](ctx, s.q, q)
}
func (s *ConfigStore) Set(ctx context.Context, cur, new database.Config) (database.Config, error) {
q := sqlf.Sprintf("UPDATE config SET")
if cur.Name != new.Name {
q = sqlf.Sprintf("%v name = %v,", q, new.Name)
}
if cur.AdminID != new.AdminID {
q = sqlf.Sprintf("%v admin_id = %v,", q, new.AdminID)
}
if cur.InternalApplication != new.InternalApplication {
q = sqlf.Sprintf("%v internal_application = %v,", q, new.InternalApplication)
}
q = sqlf.Sprintf("%v id = %v WHERE id = %v RETURNING *", q, cur.ID, cur.ID)
return Get[database.Config](ctx, s.q, q)
}

View file

@ -5,6 +5,7 @@ import (
"context"
"emperror.dev/errors"
"git.sleepycat.moe/sam/mercury/internal/database"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/oklog/ulid/v2"
)
@ -24,9 +25,55 @@ func NewBase(ctx context.Context, connString string) (*Base, error) {
base := &Base{
pool: pool,
}
// create configuration
if err := base.initSingletons(ctx); err != nil {
return nil, errors.Wrap(err, "initializing configuration")
}
return base, nil
}
func (base *Base) initSingletons(ctx context.Context) error {
err := NewConfigStore(base.pool).initConfig(ctx)
if err != nil {
return errors.Wrap(err, "initializing configuration")
}
cfg, err := NewConfigStore(base.pool).Get(ctx)
if err != nil {
return errors.Wrap(err, "getting configuration")
}
if cfg.InternalApplication == nil {
tx, err := base.BeginTx(ctx)
if err != nil {
return errors.Wrap(err, "creating transaction")
}
defer tx.Rollback(ctx)
app, err := NewTokenStore(tx).CreateApplication(ctx, database.InternalApplicationName)
if err != nil {
return errors.Wrap(err, "creating internal application")
}
newCfg := cfg
newCfg.InternalApplication = &app.ID
cfg, err = NewConfigStore(tx).Set(ctx, cfg, newCfg)
if err != nil {
return errors.Wrap(err, "updating configuration")
}
err = tx.Commit(ctx)
if err != nil {
return errors.Wrap(err, "committing transaction")
}
}
return nil
}
// Acquire acquires a connection from the database pool.
// It is the caller's responsibility to call the Release method.
func (base *Base) Acquire(ctx context.Context) (ReleaseableQuerier, error) {

View file

@ -48,3 +48,8 @@ func Get[T any](ctx context.Context, querier Querier, query *sqlf.Query) (T, err
}
return dst, nil
}
func Exec(ctx context.Context, querier Querier, query *sqlf.Query) error {
_, err := querier.Exec(ctx, query.Query(sqlf.PostgresBindVar), query.Args()...)
return err
}

View file

@ -0,0 +1,52 @@
package sql
import (
"context"
"time"
"emperror.dev/errors"
"git.sleepycat.moe/sam/mercury/internal/database"
"github.com/keegancsmith/sqlf"
"github.com/oklog/ulid/v2"
)
// TokenStore is the interface to tokens in the database.
type TokenStore struct {
q Querier
}
// NewTokenStore creates a new TokenStore instance.
func NewTokenStore(q Querier) *TokenStore {
return &TokenStore{q: q}
}
func (s *TokenStore) Get(ctx context.Context, id ulid.ULID) (database.Token, error) {
q := sqlf.Sprintf("SELECT * FROM tokens WHERE id = %s", id)
t, err := Get[database.Token](ctx, s.q, q)
return t, errors.Wrap(err, "executing query")
}
func (s *TokenStore) GetApplication(ctx context.Context, id ulid.ULID) (database.Application, error) {
q := sqlf.Sprintf("SELECT * FROM applications WHERE id = %s", id)
app, err := Get[database.Application](ctx, s.q, q)
return app, errors.Wrap(err, "executing query")
}
func (s *TokenStore) Create(ctx context.Context, userID, appID ulid.ULID, scopes []string, expires time.Time) (database.Token, error) {
q := sqlf.Sprintf(`INSERT INTO tokens
(id, user_id, app_id, scopes, expires)
values (%s, %s, %s, %v, %v)
RETURNING *`, makeULID(), userID, appID, scopes, expires)
t, err := Get[database.Token](ctx, s.q, q)
return t, errors.Wrap(err, "executing query")
}
func (s *TokenStore) CreateApplication(ctx context.Context, name string) (database.Application, error) {
q := sqlf.Sprintf("INSERT INTO applications (id, name) VALUES (%s, %s) RETURNING *", makeULID(), name)
app, err := Get[database.Application](ctx, s.q, q)
return app, errors.Wrap(err, "executing query")
}