tagliatelle

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

include-db.go (4408B)


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