No One Can Stop Me Now

This commit is contained in:
DigiJ
2026-03-13 23:48:47 -07:00
parent 4d3570781e
commit 1a138a2bd0
428 changed files with 519668 additions and 259 deletions

View File

@@ -0,0 +1,46 @@
package db
import "time"
type Backup struct {
ID int64 `json:"id"`
SiteID *int64 `json:"site_id"`
BackupType string `json:"backup_type"`
FilePath string `json:"file_path"`
SizeBytes int64 `json:"size_bytes"`
CreatedAt time.Time `json:"created_at"`
}
func (d *DB) CreateBackup(siteID *int64, backupType, filePath string, sizeBytes int64) (int64, error) {
result, err := d.conn.Exec(`INSERT INTO backups (site_id, backup_type, file_path, size_bytes)
VALUES (?, ?, ?, ?)`, siteID, backupType, filePath, sizeBytes)
if err != nil {
return 0, err
}
return result.LastInsertId()
}
func (d *DB) ListBackups() ([]Backup, error) {
rows, err := d.conn.Query(`SELECT id, site_id, backup_type, file_path, size_bytes, created_at
FROM backups ORDER BY id DESC`)
if err != nil {
return nil, err
}
defer rows.Close()
var backups []Backup
for rows.Next() {
var b Backup
if err := rows.Scan(&b.ID, &b.SiteID, &b.BackupType, &b.FilePath,
&b.SizeBytes, &b.CreatedAt); err != nil {
return nil, err
}
backups = append(backups, b)
}
return backups, rows.Err()
}
func (d *DB) DeleteBackup(id int64) error {
_, err := d.conn.Exec(`DELETE FROM backups WHERE id=?`, id)
return err
}

View File

@@ -0,0 +1,163 @@
package db
import (
"database/sql"
"fmt"
"os"
"path/filepath"
_ "modernc.org/sqlite"
)
type DB struct {
conn *sql.DB
}
func Open(path string) (*DB, error) {
// Ensure directory exists
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return nil, fmt.Errorf("create db dir: %w", err)
}
conn, err := sql.Open("sqlite", path+"?_journal_mode=WAL&_busy_timeout=5000")
if err != nil {
return nil, fmt.Errorf("open database: %w", err)
}
conn.SetMaxOpenConns(1) // SQLite single-writer
db := &DB{conn: conn}
if err := db.migrate(); err != nil {
conn.Close()
return nil, fmt.Errorf("migrate: %w", err)
}
return db, nil
}
func (d *DB) Close() error {
return d.conn.Close()
}
func (d *DB) Conn() *sql.DB {
return d.conn
}
func (d *DB) migrate() error {
migrations := []string{
migrateSites,
migrateSystemUsers,
migrateManagerUsers,
migrateDeployments,
migrateCronJobs,
migrateFirewallRules,
migrateFloatSessions,
migrateBackups,
migrateAuditLog,
}
for _, m := range migrations {
if _, err := d.conn.Exec(m); err != nil {
return err
}
}
return nil
}
const migrateSites = `CREATE TABLE IF NOT EXISTS sites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT NOT NULL UNIQUE,
aliases TEXT DEFAULT '',
app_type TEXT NOT NULL DEFAULT 'static',
app_root TEXT NOT NULL,
app_port INTEGER DEFAULT 0,
app_entry TEXT DEFAULT '',
git_repo TEXT DEFAULT '',
git_branch TEXT DEFAULT 'main',
ssl_enabled BOOLEAN DEFAULT FALSE,
ssl_cert_path TEXT DEFAULT '',
ssl_key_path TEXT DEFAULT '',
ssl_auto BOOLEAN DEFAULT TRUE,
enabled BOOLEAN DEFAULT TRUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`
const migrateSystemUsers = `CREATE TABLE IF NOT EXISTS system_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
uid INTEGER,
home_dir TEXT,
shell TEXT DEFAULT '/bin/bash',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`
const migrateManagerUsers = `CREATE TABLE IF NOT EXISTS manager_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT DEFAULT 'admin',
force_change BOOLEAN DEFAULT FALSE,
last_login DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`
const migrateDeployments = `CREATE TABLE IF NOT EXISTS deployments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER REFERENCES sites(id),
action TEXT NOT NULL,
status TEXT DEFAULT 'pending',
output TEXT DEFAULT '',
started_at DATETIME,
finished_at DATETIME
);`
const migrateCronJobs = `CREATE TABLE IF NOT EXISTS cron_jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER REFERENCES sites(id),
job_type TEXT NOT NULL,
schedule TEXT NOT NULL,
enabled BOOLEAN DEFAULT TRUE,
last_run DATETIME,
next_run DATETIME
);`
const migrateFirewallRules = `CREATE TABLE IF NOT EXISTS firewall_rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
direction TEXT DEFAULT 'in',
protocol TEXT DEFAULT 'tcp',
port TEXT NOT NULL,
source TEXT DEFAULT 'any',
action TEXT DEFAULT 'allow',
comment TEXT DEFAULT '',
enabled BOOLEAN DEFAULT TRUE
);`
const migrateFloatSessions = `CREATE TABLE IF NOT EXISTS float_sessions (
id TEXT PRIMARY KEY,
user_id INTEGER REFERENCES manager_users(id),
client_ip TEXT,
client_agent TEXT,
usb_bridge BOOLEAN DEFAULT FALSE,
connected_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_ping DATETIME,
expires_at DATETIME
);`
const migrateAuditLog = `CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
ip TEXT,
action TEXT NOT NULL,
detail TEXT DEFAULT '',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`
const migrateBackups = `CREATE TABLE IF NOT EXISTS backups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER REFERENCES sites(id),
backup_type TEXT DEFAULT 'site',
file_path TEXT NOT NULL,
size_bytes INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`

