flounder

A simple gemini site builder
Log | Files | Refs | README | LICENSE

commit 54e04e31ab9c15c8d6850b33bffd5515454df31d
parent 2772a6a12025ad55c52e87a9be1ee129d0e6750b
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Wed, 30 Dec 2020 10:45:14 -0800

refactor -- move stuff out of main.go

Diffstat:
Adb.go | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mhttp.go | 2--
Mmain.go | 209+------------------------------------------------------------------------------
Mutils.go | 13+++++++++++++
4 files changed, 220 insertions(+), 210 deletions(-)

diff --git a/db.go b/db.go @@ -0,0 +1,206 @@ +package main + +import ( + "crypto/rand" + "database/sql" + "io" + "io/ioutil" + "log" + mathrand "math/rand" + "os" + "path" + "path/filepath" + "sort" + "time" +) + +var DB *sql.DB + +func initializeDB() { + var err error + DB, err = sql.Open("sqlite3", c.DBFile) + if err != nil { + log.Fatal(err) + } + createTablesIfDNE() +} + +type File struct { // also folders + Creator string + Name string // includes folder + UpdatedTime time.Time + TimeAgo string + IsText bool + Children []File + Host string +} + +func fileFromPath(fullPath string) File { + info, _ := os.Stat(fullPath) + creatorFolder := getCreator(fullPath) + isText := isTextFile(fullPath) + updatedTime := info.ModTime() + return File{ + Name: getLocalPath(fullPath), + Creator: path.Base(creatorFolder), + UpdatedTime: updatedTime, + IsText: isText, + TimeAgo: timeago(&updatedTime), + Host: c.Host, + } + +} + +type User struct { + Username string + Email string + Active bool + Admin bool + CreatedAt int // timestamp + Reference string +} + +// returns in a random order +func getActiveUserNames() ([]string, error) { + rows, err := DB.Query(`SELECT username from user WHERE active is true`) + if err != nil { + return nil, err + } + var users []string + for rows.Next() { + var user string + err = rows.Scan(&user) + if err != nil { + return nil, err + } + users = append(users, user) + } + + dest := make([]string, len(users)) + perm := mathrand.Perm(len(users)) + for i, v := range perm { + dest[v] = users[i] + } + return dest, nil +} + +func getUserByName(username string) (*User, error) { + var user User + row := DB.QueryRow(`SELECT username, email, active, admin, created_at, reference from user WHERE username = ?`, username) + err := row.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference) + if err != nil { + return nil, err + } + return &user, nil +} + +func getUsers() ([]User, error) { + rows, err := DB.Query(`SELECT username, email, active, admin, created_at, reference from user ORDER BY created_at DESC`) + if err != nil { + return nil, err + } + var users []User + for rows.Next() { + var user User + err = rows.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference) + if err != nil { + return nil, err + } + users = append(users, user) + } + return users, nil +} + +func getIndexFiles(admin bool) ([]*File, error) { // cache this function + result := []*File{} + err := filepath.Walk(c.FilesDirectory, func(thepath string, info os.FileInfo, err error) error { + if err != nil { + log.Printf("Failure accessing a path %q: %v\n", thepath, err) + return err // think about + } + if !admin && info.IsDir() && info.Name() == HIDDEN_FOLDER { + return filepath.SkipDir + } + // make this do what it should + if !info.IsDir() { + res := fileFromPath(thepath) + result = append(result, &res) + } + return nil + }) + if err != nil { + return nil, err + } + sort.Slice(result, func(i, j int) bool { + return result[i].UpdatedTime.After(result[j].UpdatedTime) + }) + if len(result) > 50 { + result = result[:50] + } + return result, nil +} // todo clean up paths + +func getMyFilesRecursive(p string, creator string) ([]File, error) { + result := []File{} + files, err := ioutil.ReadDir(p) + if err != nil { + return nil, err + } + for _, file := range files { + fullPath := path.Join(p, file.Name()) + f := fileFromPath(fullPath) + if file.IsDir() { + f.Children, err = getMyFilesRecursive(path.Join(p, file.Name()), creator) + } + result = append(result, f) + } + return result, nil +} + +func createTablesIfDNE() { + _, err := DB.Exec(`CREATE TABLE IF NOT EXISTS user ( + id INTEGER PRIMARY KEY NOT NULL, + username TEXT NOT NULL UNIQUE, + email TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + reference TEXT NOT NULL default "", + active boolean NOT NULL DEFAULT false, + admin boolean NOT NULL DEFAULT false, + created_at INTEGER DEFAULT (strftime('%s', 'now')) +); + +CREATE TABLE IF NOT EXISTS cookie_key ( + value TEXT NOT NULL +);`) + if err != nil { + log.Fatal(err) + } +} + +// Generate a cryptographically secure key for the cookie store +func generateCookieKeyIfDNE() []byte { + rows, err := DB.Query("SELECT value FROM cookie_key LIMIT 1") + defer rows.Close() + if err != nil { + log.Fatal(err) + } + if rows.Next() { + var cookie []byte + err := rows.Scan(&cookie) + if err != nil { + log.Fatal(err) + } + return cookie + } else { + k := make([]byte, 32) + _, err := io.ReadFull(rand.Reader, k) + if err != nil { + log.Fatal(err) + } + _, err = DB.Exec("insert into cookie_key values (?)", k) + if err != nil { + log.Fatal(err) + } + return k + } +} diff --git a/http.go b/http.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "database/sql" "fmt" gmi "git.sr.ht/~adnano/go-gemini" "github.com/gorilla/handlers" @@ -22,7 +21,6 @@ import ( ) var t *template.Template -var DB *sql.DB var SessionStore *sessions.CookieStore func renderDefaultError(w http.ResponseWriter, statusCode int) { diff --git a/main.go b/main.go @@ -1,221 +1,19 @@ package main import ( - "crypto/rand" - "database/sql" "flag" "fmt" "github.com/gorilla/sessions" "io" - "io/ioutil" "log" - mathrand "math/rand" "os" - "path" - "path/filepath" - "sort" - "strings" "sync" - "time" ) var c Config // global var to hold static configuration const HIDDEN_FOLDER = ".hidden" -type File struct { // also folders - Creator string - Name string // includes folder - UpdatedTime time.Time - TimeAgo string - IsText bool - Children []File - Host string -} - -func fileFromPath(fullPath string) File { - info, _ := os.Stat(fullPath) - creatorFolder := getCreator(fullPath) - isText := isTextFile(fullPath) - updatedTime := info.ModTime() - return File{ - Name: getLocalPath(fullPath), - Creator: path.Base(creatorFolder), - UpdatedTime: updatedTime, - IsText: isText, - TimeAgo: timeago(&updatedTime), - Host: c.Host, - } - -} - -type User struct { - Username string - Email string - Active bool - Admin bool - CreatedAt int // timestamp - Reference string -} - -// returns in a random order -func getActiveUserNames() ([]string, error) { - rows, err := DB.Query(`SELECT username from user WHERE active is true`) - if err != nil { - return nil, err - } - var users []string - for rows.Next() { - var user string - err = rows.Scan(&user) - if err != nil { - return nil, err - } - users = append(users, user) - } - - dest := make([]string, len(users)) - perm := mathrand.Perm(len(users)) - for i, v := range perm { - dest[v] = users[i] - } - return dest, nil -} - -func getUserByName(username string) (*User, error) { - var user User - row := DB.QueryRow(`SELECT username, email, active, admin, created_at, reference from user WHERE username = ?`, username) - err := row.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference) - if err != nil { - return nil, err - } - return &user, nil -} - -func getUsers() ([]User, error) { - rows, err := DB.Query(`SELECT username, email, active, admin, created_at, reference from user ORDER BY created_at DESC`) - if err != nil { - return nil, err - } - var users []User - for rows.Next() { - var user User - err = rows.Scan(&user.Username, &user.Email, &user.Active, &user.Admin, &user.CreatedAt, &user.Reference) - if err != nil { - return nil, err - } - users = append(users, user) - } - return users, nil -} - -// get the user-reltaive local path from the filespath -// NOTE -- dont use on unsafe input ( I think ) -func getLocalPath(filesPath string) string { - l := len(strings.Split(c.FilesDirectory, "/")) - return strings.Join(strings.Split(filesPath, "/")[l+1:], "/") -} - -func getCreator(filePath string) string { - l := len(strings.Split(c.FilesDirectory, "/")) - r := strings.Split(filePath, "/")[l] - return r -} - -func getIndexFiles(admin bool) ([]*File, error) { // cache this function - result := []*File{} - err := filepath.Walk(c.FilesDirectory, func(thepath string, info os.FileInfo, err error) error { - if err != nil { - log.Printf("Failure accessing a path %q: %v\n", thepath, err) - return err // think about - } - if !admin && info.IsDir() && info.Name() == HIDDEN_FOLDER { - return filepath.SkipDir - } - // make this do what it should - if !info.IsDir() { - res := fileFromPath(thepath) - result = append(result, &res) - } - return nil - }) - if err != nil { - return nil, err - } - sort.Slice(result, func(i, j int) bool { - return result[i].UpdatedTime.After(result[j].UpdatedTime) - }) - if len(result) > 50 { - result = result[:50] - } - return result, nil -} // todo clean up paths - -func getMyFilesRecursive(p string, creator string) ([]File, error) { - result := []File{} - files, err := ioutil.ReadDir(p) - if err != nil { - return nil, err - } - for _, file := range files { - fullPath := path.Join(p, file.Name()) - f := fileFromPath(fullPath) - if file.IsDir() { - f.Children, err = getMyFilesRecursive(path.Join(p, file.Name()), creator) - } - result = append(result, f) - } - return result, nil -} - -func createTablesIfDNE() { - _, err := DB.Exec(`CREATE TABLE IF NOT EXISTS user ( - id INTEGER PRIMARY KEY NOT NULL, - username TEXT NOT NULL UNIQUE, - email TEXT NOT NULL UNIQUE, - password_hash TEXT NOT NULL, - reference TEXT NOT NULL default "", - active boolean NOT NULL DEFAULT false, - admin boolean NOT NULL DEFAULT false, - created_at INTEGER DEFAULT (strftime('%s', 'now')) -); - -CREATE TABLE IF NOT EXISTS cookie_key ( - value TEXT NOT NULL -);`) - if err != nil { - log.Fatal(err) - } -} - -// Generate a cryptographically secure key for the cookie store -func generateCookieKeyIfDNE() []byte { - rows, err := DB.Query("SELECT value FROM cookie_key LIMIT 1") - defer rows.Close() - if err != nil { - log.Fatal(err) - } - if rows.Next() { - var cookie []byte - err := rows.Scan(&cookie) - if err != nil { - log.Fatal(err) - } - return cookie - } else { - k := make([]byte, 32) - _, err := io.ReadFull(rand.Reader, k) - if err != nil { - log.Fatal(err) - } - _, err = DB.Exec("insert into cookie_key values (?)", k) - if err != nil { - log.Fatal(err) - } - return k - } -} - func main() { configPath := flag.String("c", "flounder.toml", "path to config file") // doesnt work atm flag.Parse() @@ -245,13 +43,8 @@ func main() { } } - // Generate session cookie key if does not exist - DB, err = sql.Open("sqlite3", c.DBFile) - if err != nil { - log.Fatal(err) - } + initializeDB() - createTablesIfDNE() cookie := generateCookieKeyIfDNE() SessionStore = sessions.NewCookieStore(cookie) diff --git a/utils.go b/utils.go @@ -46,6 +46,19 @@ func isTextFile(fullPath string) bool { return true } +// get the user-reltaive local path from the filespath +// NOTE -- dont use on unsafe input ( I think ) +func getLocalPath(filesPath string) string { + l := len(strings.Split(c.FilesDirectory, "/")) + return strings.Join(strings.Split(filesPath, "/")[l+1:], "/") +} + +func getCreator(filePath string) string { + l := len(strings.Split(c.FilesDirectory, "/")) + r := strings.Split(filePath, "/")[l] + return r +} + func isGemini(filename string) bool { extension := path.Ext(filename) return extension == ".gmi" || extension == ".gemini"