main.go (view raw)
1package main
2
3import (
4 "crypto/rand"
5 "database/sql"
6 "flag"
7 "github.com/gorilla/sessions"
8 "io"
9 "io/ioutil"
10 "log"
11 "os"
12 "path"
13 "path/filepath"
14 "sort"
15 "sync"
16 "time"
17)
18
19var c Config // global var to hold static configuration
20
21type File struct {
22 Creator string
23 Name string
24 UpdatedTime time.Time
25 TimeAgo string
26}
27
28func getUsers() ([]string, error) {
29 rows, err := DB.Query(`SELECT username from user`)
30 if err != nil {
31 return nil, err
32 }
33 var users []string
34 for rows.Next() {
35 var user string
36 err = rows.Scan(&user)
37 if err != nil {
38 return nil, err
39 }
40 users = append(users, user)
41 }
42 return users, nil
43}
44
45func getIndexFiles() ([]*File, error) { // cache this function
46 result := []*File{}
47 err := filepath.Walk(c.FilesDirectory, func(thepath string, info os.FileInfo, err error) error {
48 if err != nil {
49 log.Printf("Failure accessing a path %q: %v\n", thepath, err)
50 return err // think about
51 }
52 // make this do what it should
53 if !info.IsDir() {
54 creatorFolder, _ := path.Split(thepath)
55 updatedTime := info.ModTime()
56 result = append(result, &File{
57 Name: info.Name(),
58 Creator: path.Base(creatorFolder),
59 UpdatedTime: updatedTime,
60 TimeAgo: timeago(&updatedTime),
61 })
62 }
63 return nil
64 })
65 if err != nil {
66 return nil, err
67 }
68 // sort
69 // truncate
70 sort.Slice(result, func(i, j int) bool {
71 return result[i].UpdatedTime.Before(result[j].UpdatedTime)
72 })
73 if len(result) > 50 {
74 result = result[:50]
75 }
76 return result, nil
77} // todo clean up paths
78
79func getUserFiles(user string) ([]*File, error) {
80 result := []*File{}
81 files, err := ioutil.ReadDir(path.Join(c.FilesDirectory, user))
82 if err != nil {
83 return nil, err
84 }
85 for _, file := range files {
86 result = append(result, &File{
87 Name: file.Name(),
88 Creator: user,
89 UpdatedTime: file.ModTime(),
90 })
91 }
92 return result, nil
93}
94
95func createTablesIfDNE() {
96 _, err := DB.Exec(`CREATE TABLE IF NOT EXISTS user (
97 id INTEGER PRIMARY KEY NOT NULL,
98 username TEXT NOT NULL UNIQUE,
99 email TEXT NOT NULL UNIQUE,
100 password_hash TEXT NOT NULL,
101 approved boolean NOT NULL DEFAULT false,
102 created_at INTEGER DEFAULT (strftime('%s', 'now'))
103);
104
105CREATE TABLE IF NOT EXISTS cookie_key (
106 value TEXT NOT NULL
107);`)
108 if err != nil {
109 log.Fatal(err)
110 }
111}
112
113// Generate a cryptographically secure key for the cookie store
114func generateCookieKeyIfDNE() []byte {
115 rows, err := DB.Query("SELECT value FROM cookie_key LIMIT 1")
116 if err != nil {
117 log.Fatal(err)
118 }
119 if rows.Next() {
120 var cookie []byte
121 err := rows.Scan(&cookie)
122 if err != nil {
123 log.Fatal(err)
124 }
125 return cookie
126 } else {
127 k := make([]byte, 32)
128 _, err := io.ReadFull(rand.Reader, k)
129 if err != nil {
130 log.Fatal(err)
131 }
132 _, err = DB.Exec("insert into cookie_key values ($1)", k)
133 if err != nil {
134 log.Fatal(err)
135 }
136 return k
137 }
138}
139
140func main() {
141 configPath := flag.String("c", "flounder.toml", "path to config file")
142 var err error
143 c, err = getConfig(*configPath)
144 if err != nil {
145 log.Fatal(err)
146 }
147
148 // Generate self signed cert if does not exist. This is not suitable for production.
149 _, err1 := os.Stat(c.TLSCertFile)
150 _, err2 := os.Stat(c.TLSKeyFile)
151 if os.IsNotExist(err1) || os.IsNotExist(err2) {
152 log.Println("Keyfile or certfile does not exist.")
153 }
154
155 // Generate session cookie key if does not exist
156 DB, err = sql.Open("sqlite3", c.DBFile)
157 if err != nil {
158 log.Fatal(err)
159 }
160
161 createTablesIfDNE()
162 cookie := generateCookieKeyIfDNE()
163 SessionStore = sessions.NewCookieStore(cookie)
164 wg := new(sync.WaitGroup)
165 wg.Add(2)
166 go func() {
167 runHTTPServer()
168 wg.Done()
169 }()
170 go func() {
171 runGeminiServer()
172 wg.Done()
173 }()
174 wg.Wait()
175}