View File

@@ -0,0 +1,60 @@
package db
import "time"
type Deployment struct {
ID int64 `json:"id"`
SiteID *int64 `json:"site_id"`
Action string `json:"action"`
Status string `json:"status"`
Output string `json:"output"`
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
}
func (d *DB) CreateDeployment(siteID *int64, action string) (int64, error) {
result, err := d.conn.Exec(`INSERT INTO deployments (site_id, action, status, started_at)
VALUES (?, ?, 'running', CURRENT_TIMESTAMP)`, siteID, action)
if err != nil {
return 0, err
}
return result.LastInsertId()
}
func (d *DB) FinishDeployment(id int64, status, output string) error {
_, err := d.conn.Exec(`UPDATE deployments SET status=?, output=?, finished_at=CURRENT_TIMESTAMP
WHERE id=?`, status, output, id)
return err
}
func (d *DB) ListDeployments(siteID *int64, limit int) ([]Deployment, error) {
var rows_query string
var args []interface{}
if siteID != nil {
rows_query = `SELECT id, site_id, action, status, output, started_at, finished_at
FROM deployments WHERE site_id=? ORDER BY id DESC LIMIT ?`
args = []interface{}{*siteID, limit}
} else {
rows_query = `SELECT id, site_id, action, status, output, started_at, finished_at
FROM deployments ORDER BY id DESC LIMIT ?`
args = []interface{}{limit}
}
rows, err := d.conn.Query(rows_query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var deps []Deployment
for rows.Next() {
var dep Deployment
if err := rows.Scan(&dep.ID, &dep.SiteID, &dep.Action, &dep.Status,
&dep.Output, &dep.StartedAt, &dep.FinishedAt); err != nil {
return nil, err
}
deps = append(deps, dep)
}
return deps, rows.Err()
}

View File

@@ -0,0 +1,70 @@
package db
import "time"
type FloatSession struct {
ID string `json:"id"`
UserID int64 `json:"user_id"`
ClientIP string `json:"client_ip"`
ClientAgent string `json:"client_agent"`
USBBridge bool `json:"usb_bridge"`
ConnectedAt time.Time `json:"connected_at"`
LastPing *time.Time `json:"last_ping"`
ExpiresAt time.Time `json:"expires_at"`
}
func (d *DB) CreateFloatSession(id string, userID int64, clientIP, agent string, expiresAt time.Time) error {
_, err := d.conn.Exec(`INSERT INTO float_sessions (id, user_id, client_ip, client_agent, expires_at)
VALUES (?, ?, ?, ?, ?)`, id, userID, clientIP, agent, expiresAt)
return err
}
func (d *DB) GetFloatSession(id string) (*FloatSession, error) {
var s FloatSession
err := d.conn.QueryRow(`SELECT id, user_id, client_ip, client_agent, usb_bridge,
connected_at, last_ping, expires_at FROM float_sessions WHERE id=?`, id).
Scan(&s.ID, &s.UserID, &s.ClientIP, &s.ClientAgent, &s.USBBridge,
&s.ConnectedAt, &s.LastPing, &s.ExpiresAt)
if err != nil {
return nil, err
}
return &s, nil
}
func (d *DB) ListFloatSessions() ([]FloatSession, error) {
rows, err := d.conn.Query(`SELECT id, user_id, client_ip, client_agent, usb_bridge,
connected_at, last_ping, expires_at FROM float_sessions ORDER BY connected_at DESC`)
if err != nil {
return nil, err
}
defer rows.Close()
var sessions []FloatSession
for rows.Next() {
var s FloatSession
if err := rows.Scan(&s.ID, &s.UserID, &s.ClientIP, &s.ClientAgent, &s.USBBridge,
&s.ConnectedAt, &s.LastPing, &s.ExpiresAt); err != nil {
return nil, err
}
sessions = append(sessions, s)
}
return sessions, rows.Err()
}
func (d *DB) DeleteFloatSession(id string) error {
_, err := d.conn.Exec(`DELETE FROM float_sessions WHERE id=?`, id)
return err
}
func (d *DB) PingFloatSession(id string) error {
_, err := d.conn.Exec(`UPDATE float_sessions SET last_ping=CURRENT_TIMESTAMP WHERE id=?`, id)
return err
}
func (d *DB) CleanExpiredFloatSessions() (int64, error) {
result, err := d.conn.Exec(`DELETE FROM float_sessions WHERE expires_at < CURRENT_TIMESTAMP`)
if err != nil {
return 0, err
}
return result.RowsAffected()
}

View File

@@ -0,0 +1,107 @@
package db
import (
"database/sql"
"time"
)
type Site struct {
ID int64 `json:"id"`
Domain string `json:"domain"`
Aliases string `json:"aliases"`
AppType string `json:"app_type"`
AppRoot string `json:"app_root"`
AppPort int `json:"app_port"`
AppEntry string `json:"app_entry"`
GitRepo string `json:"git_repo"`
GitBranch string `json:"git_branch"`
SSLEnabled bool `json:"ssl_enabled"`
SSLCertPath string `json:"ssl_cert_path"`
SSLKeyPath string `json:"ssl_key_path"`
SSLAuto bool `json:"ssl_auto"`
Enabled bool `json:"enabled"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (d *DB) ListSites() ([]Site, error) {
rows, err := d.conn.Query(`SELECT id, domain, aliases, app_type, app_root, app_port,
app_entry, git_repo, git_branch, ssl_enabled, ssl_cert_path, ssl_key_path,
ssl_auto, enabled, created_at, updated_at FROM sites ORDER BY domain`)
if err != nil {
return nil, err
}
defer rows.Close()
var sites []Site
for rows.Next() {
var s Site
if err := rows.Scan(&s.ID, &s.Domain, &s.Aliases, &s.AppType, &s.AppRoot,
&s.AppPort, &s.AppEntry, &s.GitRepo, &s.GitBranch, &s.SSLEnabled,
&s.SSLCertPath, &s.SSLKeyPath, &s.SSLAuto, &s.Enabled,
&s.CreatedAt, &s.UpdatedAt); err != nil {
return nil, err
}
sites = append(sites, s)
}
return sites, rows.Err()
}
func (d *DB) GetSite(id int64) (*Site, error) {
var s Site
err := d.conn.QueryRow(`SELECT id, domain, aliases, app_type, app_root, app_port,
app_entry, git_repo, git_branch, ssl_enabled, ssl_cert_path, ssl_key_path,
ssl_auto, enabled, created_at, updated_at FROM sites WHERE id = ?`, id).
Scan(&s.ID, &s.Domain, &s.Aliases, &s.AppType, &s.AppRoot,
&s.AppPort, &s.AppEntry, &s.GitRepo, &s.GitBranch, &s.SSLEnabled,
&s.SSLCertPath, &s.SSLKeyPath, &s.SSLAuto, &s.Enabled,
&s.CreatedAt, &s.UpdatedAt)
if err == sql.ErrNoRows {
return nil, nil
}
return &s, err
}
func (d *DB) GetSiteByDomain(domain string) (*Site, error) {
var s Site
err := d.conn.QueryRow(`SELECT id, domain, aliases, app_type, app_root, app_port,
app_entry, git_repo, git_branch, ssl_enabled, ssl_cert_path, ssl_key_path,
ssl_auto, enabled, created_at, updated_at FROM sites WHERE domain = ?`, domain).
Scan(&s.ID, &s.Domain, &s.Aliases, &s.AppType, &s.AppRoot,
&s.AppPort, &s.AppEntry, &s.GitRepo, &s.GitBranch, &s.SSLEnabled,
&s.SSLCertPath, &s.SSLKeyPath, &s.SSLAuto, &s.Enabled,
&s.CreatedAt, &s.UpdatedAt)
if err == sql.ErrNoRows {
return nil, nil
}
return &s, err
}
func (d *DB) CreateSite(s *Site) (int64, error) {
result, err := d.conn.Exec(`INSERT INTO sites (domain, aliases, app_type, app_root, app_port,
app_entry, git_repo, git_branch, ssl_enabled, ssl_cert_path, ssl_key_path, ssl_auto, enabled)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
s.Domain, s.Aliases, s.AppType, s.AppRoot, s.AppPort,
s.AppEntry, s.GitRepo, s.GitBranch, s.SSLEnabled,
s.SSLCertPath, s.SSLKeyPath, s.SSLAuto, s.Enabled)
if err != nil {
return 0, err
}
return result.LastInsertId()
}
func (d *DB) UpdateSite(s *Site) error {
_, err := d.conn.Exec(`UPDATE sites SET domain=?, aliases=?, app_type=?, app_root=?,
app_port=?, app_entry=?, git_repo=?, git_branch=?, ssl_enabled=?,
ssl_cert_path=?, ssl_key_path=?, ssl_auto=?, enabled=?, updated_at=CURRENT_TIMESTAMP
WHERE id=?`,
s.Domain, s.Aliases, s.AppType, s.AppRoot, s.AppPort,
s.AppEntry, s.GitRepo, s.GitBranch, s.SSLEnabled,
s.SSLCertPath, s.SSLKeyPath, s.SSLAuto, s.Enabled, s.ID)
return err
}
func (d *DB) DeleteSite(id int64) error {
_, err := d.conn.Exec(`DELETE FROM sites WHERE id=?`, id)
return err
}

