2023-10-25 04:14:32 +00:00
// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: Apache-2.0
package main
import (
"errors"
"fmt"
"log"
"net/http"
"os"
2024-02-23 21:29:35 +00:00
"strconv"
2024-02-23 23:06:16 +00:00
"sync"
2023-10-25 04:14:32 +00:00
"git.sr.ht/~amolith/willow/db"
"git.sr.ht/~amolith/willow/project"
"git.sr.ht/~amolith/willow/ws"
"github.com/BurntSushi/toml"
flag "github.com/spf13/pflag"
)
type (
Config struct {
2024-02-23 23:06:16 +00:00
Server server
DBConn string
2023-10-25 04:14:32 +00:00
// TODO: Make cache location configurable
// CacheLocation string
FetchInterval int
}
server struct {
Listen string
}
)
var (
flagConfig = flag . StringP ( "config" , "c" , "config.toml" , "Path to config file" )
flagAddUser = flag . StringP ( "add" , "a" , "" , "Username of account to add" )
flagDeleteUser = flag . StringP ( "deleteuser" , "d" , "" , "Username of account to delete" )
flagCheckAuthorised = flag . StringP ( "validatecredentials" , "v" , "" , "Username of account to check" )
flagListUsers = flag . BoolP ( "listusers" , "l" , false , "List all users" )
config Config
req = make ( chan struct { } )
res = make ( chan [ ] project . Project )
manualRefresh = make ( chan struct { } )
)
func main ( ) {
flag . Parse ( )
err := checkConfig ( )
if err != nil {
log . Fatalln ( err )
}
fmt . Println ( "Opening database at" , config . DBConn )
dbConn , err := db . Open ( config . DBConn )
if err != nil {
fmt . Println ( "Error opening database:" , err )
os . Exit ( 1 )
}
2023-12-22 22:59:19 +00:00
fmt . Println ( "Checking whether database needs initialising" )
err = db . InitialiseDatabase ( dbConn )
2023-10-25 04:14:32 +00:00
if err != nil {
2023-12-22 22:59:19 +00:00
fmt . Println ( "Error initialising database:" , err )
os . Exit ( 1 )
}
fmt . Println ( "Checking whether there are pending migrations" )
err = db . Migrate ( dbConn )
if err != nil {
fmt . Println ( "Error migrating database schema:" , err )
os . Exit ( 1 )
2023-10-25 04:14:32 +00:00
}
if len ( * flagAddUser ) > 0 && len ( * flagDeleteUser ) == 0 && ! * flagListUsers && len ( * flagCheckAuthorised ) == 0 {
createUser ( dbConn , * flagAddUser )
os . Exit ( 0 )
} else if len ( * flagAddUser ) == 0 && len ( * flagDeleteUser ) > 0 && ! * flagListUsers && len ( * flagCheckAuthorised ) == 0 {
deleteUser ( dbConn , * flagDeleteUser )
os . Exit ( 0 )
} else if len ( * flagAddUser ) == 0 && len ( * flagDeleteUser ) == 0 && * flagListUsers && len ( * flagCheckAuthorised ) == 0 {
listUsers ( dbConn )
os . Exit ( 0 )
} else if len ( * flagAddUser ) == 0 && len ( * flagDeleteUser ) == 0 && ! * flagListUsers && len ( * flagCheckAuthorised ) > 0 {
checkAuthorised ( dbConn , * flagCheckAuthorised )
os . Exit ( 0 )
}
2024-02-23 23:06:16 +00:00
mu := sync . Mutex { }
2023-10-25 04:14:32 +00:00
2024-02-23 23:06:16 +00:00
fmt . Println ( "Starting refresh loop" )
go project . RefreshLoop ( dbConn , & mu , config . FetchInterval , & manualRefresh , & req , & res )
2023-10-25 04:14:32 +00:00
wsHandler := ws . Handler {
DbConn : dbConn ,
Req : & req ,
Res : & res ,
ManualRefresh : & manualRefresh ,
2024-02-23 23:06:16 +00:00
Mu : & mu ,
2023-10-25 04:14:32 +00:00
}
mux := http . NewServeMux ( )
2023-10-28 17:02:15 +00:00
mux . HandleFunc ( "/static/" , ws . StaticHandler )
2023-10-25 04:14:32 +00:00
mux . HandleFunc ( "/new" , wsHandler . NewHandler )
mux . HandleFunc ( "/login" , wsHandler . LoginHandler )
2023-10-26 00:16:36 +00:00
mux . HandleFunc ( "/logout" , wsHandler . LogoutHandler )
2023-10-28 17:02:15 +00:00
mux . HandleFunc ( "/" , wsHandler . RootHandler )
2023-10-25 04:14:32 +00:00
httpServer := & http . Server {
Addr : config . Server . Listen ,
Handler : mux ,
}
fmt . Println ( "Starting web server on" , config . Server . Listen )
if err := httpServer . ListenAndServe ( ) ; errors . Is ( err , http . ErrServerClosed ) {
fmt . Println ( "Web server closed" )
os . Exit ( 0 )
} else {
fmt . Println ( err )
os . Exit ( 1 )
}
}
func checkConfig ( ) error {
2024-02-23 21:29:35 +00:00
defaultDBConn := "willow.sqlite"
defaultFetchInterval := 3600
defaultListen := "127.0.0.1:1313"
defaultConfig := fmt . Sprintf ( ` # Path to SQLite database
DBConn = "%s"
# How often to fetch new releases in seconds
# # Minimum is % ds to avoid rate limits and unintentional abuse
FetchInterval = % d
[ Server ]
# Address to listen on
Listen = "%s" ` , defaultDBConn , defaultFetchInterval , defaultFetchInterval , defaultListen )
2023-10-25 04:14:32 +00:00
file , err := os . Open ( * flagConfig )
if err != nil {
if os . IsNotExist ( err ) {
file , err = os . Create ( * flagConfig )
if err != nil {
return err
}
defer file . Close ( )
2024-02-23 21:29:35 +00:00
_ , err = file . WriteString ( defaultConfig )
2023-10-25 04:14:32 +00:00
if err != nil {
return err
}
fmt . Println ( "Config file created at" , * flagConfig )
fmt . Println ( "Please edit it and restart the server" )
os . Exit ( 0 )
} else {
return err
}
}
defer file . Close ( )
_ , err = toml . DecodeFile ( * flagConfig , & config )
if err != nil {
return err
}
2024-02-23 21:29:35 +00:00
if config . FetchInterval < defaultFetchInterval {
fmt . Println ( "Fetch interval is set to" , strconv . Itoa ( config . FetchInterval ) , "seconds, but the minimum is" , defaultFetchInterval , "seconds, using" , strconv . Itoa ( defaultFetchInterval ) + "s" )
config . FetchInterval = defaultFetchInterval
2023-10-25 04:14:32 +00:00
}
if config . Server . Listen == "" {
2024-02-23 21:29:35 +00:00
fmt . Println ( "No listen address specified, using" , defaultListen )
config . Server . Listen = defaultListen
2023-10-25 04:14:32 +00:00
}
if config . DBConn == "" {
2024-02-23 21:29:35 +00:00
fmt . Println ( "No SQLite path specified, using \"" + defaultDBConn + "\"" )
config . DBConn = defaultDBConn
2023-10-25 04:14:32 +00:00
}
return nil
}