all repos — flounder @ aa3a622afdb7e9d30b464b78cc40d0aa728ea96c

A small site builder for the Gemini protocol

db.go (view raw)

  1package main
  2
  3import (
  4	"crypto/rand"
  5	"database/sql"
  6	"io"
  7	"io/ioutil"
  8	"log"
  9	"os"
 10	"path"
 11	"path/filepath"
 12	"sort"
 13	"time"
 14)
 15
 16var DB *sql.DB
 17
 18func initializeDB() {
 19	var err error
 20	DB, err = sql.Open("sqlite3", c.DBFile)
 21	if err != nil {
 22		log.Fatal(err)
 23	}
 24	createTablesIfDNE()
 25}
 26
 27func getAnalyticsDB() (*sql.DB, error) {
 28	db, err := sql.Open("sqlite3", c.AnalyticsDBFile)
 29	_, err = db.Exec(`CREATE TABLE IF NOT EXISTS log (
 30  id INTEGER PRIMARY KEY NOT NULL,
 31  timestamp TEXT NOT NULL,
 32  protocol TEXT NOT NULL,
 33  request_ip TEXT,
 34  request_user TEXT,
 35  status INTEGER,
 36  destination_host TEXT,
 37  path TEXT,
 38  method TEXT,
 39  referer TEXT
 40);`)
 41	return db, err
 42}
 43
 44type File struct { // also folders
 45	Creator     string
 46	Name        string // includes folder
 47	UpdatedTime time.Time
 48	TimeAgo     string
 49	IsText      bool
 50	Children    []File
 51	Host        string
 52}
 53
 54func fileFromPath(fullPath string) File {
 55	info, _ := os.Stat(fullPath)
 56	creatorFolder := getCreator(fullPath)
 57	isText := isTextFile(fullPath)
 58	updatedTime := info.ModTime()
 59	return File{
 60		Name:        getLocalPath(fullPath),
 61		Creator:     path.Base(creatorFolder),
 62		UpdatedTime: updatedTime,
 63		IsText:      isText,
 64		TimeAgo:     timeago(&updatedTime),
 65		Host:        c.Host,
 66	}
 67
 68}
 69
 70type User struct {
 71	Username      string
 72	Email         string
 73	Active        bool
 74	Admin         bool
 75	CreatedAt     int // timestamp
 76	Reference     string
 77	Domain        string
 78	DomainEnabled bool
 79}
 80
 81func getActiveUserNames() ([]string, error) {
 82	rows, err := DB.Query(`SELECT username from user WHERE active is true order by username`)
 83	if err != nil {
 84		return nil, err
 85	}
 86	var users []string
 87	for rows.Next() {
 88		var user string
 89		err = rows.Scan(&user)
 90		if err != nil {
 91			return nil, err
 92		}
 93		users = append(users, user)
 94	}
 95
 96	return users, nil
 97}
 98
 99var domains map[string]string
100
101func refreshDomainMap() error {
102	domains = make(map[string]string)
103	rows, err := DB.Query(`SELECT domain, username from user WHERE domain != ""`)
104	if err != nil {
105		log.Println(err)
106		return err
107	}
108	for rows.Next() {
109		var domain string
110		var username string
111		err = rows.Scan(&domain, &username)
112		if err != nil {
113			return err
114		}
115		domains[domain] = username
116	}
117	return nil
118}
119
120func getUserByName(username string) (*User, error) {
121	var user User
122	row := DB.QueryRow(`SELECT username, email, active, admin, created_at, reference, domain, domain_enabled from user WHERE username = ?`, username)
123	err := row.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference, &user.Domain, &user.DomainEnabled)
124	if err != nil {
125		return nil, err
126	}
127	return &user, nil
128}
129
130func getUsers() ([]User, error) {
131	rows, err := DB.Query(`SELECT username, email, active, admin, created_at, reference, domain from user ORDER BY created_at DESC`)
132	if err != nil {
133		return nil, err
134	}
135	var users []User
136	for rows.Next() {
137		var user User
138		err = rows.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference, &user.Domain)
139		if err != nil {
140			return nil, err
141		}
142		users = append(users, user)
143	}
144	return users, nil
145}
146
147func getIndexFiles(admin bool) ([]*File, error) { // cache this function
148	result := []*File{}
149	err := filepath.Walk(c.FilesDirectory, func(thepath string, info os.FileInfo, err error) error {
150		if err != nil {
151			log.Printf("Failure accessing a path %q: %v\n", thepath, err)
152			return err // think about
153		}
154		if !admin && info.IsDir() && info.Name() == HiddenFolder {
155			return filepath.SkipDir
156		}
157		// make this do what it should
158		if !info.IsDir() {
159			res := fileFromPath(thepath)
160			result = append(result, &res)
161		}
162		return nil
163	})
164	if err != nil {
165		return nil, err
166	}
167	sort.Slice(result, func(i, j int) bool {
168		return result[i].UpdatedTime.After(result[j].UpdatedTime)
169	})
170	if len(result) > 50 {
171		result = result[:50]
172	}
173	return result, nil
174} // todo clean up paths
175
176func getMyFilesRecursive(p string, creator string) ([]File, error) {
177	result := []File{}
178	files, err := ioutil.ReadDir(p)
179	if err != nil {
180		return nil, err
181	}
182	for _, file := range files {
183		fullPath := path.Join(p, file.Name())
184		f := fileFromPath(fullPath)
185		if file.IsDir() {
186			f.Children, err = getMyFilesRecursive(path.Join(p, file.Name()), creator)
187		}
188		result = append(result, f)
189	}
190	return result, nil
191}
192
193func createTablesIfDNE() {
194	_, err := DB.Exec(`CREATE TABLE IF NOT EXISTS user (
195  id INTEGER PRIMARY KEY NOT NULL,
196  username TEXT NOT NULL UNIQUE,
197  email TEXT NOT NULL UNIQUE,
198  password_hash TEXT NOT NULL,
199  reference TEXT NOT NULL default "",
200  active boolean NOT NULL DEFAULT false,
201  admin boolean NOT NULL DEFAULT false,
202  created_at INTEGER DEFAULT (strftime('%s', 'now')),
203  domain TEXT NOT NULL default "",
204  domain_enabled BOOLEAN NOT NULL DEFAULT false
205);
206
207CREATE TABLE IF NOT EXISTS cookie_key (
208  value TEXT NOT NULL
209);`)
210	if err != nil {
211		log.Fatal(err)
212	}
213}
214
215// Generate a cryptographically secure key for the cookie store
216func generateCookieKeyIfDNE() []byte {
217	rows, err := DB.Query("SELECT value FROM cookie_key LIMIT 1")
218	defer rows.Close()
219	if err != nil {
220		log.Fatal(err)
221	}
222	if rows.Next() {
223		var cookie []byte
224		err := rows.Scan(&cookie)
225		if err != nil {
226			log.Fatal(err)
227		}
228		return cookie
229	} else {
230		k := make([]byte, 32)
231		_, err := io.ReadFull(rand.Reader, k)
232		if err != nil {
233			log.Fatal(err)
234		}
235		_, err = DB.Exec("insert into cookie_key values (?)", k)
236		if err != nil {
237			log.Fatal(err)
238		}
239		return k
240	}
241}