View File

@@ -0,0 +1,124 @@
package db
import (
"database/sql"
"time"
"golang.org/x/crypto/bcrypt"
)
type ManagerUser struct {
ID int64 `json:"id"`
Username string `json:"username"`
PasswordHash string `json:"-"`
Role string `json:"role"`
ForceChange bool `json:"force_change"`
LastLogin *time.Time `json:"last_login"`
CreatedAt time.Time `json:"created_at"`
}
func (d *DB) ListManagerUsers() ([]ManagerUser, error) {
rows, err := d.conn.Query(`SELECT id, username, password_hash, role, force_change,
last_login, created_at FROM manager_users ORDER BY username`)
if err != nil {
return nil, err
}
defer rows.Close()
var users []ManagerUser
for rows.Next() {
var u ManagerUser
if err := rows.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role,
&u.ForceChange, &u.LastLogin, &u.CreatedAt); err != nil {
return nil, err
}
users = append(users, u)
}
return users, rows.Err()
}
func (d *DB) GetManagerUser(username string) (*ManagerUser, error) {
var u ManagerUser
err := d.conn.QueryRow(`SELECT id, username, password_hash, role, force_change,
last_login, created_at FROM manager_users WHERE username = ?`, username).
Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role,
&u.ForceChange, &u.LastLogin, &u.CreatedAt)
if err == sql.ErrNoRows {
return nil, nil
}
return &u, err
}
func (d *DB) GetManagerUserByID(id int64) (*ManagerUser, error) {
var u ManagerUser
err := d.conn.QueryRow(`SELECT id, username, password_hash, role, force_change,
last_login, created_at FROM manager_users WHERE id = ?`, id).
Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role,
&u.ForceChange, &u.LastLogin, &u.CreatedAt)
if err == sql.ErrNoRows {
return nil, nil
}
return &u, err
}
func (d *DB) CreateManagerUser(username, password, role string) (int64, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return 0, err
}
result, err := d.conn.Exec(`INSERT INTO manager_users (username, password_hash, role)
VALUES (?, ?, ?)`, username, string(hash), role)
if err != nil {
return 0, err
}
return result.LastInsertId()
}
func (d *DB) UpdateManagerUserPassword(id int64, password string) error {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
_, err = d.conn.Exec(`UPDATE manager_users SET password_hash=?, force_change=FALSE WHERE id=?`,
string(hash), id)
return err
}
func (d *DB) UpdateManagerUserRole(id int64, role string) error {
_, err := d.conn.Exec(`UPDATE manager_users SET role=? WHERE id=?`, role, id)
return err
}
func (d *DB) DeleteManagerUser(id int64) error {
_, err := d.conn.Exec(`DELETE FROM manager_users WHERE id=?`, id)
return err
}
func (d *DB) UpdateLoginTimestamp(id int64) error {
_, err := d.conn.Exec(`UPDATE manager_users SET last_login=CURRENT_TIMESTAMP WHERE id=?`, id)
return err
}
func (d *DB) ManagerUserCount() (int, error) {
var count int
err := d.conn.QueryRow(`SELECT COUNT(*) FROM manager_users`).Scan(&count)
return count, err
}
func (d *DB) AuthenticateUser(username, password string) (*ManagerUser, error) {
u, err := d.GetManagerUser(username)
if err != nil {
return nil, err
}
if u == nil {
return nil, nil
}
if err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password)); err != nil {
return nil, nil
}
d.UpdateLoginTimestamp(u.ID)
return u, nil
}