all repos — flounder @ 2d17a0055fd7f6473b133d6c9ddd924ea8c189af

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
 27type File struct { // also folders
 28	Creator     string
 29	Name        string // includes folder
 30	UpdatedTime time.Time
 31	TimeAgo     string
 32	IsText      bool
 33	Children    []File
 34	Host        string
 35}
 36
 37func fileFromPath(fullPath string) File {
 38	info, _ := os.Stat(fullPath)
 39	creatorFolder := getCreator(fullPath)
 40	isText := isTextFile(fullPath)
 41	updatedTime := info.ModTime()
 42	return File{
 43		Name:        getLocalPath(fullPath),
 44		Creator:     path.Base(creatorFolder),
 45		UpdatedTime: updatedTime,
 46		IsText:      isText,
 47		TimeAgo:     timeago(&updatedTime),
 48		Host:        c.Host,
 49	}
 50
 51}
 52
 53type User struct {
 54	Username  string
 55	Email     string
 56	Active    bool
 57	Admin     bool
 58	CreatedAt int // timestamp
 59	Reference string
 60}
 61
 62func getActiveUserNames() ([]string, error) {
 63	rows, err := DB.Query(`SELECT username from user WHERE active is true order by username`)
 64	if err != nil {
 65		return nil, err
 66	}
 67	var users []string
 68	for rows.Next() {
 69		var user string
 70		err = rows.Scan(&user)
 71		if err != nil {
 72			return nil, err
 73		}
 74		users = append(users, user)
 75	}
 76
 77	return users, nil
 78}
 79
 80func getUserByName(username string) (*User, error) {
 81	var user User
 82	row := DB.QueryRow(`SELECT username, email, active, admin, created_at, reference from user WHERE username = ?`, username)
 83	err := row.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference)
 84	if err != nil {
 85		return nil, err
 86	}
 87	return &user, nil
 88}
 89
 90func getUsers() ([]User, error) {
 91	rows, err := DB.Query(`SELECT username, email, active, admin, created_at, reference from user ORDER BY created_at DESC`)
 92	if err != nil {
 93		return nil, err
 94	}
 95	var users []User
 96	for rows.Next() {
 97		var user User
 98		err = rows.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference)
 99		if err != nil {
100			return nil, err
101		}
102		users = append(users, user)
103	}
104	return users, nil
105}
106
107func getIndexFiles(admin bool) ([]*File, error) { // cache this function
108	result := []*File{}
109	err := filepath.Walk(c.FilesDirectory, func(thepath string, info os.FileInfo, err error) error {
110		if err != nil {
111			log.Printf("Failure accessing a path %q: %v\n", thepath, err)
112			return err // think about
113		}
114		if !admin && info.IsDir() && info.Name() == HiddenFolder {
115			return filepath.SkipDir
116		}
117		// make this do what it should
118		if !info.IsDir() {
119			res := fileFromPath(thepath)
120			result = append(result, &res)
121		}
122		return nil
123	})
124	if err != nil {
125		return nil, err
126	}
127	sort.Slice(result, func(i, j int) bool {
128		return result[i].UpdatedTime.After(result[j].UpdatedTime)
129	})
130	if len(result) > 50 {
131		result = result[:50]
132	}
133	return result, nil
134} // todo clean up paths
135
136func getMyFilesRecursive(p string, creator string) ([]File, error) {
137	result := []File{}
138	files, err := ioutil.ReadDir(p)
139	if err != nil {
140		return nil, err
141	}
142	for _, file := range files {
143		fullPath := path.Join(p, file.Name())
144		f := fileFromPath(fullPath)
145		if file.IsDir() {
146			f.Children, err = getMyFilesRecursive(path.Join(p, file.Name()), creator)
147		}
148		result = append(result, f)
149	}
150	return result, nil
151}
152
153func createTablesIfDNE() {
154	_, err := DB.Exec(`CREATE TABLE IF NOT EXISTS user (
155  id INTEGER PRIMARY KEY NOT NULL,
156  username TEXT NOT NULL UNIQUE,
157  email TEXT NOT NULL UNIQUE,
158  password_hash TEXT NOT NULL,
159  reference TEXT NOT NULL default "",
160  active boolean NOT NULL DEFAULT false,
161  admin boolean NOT NULL DEFAULT false,
162  created_at INTEGER DEFAULT (strftime('%s', 'now'))
163);
164
165CREATE TABLE IF NOT EXISTS cookie_key (
166  value TEXT NOT NULL
167);`)
168	if err != nil {
169		log.Fatal(err)
170	}
171}
172
173// Generate a cryptographically secure key for the cookie store
174func generateCookieKeyIfDNE() []byte {
175	rows, err := DB.Query("SELECT value FROM cookie_key LIMIT 1")
176	defer rows.Close()
177	if err != nil {
178		log.Fatal(err)
179	}
180	if rows.Next() {
181		var cookie []byte
182		err := rows.Scan(&cookie)
183		if err != nil {
184			log.Fatal(err)
185		}
186		return cookie
187	} else {
188		k := make([]byte, 32)
189		_, err := io.ReadFull(rand.Reader, k)
190		if err != nil {
191			log.Fatal(err)
192		}
193		_, err = DB.Exec("insert into cookie_key values (?)", k)
194		if err != nil {
195			log.Fatal(err)
196		}
197		return k
198	}
199}