restructure the API and update the admin script

This commit is contained in:
ngn
2025-01-04 00:00:10 +03:00
parent 03586da8df
commit 26e8909998
34 changed files with 1699 additions and 983 deletions

62
api/database/admin.go Normal file
View File

@ -0,0 +1,62 @@
package database
import (
"database/sql"
"fmt"
"github.com/ngn13/website/api/util"
)
type AdminLog struct {
Action string `json:"action"` // action that was performed (service removal, service addition etc.)
Time int64 `json:"time"` // time when the action was performed
}
func (l *AdminLog) Scan(rows *sql.Rows) (err error) {
if rows != nil {
return rows.Scan(&l.Action, &l.Time)
}
return fmt.Errorf("no row/rows specified")
}
func (db *Type) AdminLogNext(l *AdminLog) bool {
var err error
if nil == db.rows {
if db.rows, err = db.sql.Query("SELECT * FROM admin_log"); err != nil {
util.Fail("failed to query admin_log table: %s", err.Error())
goto fail
}
}
if !db.rows.Next() {
goto fail
}
if err = l.Scan(db.rows); err != nil {
util.Fail("failed to scan the admin_log table: %s", err.Error())
goto fail
}
return true
fail:
if db.rows != nil {
db.rows.Close()
}
db.rows = nil
return false
}
func (db *Type) AdminLogAdd(l *AdminLog) error {
_, err := db.sql.Exec(
`INSERT INTO admin_log(
action, time
) values(?, ?)`,
&l.Action, &l.Time,
)
return err
}

View File

