diff --git a/database/db.go b/database/db.go index 0e38862..61f5bf4 100644 --- a/database/db.go +++ b/database/db.go @@ -1,41 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 + package database import ( "context" "database/sql" - _ "embed" ) -//go:embed init.sql -var initSql string +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} -type DB struct{ db *sql.DB } +func New(db DBTX) *Queries { + return &Queries{db: db} +} -func Open(p string) (*DB, error) { - db, err := sql.Open("sqlite3", p) - if err != nil { - return nil, err +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, } - _, err = db.Exec(initSql) - return &DB{db: db}, err -} - -func (d *DB) Begin() (*Tx, error) { - begin, err := d.db.Begin() - if err != nil { - return nil, err - } - return &Tx{begin}, err -} - -func (d *DB) BeginCtx(ctx context.Context) (*Tx, error) { - begin, err := d.db.BeginTx(ctx, nil) - if err != nil { - return nil, err - } - return &Tx{begin}, err -} - -func (d *DB) Close() error { - return d.db.Close() } diff --git a/database/manage-oauth.sql.go b/database/manage-oauth.sql.go new file mode 100644 index 0000000..4f60ecf --- /dev/null +++ b/database/manage-oauth.sql.go @@ -0,0 +1,178 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: manage-oauth.sql + +package database + +import ( + "context" + "database/sql" +) + +const getAppList = `-- name: GetAppList :many +SELECT subject, name, domain, owner, public, sso, active +FROM client_store +WHERE owner = ? + OR ? = 1 +LIMIT 25 OFFSET ? +` + +type GetAppListParams struct { + Owner string `json:"owner"` + Column2 interface{} `json:"column_2"` + Offset int64 `json:"offset"` +} + +type GetAppListRow struct { + Subject string `json:"subject"` + Name string `json:"name"` + Domain string `json:"domain"` + Owner string `json:"owner"` + Public sql.NullInt64 `json:"public"` + Sso sql.NullInt64 `json:"sso"` + Active sql.NullInt64 `json:"active"` +} + +func (q *Queries) GetAppList(ctx context.Context, arg GetAppListParams) ([]GetAppListRow, error) { + rows, err := q.db.QueryContext(ctx, getAppList, arg.Owner, arg.Column2, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetAppListRow + for rows.Next() { + var i GetAppListRow + if err := rows.Scan( + &i.Subject, + &i.Name, + &i.Domain, + &i.Owner, + &i.Public, + &i.Sso, + &i.Active, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getClientInfo = `-- name: GetClientInfo :one +SELECT secret, name, domain, public, sso, active +FROM client_store +WHERE subject = ? +LIMIT 1 +` + +type GetClientInfoRow struct { + Secret string `json:"secret"` + Name string `json:"name"` + Domain string `json:"domain"` + Public sql.NullInt64 `json:"public"` + Sso sql.NullInt64 `json:"sso"` + Active sql.NullInt64 `json:"active"` +} + +func (q *Queries) GetClientInfo(ctx context.Context, subject string) (GetClientInfoRow, error) { + row := q.db.QueryRowContext(ctx, getClientInfo, subject) + var i GetClientInfoRow + err := row.Scan( + &i.Secret, + &i.Name, + &i.Domain, + &i.Public, + &i.Sso, + &i.Active, + ) + return i, err +} + +const insertClientApp = `-- name: InsertClientApp :exec +INSERT INTO client_store (subject, name, secret, domain, owner, public, sso, active) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +` + +type InsertClientAppParams struct { + Subject string `json:"subject"` + Name string `json:"name"` + Secret string `json:"secret"` + Domain string `json:"domain"` + Owner string `json:"owner"` + Public sql.NullInt64 `json:"public"` + Sso sql.NullInt64 `json:"sso"` + Active sql.NullInt64 `json:"active"` +} + +func (q *Queries) InsertClientApp(ctx context.Context, arg InsertClientAppParams) error { + _, err := q.db.ExecContext(ctx, insertClientApp, + arg.Subject, + arg.Name, + arg.Secret, + arg.Domain, + arg.Owner, + arg.Public, + arg.Sso, + arg.Active, + ) + return err +} + +const updateClientApp = `-- name: UpdateClientApp :exec +UPDATE client_store +SET name = ?, + domain = ?, + public = ?, + sso = ?, + active = ? +WHERE subject = ? + AND owner = ? +` + +type UpdateClientAppParams struct { + Name string `json:"name"` + Domain string `json:"domain"` + Public sql.NullInt64 `json:"public"` + Sso sql.NullInt64 `json:"sso"` + Active sql.NullInt64 `json:"active"` + Subject string `json:"subject"` + Owner string `json:"owner"` +} + +func (q *Queries) UpdateClientApp(ctx context.Context, arg UpdateClientAppParams) error { + _, err := q.db.ExecContext(ctx, updateClientApp, + arg.Name, + arg.Domain, + arg.Public, + arg.Sso, + arg.Active, + arg.Subject, + arg.Owner, + ) + return err +} + +const resetClientAppSecret = `-- name: resetClientAppSecret :exec +UPDATE client_store +SET secret = ? +WHERE subject = ? + AND owner = ? +` + +type resetClientAppSecretParams struct { + Secret string `json:"secret"` + Subject string `json:"subject"` + Owner string `json:"owner"` +} + +func (q *Queries) resetClientAppSecret(ctx context.Context, arg resetClientAppSecretParams) error { + _, err := q.db.ExecContext(ctx, resetClientAppSecret, arg.Secret, arg.Subject, arg.Owner) + return err +} diff --git a/database/manage-users.sql.go b/database/manage-users.sql.go new file mode 100644 index 0000000..57e2241 --- /dev/null +++ b/database/manage-users.sql.go @@ -0,0 +1,110 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: manage-users.sql + +package database + +import ( + "context" + "database/sql" +) + +const getUserList = `-- name: GetUserList :many +SELECT subject, + name, + username, + picture, + email, + email_verified, + role, + updated_at, + active +FROM users +LIMIT 25 OFFSET ? +` + +type GetUserListRow struct { + Subject string `json:"subject"` + Name string `json:"name"` + Username string `json:"username"` + Picture interface{} `json:"picture"` + Email string `json:"email"` + EmailVerified int64 `json:"email_verified"` + Role int64 `json:"role"` + UpdatedAt sql.NullTime `json:"updated_at"` + Active sql.NullInt64 `json:"active"` +} + +func (q *Queries) GetUserList(ctx context.Context, offset int64) ([]GetUserListRow, error) { + rows, err := q.db.QueryContext(ctx, getUserList, offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetUserListRow + for rows.Next() { + var i GetUserListRow + if err := rows.Scan( + &i.Subject, + &i.Name, + &i.Username, + &i.Picture, + &i.Email, + &i.EmailVerified, + &i.Role, + &i.UpdatedAt, + &i.Active, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateUserRole = `-- name: UpdateUserRole :exec +UPDATE users +SET active = ?, + role=? +WHERE subject = ? +` + +type UpdateUserRoleParams struct { + Active sql.NullInt64 `json:"active"` + Role int64 `json:"role"` + Subject string `json:"subject"` +} + +func (q *Queries) UpdateUserRole(ctx context.Context, arg UpdateUserRoleParams) error { + _, err := q.db.ExecContext(ctx, updateUserRole, arg.Active, arg.Role, arg.Subject) + return err +} + +const userEmailExists = `-- name: UserEmailExists :one +SELECT EXISTS(SELECT 1 FROM users WHERE email = ? AND email_verified = 1) +` + +func (q *Queries) UserEmailExists(ctx context.Context, email string) (int64, error) { + row := q.db.QueryRowContext(ctx, userEmailExists, email) + var column_1 int64 + err := row.Scan(&column_1) + return column_1, err +} + +const verifyUserEmail = `-- name: VerifyUserEmail :exec +UPDATE users +SET email_verified = 1 +WHERE subject = ? +` + +func (q *Queries) VerifyUserEmail(ctx context.Context, subject string) error { + _, err := q.db.ExecContext(ctx, verifyUserEmail, subject) + return err +} diff --git a/database/migrations/20240309221547_init.down.sql b/database/migrations/20240309221547_init.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/database/init.sql b/database/migrations/20240309221547_init.up.sql similarity index 100% rename from database/init.sql rename to database/migrations/20240309221547_init.up.sql diff --git a/database/models.go b/database/models.go new file mode 100644 index 0000000..a00dec9 --- /dev/null +++ b/database/models.go @@ -0,0 +1,45 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 + +package database + +import ( + "database/sql" +) + +type ClientStore struct { + Subject string `json:"subject"` + Name string `json:"name"` + Secret string `json:"secret"` + Domain string `json:"domain"` + Owner string `json:"owner"` + Public sql.NullInt64 `json:"public"` + Sso sql.NullInt64 `json:"sso"` + Active sql.NullInt64 `json:"active"` +} + +type Otp struct { + Subject string `json:"subject"` + Secret string `json:"secret"` + Digits int64 `json:"digits"` +} + +type User struct { + Subject string `json:"subject"` + Name string `json:"name"` + Username string `json:"username"` + Password string `json:"password"` + Picture interface{} `json:"picture"` + Website interface{} `json:"website"` + Email string `json:"email"` + EmailVerified int64 `json:"email_verified"` + Pronouns interface{} `json:"pronouns"` + Birthdate sql.NullTime `json:"birthdate"` + Zoneinfo interface{} `json:"zoneinfo"` + Locale interface{} `json:"locale"` + Role int64 `json:"role"` + UpdatedAt sql.NullTime `json:"updated_at"` + Registered sql.NullInt64 `json:"registered"` + Active sql.NullInt64 `json:"active"` +} diff --git a/database/queries/manage-oauth.sql b/database/queries/manage-oauth.sql new file mode 100644 index 0000000..60373fd --- /dev/null +++ b/database/queries/manage-oauth.sql @@ -0,0 +1,32 @@ +-- name: GetClientInfo :one +SELECT secret, name, domain, public, sso, active +FROM client_store +WHERE subject = ? +LIMIT 1; + +-- name: GetAppList :many +SELECT subject, name, domain, owner, public, sso, active +FROM client_store +WHERE owner = ? + OR ? = 1 +LIMIT 25 OFFSET ?; + +-- name: InsertClientApp :exec +INSERT INTO client_store (subject, name, secret, domain, owner, public, sso, active) +VALUES (?, ?, ?, ?, ?, ?, ?, ?); + +-- name: UpdateClientApp :exec +UPDATE client_store +SET name = ?, + domain = ?, + public = ?, + sso = ?, + active = ? +WHERE subject = ? + AND owner = ?; + +-- name: resetClientAppSecret :exec +UPDATE client_store +SET secret = ? +WHERE subject = ? + AND owner = ?; diff --git a/database/queries/manage-users.sql b/database/queries/manage-users.sql new file mode 100644 index 0000000..b842424 --- /dev/null +++ b/database/queries/manage-users.sql @@ -0,0 +1,26 @@ +-- name: GetUserList :many +SELECT subject, + name, + username, + picture, + email, + email_verified, + role, + updated_at, + active +FROM users +LIMIT 25 OFFSET ?; + +-- name: UpdateUserRole :exec +UPDATE users +SET active = ?, + role=? +WHERE subject = ?; + +-- name: VerifyUserEmail :exec +UPDATE users +SET email_verified = 1 +WHERE subject = ?; + +-- name: UserEmailExists :one +SELECT EXISTS(SELECT 1 FROM users WHERE email = ? AND email_verified = 1); diff --git a/database/queries/users.sql b/database/queries/users.sql new file mode 100644 index 0000000..25790c8 --- /dev/null +++ b/database/queries/users.sql @@ -0,0 +1,70 @@ +-- name: HasUser :one +SELECT cast(count(subject) AS BOOLEAN) AS hasUser +FROM users; + +-- name: addUser :exec +INSERT INTO users (subject, name, username, password, email, email_verified, role, updated_at, active) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); + +-- name: checkLogin :one +SELECT subject, password, EXISTS(SELECT 1 FROM otp WHERE otp.subject = users.subject), email, email_verified +FROM users +WHERE username = ? +LIMIT 1; + +-- name: GetUser :one +SELECT name, + username, + picture, + website, + email, + email_verified, + pronouns, + birthdate, + zoneinfo, + locale, + updated_at, + active +FROM users +WHERE subject = ? +LIMIT 1; + +-- name: getUserPassword :one +SELECT password +FROM users +WHERE subject = ?; + +-- name: changeUserPassword :execrows +UPDATE users +SET password = ?, + updated_at = ? +WHERE subject = ? + AND password = ?; + +-- name: ModifyUser :execrows +UPDATE users +SET name = ?, + picture = ?, + website=?, + pronouns=?, + birthdate=?, + zoneinfo=?, + locale=?, + updated_at=? +WHERE subject = ?; + +-- name: SetTwoFactor :exec +INSERT OR +REPLACE +INTO otp (subject, secret, digits) +VALUES (?, ?, ?); + +-- name: DeleteTwoFactor :exec +DELETE +FROM otp +WHERE otp.subject = ?; + +-- name: GetTwoFactor :one +SELECT secret, digits +FROM otp +WHERE subject = ?; diff --git a/database/users.sql.go b/database/users.sql.go new file mode 100644 index 0000000..f818a0a --- /dev/null +++ b/database/users.sql.go @@ -0,0 +1,267 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: users.sql + +package database + +import ( + "context" + "database/sql" +) + +const deleteTwoFactor = `-- name: DeleteTwoFactor :exec +DELETE +FROM otp +WHERE otp.subject = ? +` + +func (q *Queries) DeleteTwoFactor(ctx context.Context, subject string) error { + _, err := q.db.ExecContext(ctx, deleteTwoFactor, subject) + return err +} + +const getTwoFactor = `-- name: GetTwoFactor :one +SELECT secret, digits +FROM otp +WHERE subject = ? +` + +type GetTwoFactorRow struct { + Secret string `json:"secret"` + Digits int64 `json:"digits"` +} + +func (q *Queries) GetTwoFactor(ctx context.Context, subject string) (GetTwoFactorRow, error) { + row := q.db.QueryRowContext(ctx, getTwoFactor, subject) + var i GetTwoFactorRow + err := row.Scan(&i.Secret, &i.Digits) + return i, err +} + +const getUser = `-- name: GetUser :one +SELECT name, + username, + picture, + website, + email, + email_verified, + pronouns, + birthdate, + zoneinfo, + locale, + updated_at, + active +FROM users +WHERE subject = ? +LIMIT 1 +` + +type GetUserRow struct { + Name string `json:"name"` + Username string `json:"username"` + Picture interface{} `json:"picture"` + Website interface{} `json:"website"` + Email string `json:"email"` + EmailVerified int64 `json:"email_verified"` + Pronouns interface{} `json:"pronouns"` + Birthdate sql.NullTime `json:"birthdate"` + Zoneinfo interface{} `json:"zoneinfo"` + Locale interface{} `json:"locale"` + UpdatedAt sql.NullTime `json:"updated_at"` + Active sql.NullInt64 `json:"active"` +} + +func (q *Queries) GetUser(ctx context.Context, subject string) (GetUserRow, error) { + row := q.db.QueryRowContext(ctx, getUser, subject) + var i GetUserRow + err := row.Scan( + &i.Name, + &i.Username, + &i.Picture, + &i.Website, + &i.Email, + &i.EmailVerified, + &i.Pronouns, + &i.Birthdate, + &i.Zoneinfo, + &i.Locale, + &i.UpdatedAt, + &i.Active, + ) + return i, err +} + +const hasUser = `-- name: HasUser :one +SELECT cast(count(subject) AS BOOLEAN) AS hasUser +FROM users +` + +func (q *Queries) HasUser(ctx context.Context) (bool, error) { + row := q.db.QueryRowContext(ctx, hasUser) + var hasuser bool + err := row.Scan(&hasuser) + return hasuser, err +} + +const modifyUser = `-- name: ModifyUser :execrows +UPDATE users +SET name = ?, + picture = ?, + website=?, + pronouns=?, + birthdate=?, + zoneinfo=?, + locale=?, + updated_at=? +WHERE subject = ? +` + +type ModifyUserParams struct { + Name string `json:"name"` + Picture interface{} `json:"picture"` + Website interface{} `json:"website"` + Pronouns interface{} `json:"pronouns"` + Birthdate sql.NullTime `json:"birthdate"` + Zoneinfo interface{} `json:"zoneinfo"` + Locale interface{} `json:"locale"` + UpdatedAt sql.NullTime `json:"updated_at"` + Subject string `json:"subject"` +} + +func (q *Queries) ModifyUser(ctx context.Context, arg ModifyUserParams) (int64, error) { + result, err := q.db.ExecContext(ctx, modifyUser, + arg.Name, + arg.Picture, + arg.Website, + arg.Pronouns, + arg.Birthdate, + arg.Zoneinfo, + arg.Locale, + arg.UpdatedAt, + arg.Subject, + ) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const setTwoFactor = `-- name: SetTwoFactor :exec +INSERT OR +REPLACE +INTO otp (subject, secret, digits) +VALUES (?, ?, ?) +` + +type SetTwoFactorParams struct { + Subject string `json:"subject"` + Secret string `json:"secret"` + Digits int64 `json:"digits"` +} + +func (q *Queries) SetTwoFactor(ctx context.Context, arg SetTwoFactorParams) error { + _, err := q.db.ExecContext(ctx, setTwoFactor, arg.Subject, arg.Secret, arg.Digits) + return err +} + +const addUser = `-- name: addUser :exec +INSERT INTO users (subject, name, username, password, email, email_verified, role, updated_at, active) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) +` + +type addUserParams struct { + Subject string `json:"subject"` + Name string `json:"name"` + Username string `json:"username"` + Password string `json:"password"` + Email string `json:"email"` + EmailVerified int64 `json:"email_verified"` + Role int64 `json:"role"` + UpdatedAt sql.NullTime `json:"updated_at"` + Active sql.NullInt64 `json:"active"` +} + +func (q *Queries) addUser(ctx context.Context, arg addUserParams) error { + _, err := q.db.ExecContext(ctx, addUser, + arg.Subject, + arg.Name, + arg.Username, + arg.Password, + arg.Email, + arg.EmailVerified, + arg.Role, + arg.UpdatedAt, + arg.Active, + ) + return err +} + +const changeUserPassword = `-- name: changeUserPassword :execrows +UPDATE users +SET password = ?, + updated_at = ? +WHERE subject = ? + AND password = ? +` + +type changeUserPasswordParams struct { + Password string `json:"password"` + UpdatedAt sql.NullTime `json:"updated_at"` + Subject string `json:"subject"` + Password_2 string `json:"password_2"` +} + +func (q *Queries) changeUserPassword(ctx context.Context, arg changeUserPasswordParams) (int64, error) { + result, err := q.db.ExecContext(ctx, changeUserPassword, + arg.Password, + arg.UpdatedAt, + arg.Subject, + arg.Password_2, + ) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const checkLogin = `-- name: checkLogin :one +SELECT subject, password, EXISTS(SELECT 1 FROM otp WHERE otp.subject = users.subject), email, email_verified +FROM users +WHERE username = ? +LIMIT 1 +` + +type checkLoginRow struct { + Subject string `json:"subject"` + Password string `json:"password"` + Column3 int64 `json:"column_3"` + Email string `json:"email"` + EmailVerified int64 `json:"email_verified"` +} + +func (q *Queries) checkLogin(ctx context.Context, username string) (checkLoginRow, error) { + row := q.db.QueryRowContext(ctx, checkLogin, username) + var i checkLoginRow + err := row.Scan( + &i.Subject, + &i.Password, + &i.Column3, + &i.Email, + &i.EmailVerified, + ) + return i, err +} + +const getUserPassword = `-- name: getUserPassword :one +SELECT password +FROM users +WHERE subject = ? +` + +func (q *Queries) getUserPassword(ctx context.Context, subject string) (string, error) { + row := q.db.QueryRowContext(ctx, getUserPassword, subject) + var password string + err := row.Scan(&password) + return password, err +} diff --git a/sqlc.yaml b/sqlc.yaml new file mode 100644 index 0000000..953e616 --- /dev/null +++ b/sqlc.yaml @@ -0,0 +1,15 @@ +version: "2" +sql: + - engine: sqlite + queries: database/queries + schema: database/migrations + gen: + go: + package: "database" + out: "database" + emit_json_tags: true + overrides: + - column: "routes.flags" + go_type: "github.com/1f349/violet/target.Flags" + - column: "redirects.flags" + go_type: "github.com/1f349/violet/target.Flags"