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