restructure the API and update the admin script
This commit is contained in:
62
api/database/admin.go
Normal file
62
api/database/admin.go
Normal 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
|
||||
}
|
@ -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
58
api/database/multilang.go
Normal 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
116
api/database/news.go
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
Reference in New Issue
Block a user