add projects and metrics routes

This commit is contained in:
ngn
2025-01-09 00:30:59 +03:00
parent dee3ef4d85
commit ac307de76c
32 changed files with 781 additions and 492 deletions

View File

@ -24,8 +24,8 @@ 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())
if db.rows, err = db.sql.Query("SELECT * FROM " + TABLE_ADMIN_LOG); err != nil {
util.Fail("failed to query table: %s", err.Error())
goto fail
}
}
@ -35,7 +35,7 @@ func (db *Type) AdminLogNext(l *AdminLog) bool {
}
if err = l.Scan(db.rows); err != nil {
util.Fail("failed to scan the admin_log table: %s", err.Error())
util.Fail("failed to scan the table: %s", err.Error())
goto fail
}
@ -52,7 +52,7 @@ fail:
func (db *Type) AdminLogAdd(l *AdminLog) error {
_, err := db.sql.Exec(
`INSERT INTO admin_log(
"INSERT INTO "+TABLE_ADMIN_LOG+`(
action, time
) values(?, ?)`,
&l.Action, &l.Time,

View File

@ -2,76 +2,62 @@ package database
import (
"fmt"
"os"
"path"
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
const (
SQL_PATH = "sql"
TABLE_ADMIN_LOG = "admin_log" // stores administrator logs
TABLE_METRICS = "metrics" // stores API usage metrcis
TABLE_NEWS = "news" // stores news posts
TABLE_SERVICES = "services" // stores services
TABLE_PROJECTS = "projects" // stores projects
)
var tables []string = []string{
TABLE_ADMIN_LOG, TABLE_METRICS, TABLE_NEWS,
TABLE_SERVICES, TABLE_PROJECTS,
}
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())
func (db *Type) create_table(table string) error {
var (
err error
query []byte
)
query_path := path.Join(SQL_PATH, table+".sql")
if query, err = os.ReadFile(query_path); err != nil {
return fmt.Errorf("failed to read %s for table %s: %", query_path, table, err.Error())
}
// see database/visitor.go
_, err = db.sql.Exec(`
CREATE TABLE IF NOT EXISTS visitor_count(
id TEXT NOT NULL UNIQUE,
count INTEGER NOT NULL
);
`)
if err != nil {
return fmt.Errorf("failed to create the visitor_count table: %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())
if _, err = db.sql.Exec(string(query)); err != nil {
return fmt.Errorf("failed to create the %s table: %s", table, err.Error())
}
return nil
}
func (db *Type) Load() (err error) {
if db.sql, err = sql.Open("sqlite3", "data.db"); err != nil {
return fmt.Errorf("failed access the database: %s", err.Error())
}
for _, table := range tables {
if err = db.create_table(table); err != nil {
return err
}
}
return nil

57
api/database/metrics.go Normal file
View File

@ -0,0 +1,57 @@
package database
import (
"database/sql"
"github.com/ngn13/website/api/util"
)
func (db *Type) MetricsGet(key string) (uint64, error) {
var (
row *sql.Row
count uint64
err error
)
if row = db.sql.QueryRow("SELECT value FROM "+TABLE_METRICS+" WHERE key = ?", key); row == nil {
return 0, nil
}
if err = row.Scan(&count); err != nil && err != sql.ErrNoRows {
util.Fail("failed to scan the table: %s", err.Error())
return 0, err
}
if err == sql.ErrNoRows {
return 0, nil
}
return count, nil
}
func (db *Type) MetricsSet(key string, value uint64) error {
var (
err error
res sql.Result
)
if res, err = db.sql.Exec("UPDATE "+TABLE_METRICS+" SET value = ? WHERE key = ?", value, key); err != nil && err != sql.ErrNoRows {
util.Fail("failed to query table: %s", err.Error())
return err
}
if effected, err := res.RowsAffected(); err != nil {
return err
} else if effected < 1 {
_, err = db.sql.Exec(
`INSERT INTO "+TABLE_METRICS+"(
key, value
) values(?, ?)`,
key, value,
)
return err
}
return nil
}

View File

@ -64,8 +64,8 @@ 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())
if db.rows, err = db.sql.Query("SELECT * FROM " + TABLE_NEWS); err != nil {
util.Fail("failed to query table: %s", err.Error())
goto fail
}
}
@ -75,7 +75,7 @@ func (db *Type) NewsNext(n *News) bool {
}
if err = n.Scan(db.rows); err != nil {
util.Fail("failed to scan the news table: %s", err.Error())
util.Fail("failed to scan the table: %s", err.Error())
goto fail
}
@ -92,7 +92,7 @@ fail:
func (db *Type) NewsRemove(id string) error {
_, err := db.sql.Exec(
"DELETE FROM news WHERE id = ?",
"DELETE FROM "+TABLE_NEWS+" WHERE id = ?",
id,
)
@ -105,7 +105,7 @@ func (db *Type) NewsAdd(n *News) (err error) {
}
_, err = db.sql.Exec(
`INSERT OR REPLACE INTO news(
"INSERT OR REPLACE INTO "+TABLE_NEWS+`(
id, title, author, time, content
) values(?, ?, ?, ?, ?)`,
n.ID, n.title,

92
api/database/project.go Normal file
View File

@ -0,0 +1,92 @@
package database
import (
"database/sql"
"github.com/ngn13/website/api/util"
)
type Project struct {
Name string `json:"name"` // name of the project
desc string `json:"-"` // description of the project (string)
Desc Multilang `json:"desc"` // description of the project
URL string `json:"url"` // URL of the project's homepage/source
License string `json:"license"` // name of project's license
}
func (p *Project) Load() error {
return p.Desc.Load(p.desc)
}
func (p *Project) Dump() (err error) {
p.desc, err = p.Desc.Dump()
return
}
func (p *Project) Scan(rows *sql.Rows) (err error) {
if err = rows.Scan(
&p.Name, &p.desc,
&p.URL, &p.License); err != nil {
return err
}
return p.Load()
}
func (p *Project) IsValid() bool {
return p.Name != "" && p.URL != "" && !p.Desc.Empty()
}
func (db *Type) ProjectNext(p *Project) bool {
var err error
if nil == db.rows {
if db.rows, err = db.sql.Query("SELECT * FROM " + TABLE_PROJECTS); err != nil {
util.Fail("failed to query table: %s", err.Error())
goto fail
}
}
if !db.rows.Next() {
goto fail
}
if err = p.Scan(db.rows); err != nil {
util.Fail("failed to scan the table: %s", err.Error())
goto fail
}
return true
fail:
if db.rows != nil {
db.rows.Close()
}
db.rows = nil
return false
}
func (db *Type) ProjectRemove(name string) error {
_, err := db.sql.Exec(
"DELETE FROM "+TABLE_PROJECTS+" WHERE name = ?",
name,
)
return err
}
func (db *Type) ProjectAdd(p *Project) (err error) {
if err = p.Dump(); err != nil {
return err
}
_, err = db.sql.Exec(
"INSERT OR REPLACE INTO "+TABLE_PROJECTS+`(
name, desc, url, license
) values(?, ?, ?, ?)`,
p.Name, p.desc, p.URL, p.License,
)
return err
}

View File

@ -58,8 +58,8 @@ 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())
if db.rows, err = db.sql.Query("SELECT * FROM " + TABLE_SERVICES); err != nil {
util.Fail("failed to query table: %s", err.Error())
goto fail
}
}
@ -69,7 +69,7 @@ func (db *Type) ServiceNext(s *Service) bool {
}
if err = s.Scan(db.rows, nil); err != nil {
util.Fail("failed to scan the services table: %s", err.Error())
util.Fail("failed to scan the table: %s", err.Error())
goto fail
}
@ -91,7 +91,7 @@ func (db *Type) ServiceFind(name string) (*Service, error) {
err error
)
if row = db.sql.QueryRow("SELECT * FROM services WHERE name = ?", name); row == nil || row.Err() == sql.ErrNoRows {
if row = db.sql.QueryRow("SELECT * FROM "+TABLE_SERVICES+" WHERE name = ?", name); row == nil || row.Err() == sql.ErrNoRows {
return nil, nil
}
@ -104,7 +104,7 @@ func (db *Type) ServiceFind(name string) (*Service, error) {
func (db *Type) ServiceRemove(name string) error {
_, err := db.sql.Exec(
"DELETE FROM services WHERE name = ?",
"DELETE FROM "+TABLE_SERVICES+" WHERE name = ?",
name,
)
@ -117,7 +117,7 @@ func (db *Type) ServiceUpdate(s *Service) (err error) {
}
_, err = db.sql.Exec(
`INSERT OR REPLACE INTO services(
"INSERT OR REPLACE INTO "+TABLE_SERVICES+`(
name, desc, check_time, check_res, check_url, clear, onion, i2p
) values(?, ?, ?, ?, ?, ?, ?, ?)`,
s.Name, s.desc,

View File

@ -1,48 +0,0 @@
package database
import (
"database/sql"
)
func (db *Type) VisitorGet() (uint64, error) {
var (
row *sql.Row
count uint64
err error
)
if row = db.sql.QueryRow("SELECT count FROM visitor_count WHERE id = 0"); row == nil {
return 0, nil
}
if err = row.Scan(&count); err != nil && err != sql.ErrNoRows {
return 0, err
}
if err == sql.ErrNoRows {
return 0, nil
}
return count, nil
}
func (db *Type) VisitorIncrement() (err error) {
if _, err = db.sql.Exec("UPDATE visitor_count SET count = count + 1 WHERE id = 0"); err != nil && err != sql.ErrNoRows {
return err
}
// TODO: err is always nil even if there is no rows for some reason, check sql.Result instead
if err == sql.ErrNoRows {
_, err = db.sql.Exec(
`INSERT INTO visitor_count(
id, count
) values(?, ?)`,
0, 0,
)
return err
}
return nil
}