@ -1,44 +1,66 @@
package database
import (
"fmt"
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
func Setup(db *sql.DB) error {
_, err := db.Exec(`
CREATE TABLE IF NOT EXISTS posts(
id TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
author TEXT NOT NULL,
date TEXT NOT NULL,
content TEXT NOT NULL,
public INTEGER NOT NULL,
vote INTEGER NOT NULL
);
`)
if err != nil {
return err
}
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS services(
name TEXT NOT NULL UNIQUE,
desc TEXT NOT NULL,
url TEXT NOT NULL
);
`)
if err != nil {
return err
}
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS votes(
hash TEXT NOT NULL UNIQUE,
is_upvote INTEGER NOT NULL
);
`)
return err
type Type struct {
sql *sql.DB
rows *sql.Rows
}
func (db *Type) Load() (err error) {
if db.sql, err = sql.Open("sqlite3", "data.db"); err != nil {
return fmt.Errorf("cannot access the database: %s", err.Error())
}
// see database/service.go
_, err = db.sql.Exec(`
CREATE TABLE IF NOT EXISTS services(
name TEXT NOT NULL UNIQUE,
desc TEXT NOT NULL,
check_time INTEGER NOT NULL,
check_res INTEGER NOT NULL,
check_url TEXT NOT NULL,
clear TEXT,
onion TEXT,
i2p TEXT
);
`)
if err != nil {
return fmt.Errorf("failed to create the services table: %s", err.Error())
}
// see database/news.go
_, err = db.sql.Exec(`
CREATE TABLE IF NOT EXISTS news(
id TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
author TEXT NOT NULL,
time INTEGER NOT NULL,
content TEXT NOT NULL
);
`)
if err != nil {
return fmt.Errorf("failed to create the news table: %s", err.Error())
}
// see database/admin.go
_, err = db.sql.Exec(`
CREATE TABLE IF NOT EXISTS admin_log(
action TEXT NOT NULL,
time INTEGER NOT NULL
);
`)
if err != nil {
return fmt.Errorf("failed to create the admin_log table: %s", err.Error())
}
return nil
}

58
api/database/multilang.go Normal file
View File

@ -0,0 +1,58 @@
package database
import (
"encoding/json"
"reflect"
"strings"
"unicode"
)
type Multilang struct {
En string `json:"en"` // english
Tr string `json:"tr"` // turkish
}
func (ml *Multilang) Supports(lang string) bool {
ml_ref := reflect.ValueOf(ml).Elem()
for i := 0; i < reflect.Indirect(ml_ref).NumField(); i++ {
if name := reflect.Indirect(ml_ref).Field(i).Type().Name(); strings.ToLower(name) == lang {
return true
}
}
return false
}
func (ml *Multilang) Get(lang string) string {
r := []rune(lang)
r[0] = unicode.ToUpper(r[0])
l := string(r)
ml_ref := reflect.ValueOf(ml)
return reflect.Indirect(ml_ref).FieldByName(l).String()
}
func (ml *Multilang) Empty() bool {
ml_ref := reflect.ValueOf(ml)
for i := 0; i < reflect.Indirect(ml_ref).NumField(); i++ {
if field := reflect.Indirect(ml_ref).Field(i); field.String() != "" {
return false
}
}
return true
}
func (ml *Multilang) Dump() (string, error) {
if data, err := json.Marshal(ml); err != nil {
return "", err
} else {
return string(data), nil
}
}
func (ml *Multilang) Load(s string) error {
return json.Unmarshal([]byte(s), ml)
}

116
api/database/news.go Normal file
View File

@ -0,0 +1,116 @@
package database
import (
"database/sql"
"github.com/ngn13/website/api/util"
)
type News struct {
ID string `json:"id"` // ID of the news
title string `json:"-"` // title of the news (string)
Title Multilang `json:"title"` // title of the news
Author string `json:"author"` // author of the news
Time uint64 `json:"time"` // when the new was published
content string `json:"-"` // content of the news (string)
Content Multilang `json:"content"` // content of the news
}
func (n *News) Supports(lang string) bool {
return n.Content.Supports(lang) && n.Title.Supports(lang)
}
func (n *News) Load() (err error) {
if err = n.Title.Load(n.title); err != nil {
return err
}
if err = n.Content.Load(n.content); err != nil {
return err
}
return nil
}
func (n *News) Dump() (err error) {
if n.title, err = n.Title.Dump(); err != nil {
return err
}
if n.content, err = n.Content.Dump(); err != nil {
return err
}
return nil
}
func (n *News) Scan(rows *sql.Rows) (err error) {
err = rows.Scan(
&n.ID, &n.title, &n.Author,
&n.Time, &n.content)
if err != nil {
return err
}
return n.Load()
}
func (n *News) IsValid() bool {
return n.Author != "" && n.ID != "" && !n.Title.Empty() && !n.Content.Empty()
}
func (db *Type) NewsNext(n *News) bool {
var err error
if nil == db.rows {
if db.rows, err = db.sql.Query("SELECT * FROM news"); err != nil {
util.Fail("failed to query news table: %s", err.Error())
goto fail
}
}
if !db.rows.Next() {
goto fail
}
if err = n.Scan(db.rows); err != nil {
util.Fail("failed to scan the news table: %s", err.Error())
goto fail
}
return true
fail:
if db.rows != nil {
db.rows.Close()
}
db.rows = nil
return false
}
func (db *Type) NewsRemove(id string) error {
_, err := db.sql.Exec(
"DELETE FROM news WHERE id = ?",
id,
)
return err
}
func (db *Type) NewsAdd(n *News) (err error) {
if err = n.Dump(); err != nil {
return err
}
_, err = db.sql.Exec(
`INSERT OR REPLACE INTO news(
id, title, author, time, content
) values(?, ?, ?, ?, ?)`,
n.ID, n.title,
n.Author, n.Time, n.content,
)
return err
}

View File

@ -1,71 +0,0 @@
package database
import (
"database/sql"
"github.com/ngn13/website/api/util"
)
type Post struct {
ID string `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
Date string `json:"date"`
Content string `json:"content"`
Public int `json:"public"`
Vote int `json:"vote"`
}
func (p *Post) Load(rows *sql.Rows) error {
return rows.Scan(&p.ID, &p.Title, &p.Author, &p.Date, &p.Content, &p.Public, &p.Vote)
}
func (p *Post) Get(db *sql.DB, id string) (bool, error) {
var (
success bool
rows *sql.Rows
err error
)
if rows, err = db.Query("SELECT * FROM posts WHERE id = ?", id); err != nil {
return false, err
}
defer rows.Close()
if success = rows.Next(); !success {
return false, nil
}
if err = p.Load(rows); err != nil {
return false, err
}
return true, nil
}
func (p *Post) Remove(db *sql.DB) error {
_, err := db.Exec("DELETE FROM posts WHERE id = ?", p.ID)
return err
}
func (p *Post) Save(db *sql.DB) error {
p.ID = util.TitleToID(p.Title)
_, err := db.Exec(
"INSERT INTO posts(id, title, author, date, content, public, vote) values(?, ?, ?, ?, ?, ?, ?)",
p.ID, p.Title, p.Author, p.Date, p.Content, p.Public, p.Vote,
)
return err
}
func (p *Post) Update(db *sql.DB) error {
p.ID = util.TitleToID(p.Title)
_, err := db.Exec(
"UPDATE posts SET title = ?, author = ?, date = ?, content = ?, public = ?, vote = ? WHERE id = ?",
p.Title, p.Author, p.Date, p.Content, p.Public, p.Vote, p.ID,
)
return err
}

View File

@ -2,54 +2,127 @@ package database
import (
"database/sql"
"fmt"
"github.com/ngn13/website/api/util"
)
type Service struct {
Name string `json:"name"`
Desc string `json:"desc"`
Url string `json:"url"`
Name string `json:"name"` // name of the service
desc string `json:"-"` // description of the service (string)
Desc Multilang `json:"desc"` // description of the service
CheckTime uint64 `json:"check_time"` // last status check time
CheckRes uint8 `json:"check_res"` // result of the status check
CheckURL string `json:"check_url"` // URL used for status check
Clear string `json:"clear"` // Clearnet (cringe) URL for the service
Onion string `json:"onion"` // Onion (TOR) URL for the service
I2P string `json:"i2p"` // I2P URL for the service
}
func (s *Service) Load(rows *sql.Rows) error {
return rows.Scan(&s.Name, &s.Desc, &s.Url)
func (s *Service) Load() error {
return s.Desc.Load(s.desc)
}
func (s *Service) Get(db *sql.DB, name string) (bool, error) {
func (s *Service) Dump() (err error) {
s.desc, err = s.Desc.Dump()
return
}
func (s *Service) Scan(rows *sql.Rows, row *sql.Row) (err error) {
if rows != nil {
err = rows.Scan(
&s.Name, &s.desc,
&s.CheckTime, &s.CheckRes, &s.CheckURL,
&s.Clear, &s.Onion, &s.I2P)
} else if row != nil {
err = row.Scan(
&s.Name, &s.desc,
&s.CheckTime, &s.CheckRes, &s.CheckURL,
&s.Clear, &s.Onion, &s.I2P)
} else {
return fmt.Errorf("no row/rows specified")
}
if err != nil {
return err
}
return s.Load()
}
func (s *Service) IsValid() bool {
return s.Name != "" && (s.Clear != "" || s.Onion != "" || s.I2P != "") && !s.Desc.Empty()
}
func (db *Type) ServiceNext(s *Service) bool {
var err error
if nil == db.rows {
if db.rows, err = db.sql.Query("SELECT * FROM services"); err != nil {
util.Fail("failed to query services table: %s", err.Error())
goto fail
}
}
if !db.rows.Next() {
goto fail
}
if err = s.Scan(db.rows, nil); err != nil {
util.Fail("failed to scan the services table: %s", err.Error())
goto fail
}
return true
fail:
if db.rows != nil {
db.rows.Close()
}
db.rows = nil
return false
}
func (db *Type) ServiceFind(name string) (*Service, error) {
var (
success bool
rows *sql.Rows
err error
row *sql.Row
s Service
err error
)
if rows, err = db.Query("SELECT * FROM services WHERE name = ?", name); err != nil {
return false, err
}
defer rows.Close()
if success = rows.Next(); !success {
return false, nil
if row = db.sql.QueryRow("SELECT * FROM services WHERE name = ?", name); row == nil || row.Err() == sql.ErrNoRows {
return nil, nil
}
if err = s.Load(rows); err != nil {
return false, err
if err = s.Scan(nil, row); err != nil {
return nil, err
}
return true, nil
return &s, nil
}
func (s *Service) Remove(db *sql.DB) error {
_, err := db.Exec(
func (db *Type) ServiceRemove(name string) error {
_, err := db.sql.Exec(
"DELETE FROM services WHERE name = ?",
s.Name,
name,
)
return err
}
func (s *Service) Save(db *sql.DB) error {
_, err := db.Exec(
"INSERT INTO services(name, desc, url) values(?, ?, ?)",
s.Name, s.Desc, s.Url,
func (db *Type) ServiceUpdate(s *Service) (err error) {
if err = s.Dump(); err != nil {
return err
}
_, err = db.sql.Exec(
`INSERT OR REPLACE INTO services(
name, desc, check_time, check_res, check_url, clear, onion, i2p
) values(?, ?, ?, ?, ?, ?, ?, ?)`,
s.Name, s.desc,
s.CheckTime, s.CheckRes, s.CheckURL,
s.Clear, s.Onion, s.I2P,
)
return err

View File

@ -1,49 +0,0 @@
package database
import "database/sql"
type Vote struct {
Hash string
IsUpvote bool
}
func (v *Vote) Load(rows *sql.Rows) error {
return rows.Scan(&v.Hash, &v.IsUpvote)
}
func (v *Vote) Get(db *sql.DB, hash string) (bool, error) {
var (
success bool
rows *sql.Rows
err error
)
if rows, err = db.Query("SELECT * FROM votes WHERE hash = ?", hash); err != nil {
return false, err
}
defer rows.Close()
if success = rows.Next(); !success {
return false, nil
}
if err = v.Load(rows); err != nil {
return false, err
}
return true, nil
}
func (v *Vote) Update(db *sql.DB) error {
_, err := db.Exec("UPDATE votes SET is_upvote = ? WHERE hash = ?", v.IsUpvote, v.Hash)
return err
}
func (v *Vote) Save(db *sql.DB) error {
_, err := db.Exec(
"INSERT INTO votes(hash, is_upvote) values(?, ?)",
v.Hash, v.IsUpvote,
)
return err
}