migration to chi

This commit is contained in:
2025-03-12 16:30:40 +03:00
parent ae81fbcce9
commit 45bb32e7a6
46 changed files with 1352 additions and 1323 deletions
+44
View File
@@ -0,0 +1,44 @@
package database
type Character struct {
ID int32 `json:"id"`
OriginalName string `db:"original_name" json:"original_name"`
LocalizedName string `db:"localized_name" json:"localized_name"`
Description string `json:"description"`
Image string `json:"image"`
Main bool `json:"main"`
}
func FetchCharacters() ([]*Character, error) {
var characters = make([]*Character, 0)
err := Database.Select(&characters, `select * from characters;`)
return characters, err
}
func FetchCharacter(id int) (*Character, error) {
var char = &Character{}
err := Database.Get(char, `select * from characters where id=$1;`, id)
return char, err
}
func UpdateCharacter(character *Character) (*Character, error) {
rows, err := Database.NamedQuery("update characters set original_name=:original_name, localized_name=:localized_name, description=:description, image=:image where id=:id returning *;", character)
if err != nil {
return nil, err
}
char := new(Character)
if rows.Next() {
err = rows.StructScan(char)
}
return char, err
}
func DeleteCharacter(id int) error {
_, err := Database.Exec("delete from characters where id=$1;", id)
return err
}
func FetchCharacterNovels(id int) ([]Novel, error) {
var novels = make([]Novel, 0)
err := Database.Select(&novels, `select id, original_name, localized_name, coalesce(n.image, '') as image from character_novel join novels n on (n.id=character_novel.novel_id) where character_id=$1;`, id)
return novels, err
}
+21
View File
@@ -0,0 +1,21 @@
package database
type Club struct {
ID uint16 `json:"id"`
Name string `json:"name"`
Avatar string `json:"avatar"`
Owner int `json:"-"`
IsPrivate bool `json:"is_private" db:"is_private"`
}
func GetClubs(userId int) ([]*Club, error) {
clubs := make([]*Club, 0)
err := Database.Select(&clubs, "select clubs.* from clubs join club_member cm on clubs.id = cm.club_id where not is_private or cm.user_id=$1;", userId)
return clubs, err
}
func GetClub(id int, userId uint64) (*Club, error) {
club := new(Club)
err := Database.Get(club, "select clubs.* from clubs join club_member cm on clubs.id = cm.club_id where club_id=$1 and (not is_private or cm.user_id=$2);", id, userId)
return club, err
}
+29
View File
@@ -0,0 +1,29 @@
package database
import (
"fmt"
"github.com/getsentry/sentry-go"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"log"
"os"
)
var Database *sqlx.DB
func ConnectToDB() {
var err error
host := os.Getenv("DB_HOST")
user := os.Getenv("DB_USER")
password := os.Getenv("DB_PASSWORD")
name := os.Getenv("DB_NAME")
url := fmt.Sprintf("host=%s port=5432 user=%s password=%s dbname=%s sslmode=disable", host, user, password, name)
Database, err = sqlx.Open("postgres", url)
if err != nil {
sentry.CaptureException(err)
log.Fatalln(err)
}
log.Printf("Connected to DB with name %v as %v on %v\n", name, user, host)
}
+18
View File
@@ -0,0 +1,18 @@
package database
import "time"
type Game struct {
ID int32 `json:"id"`
OriginalName string `json:"original_name" db:"original_name"`
LocalizedName string `json:"localized_name" db:"localized_name"`
Description string `json:"description"`
Image string `json:"image"`
ReleaseDate time.Time `json:"release_date" db:"release_date"`
}
func FetchGame(id int32) (Game, error) {
var game Game
err := Database.Get(&game, "select * from games where id = $1 limit 1;", id)
return game, err
}
+26
View File
@@ -0,0 +1,26 @@
package database
// TODO
type Genre struct {
ID int16 `json:"id"`
Name string `json:"name"`
LocalizedName string `json:"localized_name" db:"localized_name"`
}
func FetchGenres() ([]*Genre, error) {
var genres = make([]*Genre, 0)
err := Database.Select(&genres, "select * from genres;")
return genres, err
}
func FetchGenre(id int) (*Genre, error) {
var genre = &Genre{}
err := Database.Get(genre, "select * from genres where id = $1;", id)
return genre, err
}
func FetchGenreNovels(id int) ([]*Novel, error) {
var novels = make([]*Novel, 0)
err := Database.Select(&novels, "select novels.* from novel_genre join novels on (novel_genre.novel_id = novels.id) where genre_id = $1;", id)
return novels, err
}
+23
View File
@@ -0,0 +1,23 @@
package database
type Group struct {
ID uint8 `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
MaxAvatarSize int `json:"max_avatar_size" db:"max_ava_size"`
MaxAvaWeight int64 `json:"max_avatar_weight" db:"max_ava_weight"`
IsSuperuser bool `json:"is_superuser" db:"is_superuser"`
Permissions string `json:"permissions"`
}
func GetGroups() ([]*Group, error) {
var groups = make([]*Group, 0)
err := Database.Select(&groups, "select * from groups;")
return groups, err
}
func GetGroup(id int) (*Group, error) {
var group = &Group{}
err := Database.Get(group, "select * from groups where id=$1;", id)
return group, err
}
+51
View File
@@ -0,0 +1,51 @@
package database
import (
"time"
)
type Post struct {
ID uint16 `json:"id"`
Title string `json:"title"`
ShortPost string `json:"short_post" db:"short_post"`
FullPost string `json:"full_post" db:"full_post"`
Date time.Time `json:"date"`
AuthorID uint64 `json:"author_id" db:"author_id" sql:"REFERENCES users(id)"`
Author string `json:"author"`
}
func FetchTotalNews() (int, error) {
count := 0
err := Database.Get(&count, `select count("news") from news;`)
return count, err
}
func FetchNews(offset, limit int) ([]Post, error) {
news := make([]Post, 0)
err := Database.Select(&news, "select news.*, u.username as author from news inner join users u on news.author_id = u.id offset $1 limit $2;", offset, limit)
return news, err
}
func CreatePost(post *Post) (*Post, error) {
destPost := new(Post)
r, err := Database.NamedQuery("insert into news (title, short_post, full_post, author_id) values (:title, :short_post, :full_post, :author_id) returning *;", post)
if err != nil {
return nil, err
}
if r.Next() {
err = r.StructScan(destPost)
}
return destPost, err
}
func FetchPost(id int) (*Post, error) {
post := &Post{}
err := Database.Get(post, "select news.*, u.username as author from news inner join users u on news.author_id = u.id where news.id = $1;", id)
return post, err
}
func UpdatePost(post *Post) error {
_, err := Database.NamedExec("update news set title=:title, short_post=:short_post, full_post=:full_post where id=:id;", post)
return err
}
func DeletePost(id int) error {
_, err := Database.Exec("delete from news where id=$1;", id)
return err
}
+228
View File
@@ -0,0 +1,228 @@
package database
import (
"backend/utils"
"fmt"
"strings"
"time"
)
type Novel struct {
ID int32 `json:"id"`
OriginalName string `db:"original_name" json:"original_name"`
LocalizedName string `db:"localized_name" json:"localized_name"`
Description string `json:"description"`
Image string `json:"image"`
Rating float32 `json:"rating"`
ReleaseDate time.Time `json:"release_date" db:"release_date"`
Duration uint64 `json:"duration"`
ExitStatus string `json:"exit_status" db:"exit_status"`
IsDemo bool `json:"is_demo" db:"is_demo"`
Platforms string `json:"platforms"`
Genres []Genre `json:"genres" ignore:"1"`
Links []NovelLink `json:"links" ignore:"1"`
Walkthrough string `json:"walkthrough"`
RatingAll []RatingAll `json:"rating_all" ignore:"1"`
StatusAll []StatusAll `json:"status_all" ignore:"1"`
IsNSFW bool `json:"is_nsfw" db:"is_nsfw"`
}
type ShortNovel struct {
ID int32 `json:"id"`
OriginalName string `db:"original_name" json:"original_name"`
LocalizedName string `db:"localized_name" json:"localized_name"`
Description string `json:"-"`
Image string `json:"image"`
Rating float32 `json:"rating"`
ReleaseDate time.Time `json:"release_date" db:"release_date"`
Duration uint64 `json:"-"`
ExitStatus string `json:"exit_status" db:"exit_status"`
IsDemo bool `json:"is_demo" db:"is_demo"`
Platforms string `json:"-"`
Genres []Genre `json:"genres" ignore:"1"`
Links []NovelLink `json:"-" ignore:"1"`
Walkthrough string `json:"-"`
RatingAll []RatingAll `json:"rating_all" ignore:"1"`
IsNSFW bool `json:"is_nsfw" db:"is_nsfw"`
}
type CommentNovel struct {
ID int32 `json:"id" db:"id"`
UserID int64 `json:"user_id" db:"user_id" sql:"REFERENCES users(id)"`
NovelID int16 `json:"novel_id" db:"novel_id" sql:"REFERENCES novels(id)"`
Text string `json:"text" db:"text"`
}
type NovelLink struct {
NovelID int32 `json:"novel_id" db:"novel_id"`
Name string `json:"name"`
Link string `json:"link"`
Type string `json:"type"`
}
type RatingAll struct {
Mark int `json:"mark"`
Count int `json:"count"`
}
type StatusAll struct {
Status string `json:"status"`
Count int `json:"count"`
}
func FetchNovels(orderBy string) ([]*ShortNovel, error) {
var novels = make([]*ShortNovel, 0)
err := Database.Select(&novels, fmt.Sprintf("select id, original_name, localized_name, image, rating, release_date from novels order by %v desc;", orderBy))
if err != nil {
return nil, err
}
tx := Database.MustBegin()
for _, novel := range novels {
novel.Genres = make([]Genre, 0)
err = tx.Select(&novel.Genres, "select g.* from novel_genre join genres g on g.id = novel_genre.genre_id where novel_id=$1;", novel.ID)
if err != nil {
return nil, err
}
}
err = tx.Commit()
return novels, err
}
func FetchNovel(id int) (*Novel, error) {
novel := new(Novel)
err := Database.Get(novel, `select * from novels where id = $1;`, id)
if err != nil {
return novel, err
}
novel.Genres = make([]Genre, 0)
err = Database.Select(&novel.Genres, `select genres.* from novel_genre join genres on (novel_genre.genre_id = genres.id) where novel_id = $1`, id)
if err != nil {
return novel, err
}
novel.RatingAll = make([]RatingAll, 0)
err = Database.Select(&novel.RatingAll, `select mark, count(mark) from user_novel where novel_id=$1 and mark > 0 group by mark;`, id)
if err != nil {
return novel, err
}
novel.StatusAll = make([]StatusAll, 0)
err = Database.Select(&novel.StatusAll, `select status, count(status) from user_novel where novel_id=$1 group by status;`, id)
if err != nil {
return novel, err
}
novel.Links = make([]NovelLink, 0)
err = Database.Select(&novel.Links, "select * from novel_link where novel_id=$1;", novel.ID)
return novel, err
}
func UpdateNovel(novel *Novel) (*Novel, error) {
var newNovel = new(Novel)
setPart := ""
setPartList := make([]string, 0)
for _, i := range utils.GetStructAndNamedForDatabase(newNovel) {
setPartList = append(setPartList, i[0]+"="+i[1])
}
setPart = strings.Join(setPartList, ",")
rows, err := Database.NamedQuery(fmt.Sprintf(`update novels set %s where id=:id returning *;`, setPart), novel)
if err != nil {
return newNovel, err
}
err = UpdateNovelGenres(int(novel.ID), novel.Genres)
if err != nil {
return newNovel, err
}
rows.Next()
err = rows.StructScan(&newNovel)
if err != nil {
return newNovel, err
}
genres, err := FetchNovelGenres(int(novel.ID))
newNovel.Genres = genres
return newNovel, err
}
func UpdateNovelGenres(novelId int, genres []Genre) error {
_, err := Database.Exec("delete from novel_genre where novel_id=$1;", novelId)
if err != nil {
return err
}
tx, err := Database.Begin()
if err != nil {
return err
}
for _, genre := range genres {
_, err = tx.Exec("insert into novel_genre values ($1, $2);", genre.ID, novelId)
if err != nil {
return err
}
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func UpdateNovelCover(novelId int, url string) error {
_, err := Database.Exec("update novels set image=$1 where id=$2;", url, novelId)
return err
}
func CreateNovel(novelData *Novel) (Novel, error) {
var novel Novel
rows, err := Database.NamedQuery(`insert into novels (original_name, localized_name, image, description, is_demo) values (:original_name, :localized_name, :image, :description, :is_demo) returning *;`, novelData)
if rows != nil && rows.Next() {
_ = rows.StructScan(&novel)
}
return novel, err
}
func DeleteNovel(id int) error {
_, err := Database.Exec("delete from novels where id = $1;", id)
return err
}
func SearchNovels(query string) ([]ShortNovel, error) {
novels := make([]ShortNovel, 0)
err := Database.Select(&novels, "select * from search_novel($1) order by rating desc;", query)
if err != nil {
return novels, err
}
tx := Database.MustBegin()
for _, novel := range novels {
novel.Genres = make([]Genre, 0)
err = tx.Select(&novel.Genres, "select g.* from novel_genre join genres g on g.id = novel_genre.genre_id where novel_id=$1;", novel.ID)
if err != nil {
return nil, err
}
}
err = tx.Commit()
return novels, err
}
func FetchNovelComments(id int) ([]CommentNovel, error) {
var comments = make([]CommentNovel, 0)
err := Database.Select(&comments, `select * from novel_comment where novel_id = $1;`, id)
return comments, err
}
func AddNovelComment(commentData CommentNovel) (CommentNovel, error) {
var comment CommentNovel
rows, err := Database.NamedQuery(`insert into novel_comment(user_id, novel_id, text) values (:user_id, :novel_id, :text) returning *;`, commentData)
if rows != nil && rows.Next() {
_ = rows.StructScan(&comment)
}
return comment, err
}
func FetchNovelCharacters(id int) ([]Character, error) {
var characters = make([]Character, 0)
err := Database.Select(&characters, `select characters.*, character_novel.main from character_novel join characters on (characters.id=character_novel.character_id) where novel_id=$1;`, id)
return characters, err
}
func FetchNovelGenres(id int) ([]Genre, error) {
var genres = make([]Genre, 0)
err := Database.Select(&genres, `select genres.* from novel_genre join genres on (novel_genre.genre_id = genres.id) where novel_id = $1`, id)
return genres, err
}
+20
View File
@@ -0,0 +1,20 @@
package database
type App struct {
ID int `json:"id"`
Name string `json:"name"`
Secret string `json:"-"`
IsVerified bool `json:"is_verified" db:"is_verified"`
}
func FetchApp(id int) (*App, error) {
var app = &App{}
err := Database.Get(app, "select * from apps where id=$1;", id)
return app, err
}
func FetchApps() ([]*App, error) {
var apps = make([]*App, 0)
err := Database.Select(&apps, "select * from apps;")
return apps, err
}
+166
View File
@@ -0,0 +1,166 @@
package database
import (
"backend/utils"
"fmt"
"time"
)
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Password string `json:"-"`
Email string `json:"-"`
RegistrationDate time.Time `json:"reg_date" db:"reg_date"`
LastSeen time.Time `json:"last_seen" db:"last_seen"`
Avatar string `json:"avatar"`
IsSuperuser bool `json:"is_superuser" db:"is_superuser"`
IsActivated bool `json:"is_activated" db:"is_activated"`
GroupID int8 `json:"-" db:"group_id"`
Group Group `json:"group"`
Cover string `json:"cover"`
UseGravatar bool `json:"use_gravatar" db:"use_gravatar"`
Theme string `json:"theme"`
IsBanned bool `json:"is_banned" db:"is_banned"`
}
type UserSettings struct {
UseGravatar bool `json:"use_gravatar" db:"use_gravatar"`
Avatar string `json:"avatar"`
Theme string `json:"theme"`
}
type UserNovels struct {
UserID int64 `json:"user_id" db:"user_id" sql:"REFERENCES users(id)"`
NovelID int16 `json:"novel_id" db:"novel_id" sql:"REFERENCES novels(id)"`
Mark int8 `json:"mark"`
Status string `json:"status"`
}
type UserNovelWithInfo struct {
UserNovels
Novel
}
var (
ErrNotAuthorized = fmt.Errorf("%s", "not authorized")
)
func FetchUsers() ([]*User, error) {
var users = make([]*User, 0)
err := Database.Select(&users, "select * from users limit 20;")
for _, user := range users {
if user.UseGravatar {
user.Avatar = GetUserGravatar(user.Email)
}
err = Database.Get(&user.Group, `select * from groups where id = $1;`, user.GroupID)
}
return users, err
}
func FetchUser(id int) (*User, error) {
user := new(User)
err := Database.Get(user, `select users.* from users where users.id = $1;`, id)
if err == nil {
if user.UseGravatar {
user.Avatar = GetUserGravatar(user.Email)
}
err = Database.Get(&user.Group, `select * from groups where id = $1;`, user.GroupID)
}
return user, err
}
func UpdateUser(user *User) (*User, error) {
var u = &User{}
rows, err := Database.NamedQuery(`update users set avatar=:avatar, username=:username, is_activated=:is_activated, use_gravatar=:use_gravatar where id=:id returning *;`, user)
if rows != nil && rows.Next() {
_ = rows.StructScan(u)
if u.UseGravatar {
u.Avatar = GetUserGravatar(u.Email)
}
}
return u, err
}
func UpdatePassword(id int, password string) error {
_, err := Database.Exec("update users set password=$1 where id=$2;", password, id)
return err
}
func FetchUserByLogin(login string) (*User, error) {
user := new(User)
err := Database.Get(user, `select * from users where username = $1 or email = $1;`, login)
if err == nil {
if user.UseGravatar {
user.Avatar = GetUserGravatar(user.Email)
}
err = Database.Get(&user.Group, `select * from groups where id = $1;`, user.GroupID)
}
return user, err
}
func FetchUserNovels(id int) ([]*UserNovelWithInfo, error) {
var novels = make([]*UserNovelWithInfo, 0)
err := Database.Select(&novels, `select novels.*, user_novel.* from user_novel inner join novels on (novels.id = user_novel.novel_id) where user_id = $1;`, id)
return novels, err
}
func CreateUserNovel(novelData *UserNovels) (*UserNovels, error) {
var novel = &UserNovels{}
rows, err := Database.NamedQuery(`insert into user_novel(user_id, novel_id, mark, status) VALUES (:user_id, :novel_id, :mark, :status) returning *;`, novelData)
if rows != nil && rows.Next() {
_ = rows.StructScan(novel)
}
return novel, err
}
func FetchUserNovel(userId int, novelId int) (*UserNovels, error) {
var novel = &UserNovels{}
err := Database.Get(novel, `select user_novel.* from user_novel where user_id = $1 and novel_id = $2;`, userId, novelId)
return novel, err
}
func UpdateUserNovel(novel *UserNovels) (*UserNovels, error) {
var updatedNovel = &UserNovels{}
rows, err := Database.NamedQuery(`update user_novel set mark=:mark, status=:status where user_id=:user_id and novel_id=:novel_id returning *;`, novel)
_, _ = Database.Exec(`update novels set rating=avg_novel($1) where id=$1;`, novel.NovelID)
if rows != nil && rows.Next() {
_ = rows.StructScan(updatedNovel)
}
return updatedNovel, err
}
func DeleteUserNovel(userId int, novelId int) error {
_, err := Database.Exec("delete from user_novel where user_id = $1 and novel_id = $2;", userId, novelId)
return err
}
func FetchUserNovelsComments(id int) ([]CommentNovel, error) {
var comments = make([]CommentNovel, 0)
err := Database.Select(&comments, `select * from novel_comment where user_id = $1;`, id)
return comments, err
}
func CreateUser(username string, password string, email string) (*User, error) {
var user = &User{}
var currentTime = time.Now()
err := Database.Get(user, "insert into users(username, password, email, reg_date, last_seen) VALUES ($1, $2, $3, $4, $4) returning *;", username, password, email, currentTime)
return user, err
}
func ActivateUser(id int) (*User, error) {
var user = &User{}
err := Database.Get(user, `update users set is_activated=true where id=$1 returning *;`, id)
return user, err
}
func GetUserGravatar(email string) string {
return fmt.Sprintf("https://www.gravatar.com/avatar/%s", utils.MD5(email))
}
// Settings
func FetchUserSettings(id uint64) (*UserSettings, error) {
settings := new(UserSettings)
err := Database.Get(settings, "select use_gravatar, avatar, theme from users where id=$1;", id)
return settings, err
}
func UpdateUserSettings(id uint64, settings *UserSettings) (*User, error) {
user := new(User)
err := Database.Get(user, "update users set use_gravatar=$2, avatar=$3, theme=$4 where id=$1 returning *;", id, settings.UseGravatar, settings.Avatar, settings.Theme)
return user, err
}