tagliatelle

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

include-db.go (4750B)


      1 package main
      2 
      3 import (
      4 	"database/sql"
      5 	"strings"
      6 
      7 	_ "github.com/mattn/go-sqlite3"
      8 )
      9 
     10 type Config struct {
     11 	// Values from CLI arguments
     12 	DatabasePath string
     13 	UploadDir    string
     14 	ServerPort   string
     15 	// Values from database
     16 	GallerySize  string
     17 	ItemsPerPage string
     18 	TagAliases   []TagAliasGroup
     19 	SedRules     []SedRule
     20 }
     21 
     22 type TagAliasGroup struct {
     23 	Category string   `json:"category"`
     24 	Aliases  []string `json:"aliases"`
     25 }
     26 
     27 // InitDatabase opens the database connection and creates tables if needed
     28 func InitDatabase(dbPath string) (*sql.DB, error) {
     29 	db, err := sql.Open("sqlite3", dbPath+"?_busy_timeout=5000")
     30 	if err != nil {
     31 		return nil, err
     32 	}
     33 
     34 	if err := createTables(db); err != nil {
     35 		db.Close()
     36 		return nil, err
     37 	}
     38 
     39 	return db, nil
     40 }
     41 
     42 // createTables creates all necessary database tables
     43 func createTables(db *sql.DB) error {
     44 	schema := `
     45 	CREATE TABLE IF NOT EXISTS files (
     46 		id INTEGER PRIMARY KEY AUTOINCREMENT,
     47 		filename TEXT,
     48 		path TEXT,
     49 		description TEXT DEFAULT ''
     50 	);
     51 	CREATE TABLE IF NOT EXISTS categories (
     52 		id INTEGER PRIMARY KEY AUTOINCREMENT,
     53 		name TEXT UNIQUE
     54 	);
     55 	CREATE TABLE IF NOT EXISTS tags (
     56 		id INTEGER PRIMARY KEY AUTOINCREMENT,
     57 		category_id INTEGER,
     58 		value TEXT,
     59 		UNIQUE(category_id, value)
     60 	);
     61 	CREATE TABLE IF NOT EXISTS file_tags (
     62 		file_id INTEGER,
     63 		tag_id INTEGER,
     64 		UNIQUE(file_id, tag_id)
     65 	);
     66 	CREATE TABLE IF NOT EXISTS file_properties (
     67 		file_id INTEGER,
     68 		key   TEXT,
     69 		value TEXT,
     70 		UNIQUE(file_id, key)
     71 	);
     72 	CREATE TABLE IF NOT EXISTS notes (
     73 		id INTEGER PRIMARY KEY CHECK (id = 1),
     74 		content TEXT DEFAULT '',
     75 		updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
     76 	);
     77 	CREATE TABLE IF NOT EXISTS settings (
     78 		key   TEXT PRIMARY KEY,
     79 		value TEXT NOT NULL DEFAULT ''
     80 	);
     81 	CREATE TABLE IF NOT EXISTS tag_aliases (
     82 		id       INTEGER PRIMARY KEY AUTOINCREMENT,
     83 		category TEXT NOT NULL,
     84 		aliases  TEXT NOT NULL
     85 	);
     86 	CREATE TABLE IF NOT EXISTS sed_rules (
     87 		id          INTEGER PRIMARY KEY AUTOINCREMENT,
     88 		name        TEXT NOT NULL,
     89 		description TEXT NOT NULL DEFAULT '',
     90 		command     TEXT NOT NULL
     91 	);
     92 	`
     93 
     94 	_, err := db.Exec(schema)
     95 	return err
     96 }
     97 
     98 func LoadConfig(db *sql.DB) (Config, error) {
     99 	cfg := Config{
    100 		GallerySize:  "400px",
    101 		ItemsPerPage: "100",
    102 		TagAliases:   []TagAliasGroup{},
    103 		SedRules:     []SedRule{},
    104 	}
    105 
    106 	rows, err := db.Query(`SELECT key, value FROM settings`)
    107 	if err != nil {
    108 		return cfg, err
    109 	}
    110 	defer rows.Close()
    111 	for rows.Next() {
    112 		var key, value string
    113 		if err := rows.Scan(&key, &value); err != nil {
    114 			return cfg, err
    115 		}
    116 		switch key {
    117 		case "gallery_size":
    118 			if value != "" {
    119 				cfg.GallerySize = value
    120 			}
    121 		case "items_per_page":
    122 			if value != "" {
    123 				cfg.ItemsPerPage = value
    124 			}
    125 		}
    126 	}
    127 	if err := rows.Err(); err != nil {
    128 		return cfg, err
    129 	}
    130 
    131 	aliasRows, err := db.Query(`SELECT category, aliases FROM tag_aliases ORDER BY id`)
    132 	if err != nil {
    133 		return cfg, err
    134 	}
    135 	defer aliasRows.Close()
    136 	for aliasRows.Next() {
    137 		var category, aliasesStr string
    138 		if err := aliasRows.Scan(&category, &aliasesStr); err != nil {
    139 			return cfg, err
    140 		}
    141 		cfg.TagAliases = append(cfg.TagAliases, TagAliasGroup{
    142 			Category: category,
    143 			Aliases:  strings.Split(aliasesStr, "|"),
    144 		})
    145 	}
    146 	if err := aliasRows.Err(); err != nil {
    147 		return cfg, err
    148 	}
    149 
    150 	sedRows, err := db.Query(`SELECT name, description, command FROM sed_rules ORDER BY id`)
    151 	if err != nil {
    152 		return cfg, err
    153 	}
    154 	defer sedRows.Close()
    155 	for sedRows.Next() {
    156 		var rule SedRule
    157 		if err := sedRows.Scan(&rule.Name, &rule.Description, &rule.Command); err != nil {
    158 			return cfg, err
    159 		}
    160 		cfg.SedRules = append(cfg.SedRules, rule)
    161 	}
    162 	if err := sedRows.Err(); err != nil {
    163 		return cfg, err
    164 	}
    165 
    166 	return cfg, nil
    167 }
    168 
    169 func SaveConfig(db *sql.DB, cfg Config) error {
    170 	tx, err := db.Begin()
    171 	if err != nil {
    172 		return err
    173 	}
    174 	defer tx.Rollback()
    175 
    176 	for _, kv := range [][2]string{
    177 		{"gallery_size", cfg.GallerySize},
    178 		{"items_per_page", cfg.ItemsPerPage},
    179 	} {
    180 		if _, err := tx.Exec(`
    181 			INSERT INTO settings (key, value) VALUES (?, ?)
    182 			ON CONFLICT(key) DO UPDATE SET value = excluded.value
    183 		`, kv[0], kv[1]); err != nil {
    184 			return err
    185 		}
    186 	}
    187 
    188 	if _, err := tx.Exec(`DELETE FROM tag_aliases`); err != nil {
    189 		return err
    190 	}
    191 	for _, group := range cfg.TagAliases {
    192 		aliasesStr := strings.Join(group.Aliases, "|")
    193 		if _, err := tx.Exec(
    194 			`INSERT INTO tag_aliases (category, aliases) VALUES (?, ?)`,
    195 			group.Category, aliasesStr,
    196 		); err != nil {
    197 			return err
    198 		}
    199 	}
    200 
    201 	if _, err := tx.Exec(`DELETE FROM sed_rules`); err != nil {
    202 		return err
    203 	}
    204 	for _, rule := range cfg.SedRules {
    205 		if _, err := tx.Exec(
    206 			`INSERT INTO sed_rules (name, description, command) VALUES (?, ?, ?)`,
    207 			rule.Name, rule.Description, rule.Command,
    208 		); err != nil {
    209 			return err
    210 		}
    211 	}
    212 
    213 	return tx.Commit()
    214 }