284 lines
8.0 KiB
Go
284 lines
8.0 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"code.capytal.cc/capytal/comicverse/model"
|
|
"code.capytal.cc/loreddev/x/tinyssert"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type Permissions struct {
|
|
baseRepostiory
|
|
}
|
|
|
|
// Must be initiated after [User] and [Publication]
|
|
func NewPermissions(
|
|
ctx context.Context,
|
|
db *sql.DB,
|
|
log *slog.Logger,
|
|
assert tinyssert.Assertions,
|
|
) (*Permissions, error) {
|
|
b := newBaseRepostiory(ctx, db, log, assert)
|
|
|
|
tx, err := db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
q := fmt.Sprintf(`
|
|
CREATE TABLE IF NOT EXISTS publication_permissions (
|
|
publication_id TEXT NOT NULL,
|
|
user_id TEXT NOT NULL,
|
|
permissions_value INTEGER NOT NULL DEFAULT '0',
|
|
_permissions_text TEXT NOT NULL DEFAULT '', -- For display purposes only, may not always be up-to-date
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
|
|
PRIMARY KEY(publication_id, user_id)
|
|
FOREIGN KEY(publication_id)
|
|
REFERENCES publications (id)
|
|
ON DELETE CASCADE
|
|
ON UPDATE RESTRICT,
|
|
FOREIGN KEY(user_id)
|
|
REFERENCES users (id)
|
|
ON DELETE CASCADE
|
|
ON UPDATE RESTRICT
|
|
)
|
|
`)
|
|
|
|
_, err = tx.ExecContext(ctx, q)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return nil, errors.Join(errors.New("unable to create publication tables"), err)
|
|
}
|
|
|
|
return &Permissions{baseRepostiory: b}, nil
|
|
}
|
|
|
|
func (repo Permissions) Create(publication, user uuid.UUID, permissions model.Permissions) error {
|
|
repo.assert.NotNil(repo.db)
|
|
repo.assert.NotNil(repo.ctx)
|
|
repo.assert.NotNil(repo.ctx)
|
|
|
|
tx, err := repo.db.BeginTx(repo.ctx, nil)
|
|
if err != nil {
|
|
return errors.Join(ErrDatabaseConn, err)
|
|
}
|
|
|
|
q := `
|
|
INSERT INTO publication_permissions (publication_id, user_id, permissions_value, _permissions_text, created_at, updated_at)
|
|
VALUES (:publication_id, :user_id, :permissions_value, :permissions_text, :created_at, :updated_at)
|
|
`
|
|
|
|
now := time.Now()
|
|
|
|
log := repo.log.With(slog.String("publication_id", publication.String()),
|
|
slog.String("user_id", user.String()),
|
|
slog.String("permissions", fmt.Sprintf("%d", permissions)),
|
|
slog.String("permissions_text", permissions.String()),
|
|
slog.String("query", q))
|
|
log.DebugContext(repo.ctx, "Inserting new publication permissions")
|
|
|
|
_, err = tx.ExecContext(repo.ctx, q,
|
|
sql.Named("publication_id", publication),
|
|
sql.Named("user_id", user),
|
|
sql.Named("permissions_value", permissions),
|
|
sql.Named("permissions_text", permissions.String()),
|
|
sql.Named("created_at", now.Format(dateFormat)),
|
|
sql.Named("updated_at", now.Format(dateFormat)),
|
|
)
|
|
if err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to insert publication permissions", slog.String("error", err.Error()))
|
|
return errors.Join(ErrExecuteQuery, err)
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to commit transaction", slog.String("error", err.Error()))
|
|
return errors.Join(ErrCommitQuery, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (repo Permissions) GetByID(publication uuid.UUID, user uuid.UUID) (model.Permissions, error) {
|
|
repo.assert.NotNil(repo.db)
|
|
repo.assert.NotNil(repo.ctx)
|
|
repo.assert.NotNil(repo.log)
|
|
|
|
q := `
|
|
SELECT permissions_value FROM publication_permissions
|
|
WHERE publication_id = :publication_id
|
|
AND user_id = :user_id
|
|
`
|
|
|
|
log := repo.log.With(slog.String("projcet_id", publication.String()),
|
|
slog.String("user_id", user.String()),
|
|
slog.String("query", q))
|
|
log.DebugContext(repo.ctx, "Getting by ID")
|
|
|
|
row := repo.db.QueryRowContext(repo.ctx, q,
|
|
sql.Named("publication_id", user),
|
|
sql.Named("user_id", user))
|
|
|
|
var p model.Permissions
|
|
if err := row.Scan(&p); err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to get permissions by ID", slog.String("error", err.Error()))
|
|
return model.Permissions(0), errors.Join(ErrExecuteQuery, err)
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
// GetByUserID returns a publication_id-to-permissions map containing all publications and permissions that said userID
|
|
// has relation to.
|
|
func (repo Permissions) GetByUserID(user uuid.UUID) (permissions map[uuid.UUID]model.Permissions, err error) {
|
|
repo.assert.NotNil(repo.db)
|
|
repo.assert.NotNil(repo.ctx)
|
|
repo.assert.NotNil(repo.log)
|
|
|
|
// Begin tx so we don't read rows as they are being updated
|
|
tx, err := repo.db.BeginTx(repo.ctx, nil)
|
|
if err != nil {
|
|
return nil, errors.Join(ErrDatabaseConn, err)
|
|
}
|
|
|
|
q := `
|
|
SELECT publication_id, permissions_value FROM publication_permissions
|
|
WHERE user_id = :user_id
|
|
`
|
|
|
|
log := repo.log.With(slog.String("user_id", user.String()),
|
|
slog.String("query", q))
|
|
log.DebugContext(repo.ctx, "Getting by user ID")
|
|
|
|
rows, err := tx.QueryContext(repo.ctx, q, sql.Named("user_id", user))
|
|
if err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to get permissions by user ID", slog.String("error", err.Error()))
|
|
return nil, errors.Join(ErrExecuteQuery, err)
|
|
}
|
|
|
|
defer func() {
|
|
err = rows.Close()
|
|
if err != nil {
|
|
err = errors.Join(ErrCloseConn, err)
|
|
}
|
|
}()
|
|
|
|
ps := map[uuid.UUID]model.Permissions{}
|
|
|
|
for rows.Next() {
|
|
var publication uuid.UUID
|
|
var permissions model.Permissions
|
|
|
|
err := rows.Scan(&publication, &permissions)
|
|
if err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to scan permissions of user id", slog.String("error", err.Error()))
|
|
return nil, errors.Join(ErrInvalidOutput, err)
|
|
}
|
|
|
|
ps[publication] = permissions
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to commit transaction", slog.String("error", err.Error()))
|
|
return nil, errors.Join(ErrCommitQuery, err)
|
|
}
|
|
|
|
return ps, nil
|
|
}
|
|
|
|
func (repo Permissions) Update(publication, user uuid.UUID, permissions model.Permissions) error {
|
|
repo.assert.NotNil(repo.db)
|
|
repo.assert.NotNil(repo.ctx)
|
|
repo.assert.NotNil(repo.log)
|
|
|
|
tx, err := repo.db.BeginTx(repo.ctx, nil)
|
|
if err != nil {
|
|
return errors.Join(ErrDatabaseConn, err)
|
|
}
|
|
|
|
q := `
|
|
UPDATE publication_permissions
|
|
SET permissions_value = :permissions_value
|
|
_permissions_text = :permissions_text
|
|
updated_at = :updated_at
|
|
WHERE publication_uuid = :publication_uuid
|
|
AND user_uuid = :user_uuid
|
|
`
|
|
|
|
log := repo.log.With(slog.String("publication_id", publication.String()),
|
|
slog.String("user_id", user.String()),
|
|
slog.String("permissions", fmt.Sprintf("%d", permissions)),
|
|
slog.String("permissions_text", permissions.String()),
|
|
slog.String("query", q))
|
|
log.DebugContext(repo.ctx, "Updating publication permissions")
|
|
|
|
now := time.Now()
|
|
|
|
_, err = tx.ExecContext(repo.ctx, q,
|
|
sql.Named("permissions_value", permissions),
|
|
sql.Named("permissions_text", permissions.String()),
|
|
sql.Named("updated_at", now.Format(dateFormat)),
|
|
sql.Named("publication_id", publication),
|
|
sql.Named("user_id", user),
|
|
)
|
|
if err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to update publication permissions", slog.String("error", err.Error()))
|
|
return errors.Join(ErrExecuteQuery, err)
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to commit transaction", slog.String("error", err.Error()))
|
|
return errors.Join(ErrCommitQuery, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (repo Permissions) Delete(publication, user uuid.UUID) error {
|
|
repo.assert.NotNil(repo.db)
|
|
repo.assert.NotNil(repo.ctx)
|
|
repo.assert.NotNil(repo.ctx)
|
|
|
|
tx, err := repo.db.BeginTx(repo.ctx, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
q := `
|
|
DELETE FROM publication_permissions
|
|
WHERE publication_id = :publication_id
|
|
AND user_id = :user_id
|
|
`
|
|
|
|
log := repo.log.With(slog.String("publication_id", publication.String()),
|
|
slog.String("user_id", user.String()),
|
|
slog.String("query", q))
|
|
log.DebugContext(repo.ctx, "Deleting publication permissions")
|
|
|
|
_, err = tx.ExecContext(repo.ctx, q,
|
|
sql.Named("publication_id", publication),
|
|
sql.Named("user_id", user),
|
|
)
|
|
if err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to delete publication permissions", slog.String("error", err.Error()))
|
|
return errors.Join(ErrExecuteQuery, err)
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
log.ErrorContext(repo.ctx, "Failed to commit transaction", slog.String("error", err.Error()))
|
|
return errors.Join(ErrCommitQuery, err)
|
|
}
|
|
|
|
return nil
|
|
}
|