Add auth, rest api, svelte frontend, build structure

This commit is contained in:
2025-06-02 08:44:10 +02:00
parent 97be451306
commit 17ab7c4929
222 changed files with 3057 additions and 136 deletions

31
internal/repository/db.go Normal file
View File

@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package repository
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,
}
}

View File

@@ -3,6 +3,7 @@ package repository
import (
"database/sql"
"log/slog"
"os"
"sync"
)
@@ -21,6 +22,11 @@ func Connect(dsnURI string) {
}
repo = New(dbConn)
err = checkDBVersion(dbConn)
if err != nil {
slog.Error("DB Connection: Failed DB version check", "error", err)
os.Exit(1)
}
})
}
@@ -32,10 +38,11 @@ func GetConnection() (*sql.DB, error) {
return dbConn, nil
}
func GetRepository() (*Queries, error) {
func GetRepository() *Queries {
if repo == nil {
slog.Error("Database connection not initialized!")
os.Exit(1)
}
return repo, nil
return repo
}

View File

@@ -36,14 +36,14 @@ func checkDBVersion(db *sql.DB) error {
v, dirty, err := m.Version()
if err != nil {
if err == migrate.ErrNilVersion {
slog.Warn("Legacy database without version or missing database file!")
slog.Error("Legacy database without version or missing database file!")
} else {
return err
}
}
if v < Version {
return fmt.Errorf("unsupported database version %d, need %d.\nPlease backup your database file and run cc-backend -migrate-db", v, Version)
return fmt.Errorf("unsupported database version %d, need %d.\nPlease backup your database file and run server -migrate-db", v, Version)
} else if v > Version {
return fmt.Errorf("unsupported database version %d, need %d.\nPlease refer to documentation how to downgrade db with external migrate tool", v, Version)
}
@@ -61,7 +61,7 @@ func getMigrateInstance(dsnURI string) (m *migrate.Migrate, err error) {
slog.Error("failed to get instance", "Error", err)
}
m, err = migrate.NewWithSourceInstance("iofs", d, dsnURI)
m, err = migrate.NewWithSourceInstance("iofs", d, "sqlite3://"+dsnURI)
if err != nil {
return m, err
}
@@ -75,7 +75,7 @@ func MigrateDB(db string) error {
return err
}
v, dirty, err := m.Version()
_, dirty, err := m.Version()
if err != nil {
if err == migrate.ErrNilVersion {
slog.Warn("Legacy database without version or missing database file!")
@@ -84,10 +84,6 @@ func MigrateDB(db string) error {
}
}
if v < Version {
slog.Info("unsupported database version %d, need %d.\nPlease backup your database file and run cc-backend -migrate-db", v, Version)
}
if dirty {
return fmt.Errorf("last migration to version %d has failed, please fix the db manually and force version with -force-db flag", Version)
}

View File

@@ -1,11 +0,0 @@
CREATE TABLE news (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
bio TEXT
);
CREATE TABLE retailer (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
bio TEXT
);

View File

@@ -0,0 +1,23 @@
CREATE TABLE IF NOT EXISTS news (
id INTEGER PRIMARY KEY,
news_title TEXT NOT NULL,
news_text TEXT NOT NULL,
news_date DATETIME,
news_publish DATETIME,
display TINYINT NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS retailer (
id INTEGER PRIMARY KEY,
shopname TEXT NOT NULL,
url TEXT NOT NULL,
country TEXT NOT NULL,
display TINYINT NOT NULL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS app_user (
user_name TEXT PRIMARY KEY,
user_pass TEXT DEFAULT NULL,
realname TEXT DEFAULT NULL,
email TEXT DEFAULT NULL
);

View File

@@ -0,0 +1,33 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package repository
import (
"time"
)
type AppUser struct {
UserName string `db:"user_name" json:"userName"`
UserPass *string `db:"user_pass" json:"userPass"`
Realname *string `db:"realname" json:"realname"`
Email *string `db:"email" json:"email"`
}
type News struct {
ID int64 `db:"id" json:"id"`
NewsTitle string `db:"news_title" json:"newsTitle"`
NewsText string `db:"news_text" json:"newsText"`
NewsDate *time.Time `db:"news_date" json:"newsDate"`
NewsPublish *time.Time `db:"news_publish" json:"newsPublish"`
Display int64 `db:"display" json:"display"`
}
type Retailer struct {
ID int64 `db:"id" json:"id"`
Shopname string `db:"shopname" json:"shopname"`
Url string `db:"url" json:"url"`
Country string `db:"country" json:"country"`
Display int64 `db:"display" json:"display"`
}

View File

@@ -0,0 +1,386 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: query.sql
package repository
import (
"context"
"time"
)
const createNewsEntry = `-- name: CreateNewsEntry :exec
INSERT INTO news (
news_title, news_text, news_date,
news_publish, display
) VALUES (?, ?, ?, ?, ?)
`
type CreateNewsEntryParams struct {
NewsTitle string `db:"news_title" json:"newsTitle"`
NewsText string `db:"news_text" json:"newsText"`
NewsDate *time.Time `db:"news_date" json:"newsDate"`
NewsPublish *time.Time `db:"news_publish" json:"newsPublish"`
Display int64 `db:"display" json:"display"`
}
func (q *Queries) CreateNewsEntry(ctx context.Context, arg CreateNewsEntryParams) error {
_, err := q.db.ExecContext(ctx, createNewsEntry,
arg.NewsTitle,
arg.NewsText,
arg.NewsDate,
arg.NewsPublish,
arg.Display,
)
return err
}
const createRetailer = `-- name: CreateRetailer :exec
INSERT INTO retailer (
shopname, url, country, display
) VALUES (?, ?, ?, ?)
`
type CreateRetailerParams struct {
Shopname string `db:"shopname" json:"shopname"`
Url string `db:"url" json:"url"`
Country string `db:"country" json:"country"`
Display int64 `db:"display" json:"display"`
}
func (q *Queries) CreateRetailer(ctx context.Context, arg CreateRetailerParams) error {
_, err := q.db.ExecContext(ctx, createRetailer,
arg.Shopname,
arg.Url,
arg.Country,
arg.Display,
)
return err
}
const createUser = `-- name: CreateUser :exec
INSERT INTO app_user (
user_name, user_pass
)
VALUES (?, ?)
`
type CreateUserParams struct {
UserName string `db:"user_name" json:"userName"`
UserPass *string `db:"user_pass" json:"userPass"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) error {
_, err := q.db.ExecContext(ctx, createUser, arg.UserName, arg.UserPass)
return err
}
const deleteNewsEntry = `-- name: DeleteNewsEntry :exec
DELETE FROM news
WHERE id = ?
`
func (q *Queries) DeleteNewsEntry(ctx context.Context, id int64) error {
_, err := q.db.ExecContext(ctx, deleteNewsEntry, id)
return err
}
const deleteRetailer = `-- name: DeleteRetailer :exec
DELETE FROM news
WHERE id = ?
`
func (q *Queries) DeleteRetailer(ctx context.Context, id int64) error {
_, err := q.db.ExecContext(ctx, deleteRetailer, id)
return err
}
const deleteUser = `-- name: DeleteUser :exec
DELETE FROM app_user
WHERE user_name = ?
`
func (q *Queries) DeleteUser(ctx context.Context, userName string) error {
_, err := q.db.ExecContext(ctx, deleteUser, userName)
return err
}
const getNewsEntry = `-- name: GetNewsEntry :one
SELECT id, news_title, news_text, news_date, news_publish, display FROM news
WHERE id = ? LIMIT 1
`
func (q *Queries) GetNewsEntry(ctx context.Context, id int64) (News, error) {
row := q.db.QueryRowContext(ctx, getNewsEntry, id)
var i News
err := row.Scan(
&i.ID,
&i.NewsTitle,
&i.NewsText,
&i.NewsDate,
&i.NewsPublish,
&i.Display,
)
return i, err
}
const getUser = `-- name: GetUser :one
SELECT user_name, user_pass, realname, email FROM app_user
WHERE user_name = ? LIMIT 1
`
func (q *Queries) GetUser(ctx context.Context, userName string) (AppUser, error) {
row := q.db.QueryRowContext(ctx, getUser, userName)
var i AppUser
err := row.Scan(
&i.UserName,
&i.UserPass,
&i.Realname,
&i.Email,
)
return i, err
}
const listActiveNews = `-- name: ListActiveNews :many
SELECT id, news_title, news_text, news_date, news_publish, display FROM news
WHERE display = 1
ORDER BY news_date
`
func (q *Queries) ListActiveNews(ctx context.Context) ([]News, error) {
rows, err := q.db.QueryContext(ctx, listActiveNews)
if err != nil {
return nil, err
}
defer rows.Close()
var items []News
for rows.Next() {
var i News
if err := rows.Scan(
&i.ID,
&i.NewsTitle,
&i.NewsText,
&i.NewsDate,
&i.NewsPublish,
&i.Display,
); 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 listActiveRetailers = `-- name: ListActiveRetailers :many
SELECT id, shopname, url, country, display FROM retailer
WHERE display = 1
ORDER BY shopname
`
func (q *Queries) ListActiveRetailers(ctx context.Context) ([]Retailer, error) {
rows, err := q.db.QueryContext(ctx, listActiveRetailers)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Retailer
for rows.Next() {
var i Retailer
if err := rows.Scan(
&i.ID,
&i.Shopname,
&i.Url,
&i.Country,
&i.Display,
); 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 listNews = `-- name: ListNews :many
SELECT id, news_title, news_text, news_date, news_publish, display FROM news
ORDER BY news_date
`
func (q *Queries) ListNews(ctx context.Context) ([]News, error) {
rows, err := q.db.QueryContext(ctx, listNews)
if err != nil {
return nil, err
}
defer rows.Close()
var items []News
for rows.Next() {
var i News
if err := rows.Scan(
&i.ID,
&i.NewsTitle,
&i.NewsText,
&i.NewsDate,
&i.NewsPublish,
&i.Display,
); 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 listRetailers = `-- name: ListRetailers :many
SELECT id, shopname, url, country, display FROM retailer
ORDER BY shopname
`
func (q *Queries) ListRetailers(ctx context.Context) ([]Retailer, error) {
rows, err := q.db.QueryContext(ctx, listRetailers)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Retailer
for rows.Next() {
var i Retailer
if err := rows.Scan(
&i.ID,
&i.Shopname,
&i.Url,
&i.Country,
&i.Display,
); 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 listUsers = `-- name: ListUsers :many
SELECT user_name, user_pass, realname, email FROM app_user
ORDER BY user_name
`
func (q *Queries) ListUsers(ctx context.Context) ([]AppUser, error) {
rows, err := q.db.QueryContext(ctx, listUsers)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AppUser
for rows.Next() {
var i AppUser
if err := rows.Scan(
&i.UserName,
&i.UserPass,
&i.Realname,
&i.Email,
); 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 updateNewsEntry = `-- name: UpdateNewsEntry :exec
UPDATE news
SET
news_title = ?, news_text = ?,
news_date = ?, news_publish = ?, display = ?
WHERE id = ?
`
type UpdateNewsEntryParams struct {
NewsTitle string `db:"news_title" json:"newsTitle"`
NewsText string `db:"news_text" json:"newsText"`
NewsDate *time.Time `db:"news_date" json:"newsDate"`
NewsPublish *time.Time `db:"news_publish" json:"newsPublish"`
Display int64 `db:"display" json:"display"`
ID int64 `db:"id" json:"id"`
}
func (q *Queries) UpdateNewsEntry(ctx context.Context, arg UpdateNewsEntryParams) error {
_, err := q.db.ExecContext(ctx, updateNewsEntry,
arg.NewsTitle,
arg.NewsText,
arg.NewsDate,
arg.NewsPublish,
arg.Display,
arg.ID,
)
return err
}
const updateRetailer = `-- name: UpdateRetailer :exec
UPDATE retailer
SET shopname = ?, url = ?, country = ?, display = ?
WHERE id = ?
`
type UpdateRetailerParams struct {
Shopname string `db:"shopname" json:"shopname"`
Url string `db:"url" json:"url"`
Country string `db:"country" json:"country"`
Display int64 `db:"display" json:"display"`
ID int64 `db:"id" json:"id"`
}
func (q *Queries) UpdateRetailer(ctx context.Context, arg UpdateRetailerParams) error {
_, err := q.db.ExecContext(ctx, updateRetailer,
arg.Shopname,
arg.Url,
arg.Country,
arg.Display,
arg.ID,
)
return err
}
const updateUser = `-- name: UpdateUser :exec
UPDATE app_user
SET user_pass = ?
WHERE user_name = ?
`
type UpdateUserParams struct {
UserPass *string `db:"user_pass" json:"userPass"`
UserName string `db:"user_name" json:"userName"`
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
_, err := q.db.ExecContext(ctx, updateUser, arg.UserPass, arg.UserName)
return err
}

View File

@@ -1,25 +1,75 @@
-- name: GetAuthor :one
SELECT * FROM authors
-- name: GetUser :one
SELECT * FROM app_user
WHERE user_name = ? LIMIT 1;
--
-- name: ListUsers :many
SELECT * FROM app_user
ORDER BY user_name;
-- name: CreateUser :exec
INSERT INTO app_user (
user_name, user_pass
)
VALUES (?, ?);
-- name: UpdateUser :exec
UPDATE app_user
SET user_pass = ?
WHERE user_name = ?;
-- name: DeleteUser :exec
DELETE FROM app_user
WHERE user_name = ?;
--
-- name: GetNewsEntry :one
SELECT * FROM news
WHERE id = ? LIMIT 1;
-- name: ListAuthors :many
SELECT * FROM authors
ORDER BY name;
-- name: ListNews :many
SELECT * FROM news
ORDER BY news_date;
-- name: CreateAuthor :one
INSERT INTO authors (
name, bio
) VALUES (
?, ?
)
RETURNING *;
-- name: ListActiveNews :many
SELECT * FROM news
WHERE display = 1
ORDER BY news_date;
-- name: UpdateAuthor :exec
UPDATE authors
set name = ?,
bio = ?
-- name: CreateNewsEntry :exec
INSERT INTO news (
news_title, news_text, news_date,
news_publish, display
) VALUES (?, ?, ?, ?, ?);
-- name: UpdateNewsEntry :exec
UPDATE news
SET
news_title = ?, news_text = ?,
news_date = ?, news_publish = ?, display = ?
WHERE id = ?;
-- name: DeleteAuthor :exec
DELETE FROM authors
-- name: DeleteNewsEntry :exec
DELETE FROM news
WHERE id = ?;
--
-- name: ListRetailers :many
SELECT * FROM retailer
ORDER BY shopname;
-- name: ListActiveRetailers :many
SELECT * FROM retailer
WHERE display = 1
ORDER BY shopname;
-- name: CreateRetailer :exec
INSERT INTO retailer (
shopname, url, country, display
) VALUES (?, ?, ?, ?);
-- name: UpdateRetailer :exec
UPDATE retailer
SET shopname = ?, url = ?, country = ?, display = ?
WHERE id = ?;
-- name: DeleteRetailer :exec
DELETE FROM news
WHERE id = ?;