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 }