init
This commit is contained in:
commit
49b24e5773
22 changed files with 1499 additions and 0 deletions
78
db/db.go
Normal file
78
db/db.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"os"
|
||||
|
||||
"codeberg.org/u1f320/filer/db/queries"
|
||||
"codeberg.org/u1f320/filer/store"
|
||||
"codeberg.org/u1f320/filer/store/local"
|
||||
"emperror.dev/errors"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/rs/zerolog/log"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
|
||||
// sqlite driver
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
godotenv.Load()
|
||||
}
|
||||
|
||||
const defaultDBName = "filer.db?_pragma=foreign_keys(1)"
|
||||
|
||||
type DB struct {
|
||||
*queries.Queries
|
||||
|
||||
db *sql.DB
|
||||
Store store.Store
|
||||
}
|
||||
|
||||
func New() (*DB, error) {
|
||||
sqldb, err := sql.Open("sqlite", defaultDBName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating database")
|
||||
}
|
||||
|
||||
err = migrateDB(sqldb, defaultDBName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "migrating database")
|
||||
}
|
||||
|
||||
db := &DB{
|
||||
Queries: queries.New(sqldb),
|
||||
db: sqldb,
|
||||
}
|
||||
|
||||
switch os.Getenv("STORAGE") {
|
||||
case local.StoreKey:
|
||||
db.Store, err = local.New(os.Getenv("LOCAL_PATH"))
|
||||
default:
|
||||
err = store.ErrInvalidStoreKey
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating store")
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
//go:embed migrations
|
||||
var migrateFS embed.FS
|
||||
|
||||
func migrateDB(db *sql.DB, dbName string) error {
|
||||
migrations := &migrate.EmbedFileSystemMigrationSource{
|
||||
FileSystem: migrateFS,
|
||||
Root: "migrations",
|
||||
}
|
||||
|
||||
n, err := migrate.Exec(db, "sqlite3", migrations, migrate.Up)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "executing migrations")
|
||||
}
|
||||
|
||||
log.Info().Int("count", n).Msg("Performed migrations!")
|
||||
return nil
|
||||
}
|
33
db/migrations/1692914919-init.sql
Normal file
33
db/migrations/1692914919-init.sql
Normal file
|
@ -0,0 +1,33 @@
|
|||
-- +migrate Up
|
||||
|
||||
create table users (
|
||||
id integer primary key,
|
||||
username text not null unique,
|
||||
password text not null,
|
||||
is_admin boolean not null default false
|
||||
);
|
||||
|
||||
create table tokens (
|
||||
id integer primary key,
|
||||
user_id integer not null references users (id) on delete cascade,
|
||||
token text not null unique
|
||||
);
|
||||
|
||||
create table files (
|
||||
id text primary key, -- uuid
|
||||
user_id integer not null references users (id) on delete cascade,
|
||||
filename text not null,
|
||||
content_type text not null,
|
||||
hash text not null,
|
||||
size integer not null,
|
||||
created_at integer not null,
|
||||
expires integer,
|
||||
|
||||
unique(filename, hash)
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
|
||||
drop table files;
|
||||
drop table tokens;
|
||||
drop table users;
|
47
db/queries.sql
Normal file
47
db/queries.sql
Normal file
|
@ -0,0 +1,47 @@
|
|||
-- name: CreateUser :one
|
||||
insert into users (username, password) values (@username, @password)
|
||||
returning *;
|
||||
|
||||
-- name: GetUser :one
|
||||
select * from users where username = @username;
|
||||
|
||||
-- name: GetUsers :many
|
||||
select * from users order by id;
|
||||
|
||||
-- name: UpdateUser :one
|
||||
update users set
|
||||
username = @username, password = @password, is_admin = @is_admin
|
||||
where id = @id
|
||||
returning *;
|
||||
|
||||
-- name: DeleteUser :exec
|
||||
delete from users where id = @id;
|
||||
|
||||
-- name: GetUserByToken :one
|
||||
select u.* from users u join tokens t on u.id = t.user_id
|
||||
where t.token = @token;
|
||||
|
||||
-- name: CreateToken :one
|
||||
insert into tokens (user_id, token) values (@user_id, @token)
|
||||
returning *;
|
||||
|
||||
-- name: DeleteToken :exec
|
||||
delete from tokens where id = @id;
|
||||
|
||||
-- name: CreateFile :one
|
||||
insert into files
|
||||
(id, user_id, filename, content_type, hash, size, created_at, expires)
|
||||
values (@id, @user_id, @filename, @content_type, @hash, @size, unixepoch(), @expires)
|
||||
returning *;
|
||||
|
||||
-- name: GetFileByID :one
|
||||
select * from files where id = @id;
|
||||
|
||||
-- name: GetFileByName :one
|
||||
select * from files where filename = @filename and hash = @hash;
|
||||
|
||||
-- name: GetExpiredFiles :many
|
||||
select * from files where expires < unixepoch();
|
||||
|
||||
-- name: DeleteFile :exec
|
||||
delete from files where id = @id;
|
31
db/queries/db.go
Normal file
31
db/queries/db.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.20.0
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
35
db/queries/models.go
Normal file
35
db/queries/models.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.20.0
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
ID uuid.UUID
|
||||
UserID int64
|
||||
Filename string
|
||||
ContentType string
|
||||
Hash string
|
||||
Size int64
|
||||
CreatedAt int64
|
||||
Expires sql.NullInt64
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
Token string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64
|
||||
Username string
|
||||
Password string
|
||||
IsAdmin bool
|
||||
}
|
297
db/queries/queries.sql.go
Normal file
297
db/queries/queries.sql.go
Normal file
|
@ -0,0 +1,297 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.20.0
|
||||
// source: queries.sql
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createFile = `-- name: CreateFile :one
|
||||
insert into files
|
||||
(id, user_id, filename, content_type, hash, size, created_at, expires)
|
||||
values (?1, ?2, ?3, ?4, ?5, ?6, unixepoch(), ?7)
|
||||
returning id, user_id, filename, content_type, hash, size, created_at, expires
|
||||
`
|
||||
|
||||
type CreateFileParams struct {
|
||||
ID uuid.UUID
|
||||
UserID int64
|
||||
Filename string
|
||||
ContentType string
|
||||
Hash string
|
||||
Size int64
|
||||
Expires sql.NullInt64
|
||||
}
|
||||
|
||||
func (q *Queries) CreateFile(ctx context.Context, arg CreateFileParams) (File, error) {
|
||||
row := q.db.QueryRowContext(ctx, createFile,
|
||||
arg.ID,
|
||||
arg.UserID,
|
||||
arg.Filename,
|
||||
arg.ContentType,
|
||||
arg.Hash,
|
||||
arg.Size,
|
||||
arg.Expires,
|
||||
)
|
||||
var i File
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Filename,
|
||||
&i.ContentType,
|
||||
&i.Hash,
|
||||
&i.Size,
|
||||
&i.CreatedAt,
|
||||
&i.Expires,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createToken = `-- name: CreateToken :one
|
||||
insert into tokens (user_id, token) values (?1, ?2)
|
||||
returning id, user_id, token
|
||||
`
|
||||
|
||||
type CreateTokenParams struct {
|
||||
UserID int64
|
||||
Token string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateToken(ctx context.Context, arg CreateTokenParams) (Token, error) {
|
||||
row := q.db.QueryRowContext(ctx, createToken, arg.UserID, arg.Token)
|
||||
var i Token
|
||||
err := row.Scan(&i.ID, &i.UserID, &i.Token)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createUser = `-- name: CreateUser :one
|
||||
insert into users (username, password) values (?1, ?2)
|
||||
returning id, username, password, is_admin
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, createUser, arg.Username, arg.Password)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteFile = `-- name: DeleteFile :exec
|
||||
delete from files where id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteFile(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteFile, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteToken = `-- name: DeleteToken :exec
|
||||
delete from tokens where id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteToken(ctx context.Context, id int64) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteToken, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteUser = `-- name: DeleteUser :exec
|
||||
delete from users where id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteUser(ctx context.Context, id int64) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteUser, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const getExpiredFiles = `-- name: GetExpiredFiles :many
|
||||
select id, user_id, filename, content_type, hash, size, created_at, expires from files where expires < unixepoch()
|
||||
`
|
||||
|
||||
func (q *Queries) GetExpiredFiles(ctx context.Context) ([]File, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getExpiredFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []File
|
||||
for rows.Next() {
|
||||
var i File
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Filename,
|
||||
&i.ContentType,
|
||||
&i.Hash,
|
||||
&i.Size,
|
||||
&i.CreatedAt,
|
||||
&i.Expires,
|
||||
); 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 getFileByID = `-- name: GetFileByID :one
|
||||
select id, user_id, filename, content_type, hash, size, created_at, expires from files where id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) GetFileByID(ctx context.Context, id uuid.UUID) (File, error) {
|
||||
row := q.db.QueryRowContext(ctx, getFileByID, id)
|
||||
var i File
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Filename,
|
||||
&i.ContentType,
|
||||
&i.Hash,
|
||||
&i.Size,
|
||||
&i.CreatedAt,
|
||||
&i.Expires,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getFileByName = `-- name: GetFileByName :one
|
||||
select id, user_id, filename, content_type, hash, size, created_at, expires from files where filename = ?1 and hash = ?2
|
||||
`
|
||||
|
||||
type GetFileByNameParams struct {
|
||||
Filename string
|
||||
Hash string
|
||||
}
|
||||
|
||||
func (q *Queries) GetFileByName(ctx context.Context, arg GetFileByNameParams) (File, error) {
|
||||
row := q.db.QueryRowContext(ctx, getFileByName, arg.Filename, arg.Hash)
|
||||
var i File
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Filename,
|
||||
&i.ContentType,
|
||||
&i.Hash,
|
||||
&i.Size,
|
||||
&i.CreatedAt,
|
||||
&i.Expires,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUser = `-- name: GetUser :one
|
||||
select id, username, password, is_admin from users where username = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, username string) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUser, username)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByToken = `-- name: GetUserByToken :one
|
||||
select u.id, u.username, u.password, u.is_admin from users u join tokens t on u.id = t.user_id
|
||||
where t.token = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByToken(ctx context.Context, token string) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUserByToken, token)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUsers = `-- name: GetUsers :many
|
||||
select id, username, password, is_admin from users order by id
|
||||
`
|
||||
|
||||
func (q *Queries) GetUsers(ctx context.Context) ([]User, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []User
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.IsAdmin,
|
||||
); 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 updateUser = `-- name: UpdateUser :one
|
||||
update users set
|
||||
username = ?1, password = ?2, is_admin = ?3
|
||||
where id = ?4
|
||||
returning id, username, password, is_admin
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
Username string
|
||||
Password string
|
||||
IsAdmin bool
|
||||
ID int64
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateUser,
|
||||
arg.Username,
|
||||
arg.Password,
|
||||
arg.IsAdmin,
|
||||
arg.ID,
|
||||
)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
25
db/schema.sql
Normal file
25
db/schema.sql
Normal file
|
@ -0,0 +1,25 @@
|
|||
create table users (
|
||||
id integer primary key,
|
||||
username text not null unique,
|
||||
password text not null,
|
||||
is_admin boolean not null default false
|
||||
);
|
||||
|
||||
create table tokens (
|
||||
id integer primary key,
|
||||
user_id integer not null references users (id) on delete cascade,
|
||||
token text not null unique
|
||||
);
|
||||
|
||||
create table files (
|
||||
id text primary key, -- uuid
|
||||
user_id integer not null references users (id) on delete cascade,
|
||||
filename text not null,
|
||||
content_type text not null,
|
||||
hash text not null,
|
||||
size integer not null,
|
||||
created_at integer not null,
|
||||
expires integer,
|
||||
|
||||
unique(filename, hash)
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue