HEAVY: migrate accounts and logs to service/repo architecture
This commit is contained in:
parent
5c255fb34b
commit
49e14b5bc5
37 changed files with 1019 additions and 687 deletions
321
main.go
321
main.go
|
|
@ -5,6 +5,7 @@ import (
|
|||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
stdLog "log"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
|
@ -21,8 +22,13 @@ import (
|
|||
"arimelody-web/colour"
|
||||
"arimelody-web/controller"
|
||||
"arimelody-web/cursor"
|
||||
"arimelody-web/log"
|
||||
"arimelody-web/model"
|
||||
"arimelody-web/model/app"
|
||||
accountRepo "arimelody-web/repository/account"
|
||||
logRepo "arimelody-web/repository/log"
|
||||
repo "arimelody-web/repository/postgres"
|
||||
accountService "arimelody-web/service/account"
|
||||
logService "arimelody-web/service/log"
|
||||
"arimelody-web/view"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
|
@ -35,14 +41,17 @@ const DB_VERSION = 1
|
|||
|
||||
const DEFAULT_PORT int64 = 8080
|
||||
const HRT_DATE int64 = 1756478697
|
||||
const DEFAULT_LOG_FLAGS = log.Ldate | log.Ltime | log.Lmicroseconds
|
||||
|
||||
//go:embed "public"
|
||||
var publicFS embed.FS
|
||||
|
||||
func main() {
|
||||
fmt.Printf("made with <3 by ari melody\n\n")
|
||||
logger := log.New(os.Stderr, "main", DEFAULT_LOG_FLAGS)
|
||||
|
||||
app := model.AppState{
|
||||
logger.Print("made with <3 by ari melody\n\n")
|
||||
|
||||
app := app.AppState{
|
||||
Config: controller.GetConfig(),
|
||||
Twitch: nil,
|
||||
PublicFS: publicFS,
|
||||
|
|
@ -50,44 +59,49 @@ func main() {
|
|||
|
||||
// initialise database connection
|
||||
if app.Config.DB.Host == "" {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: db.host not provided! Exiting...\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: db.host not provided! Exiting...\n")
|
||||
}
|
||||
if app.Config.DB.Name == "" {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: db.name not provided! Exiting...\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: db.name not provided! Exiting...\n")
|
||||
}
|
||||
if app.Config.DB.User == "" {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: db.user not provided! Exiting...\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: db.user not provided! Exiting...\n")
|
||||
}
|
||||
if app.Config.DB.Pass == "" {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: db.pass not provided! Exiting...\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: db.pass not provided! Exiting...\n")
|
||||
}
|
||||
|
||||
var err error
|
||||
app.DB, err = sqlx.Connect(
|
||||
psqlDB, err := sqlx.Connect(
|
||||
"postgres",
|
||||
fmt.Sprintf(
|
||||
"host=%s port=%d user=%s dbname=%s password='%s' sslmode=disable",
|
||||
"host=%s port=%d user=%s password='%s' dbname=%s sslmode=disable",
|
||||
app.Config.DB.Host,
|
||||
app.Config.DB.Port,
|
||||
app.Config.DB.User,
|
||||
app.Config.DB.Name,
|
||||
app.Config.DB.Pass,
|
||||
app.Config.DB.Name,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Unable to initialise database: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
app.DB.SetConnMaxLifetime(time.Minute * 3)
|
||||
app.DB.SetMaxOpenConns(10)
|
||||
app.DB.SetMaxIdleConns(10)
|
||||
defer app.DB.Close()
|
||||
defer psqlDB.Close()
|
||||
psqlDB.SetConnMaxLifetime(time.Minute * 3)
|
||||
psqlDB.SetMaxOpenConns(10)
|
||||
psqlDB.SetMaxIdleConns(10)
|
||||
app.DB = psqlDB
|
||||
|
||||
app.Log = log.Logger{ DB: app.DB }
|
||||
logRepo := logRepo.NewLogRepositoryPostgres(psqlDB)
|
||||
app.Log = logService.NewLogService(
|
||||
logRepo,
|
||||
log.New(os.Stderr, "logger", DEFAULT_LOG_FLAGS),
|
||||
)
|
||||
|
||||
accountRepo := accountRepo.NewAccountRepositoryPostgres(psqlDB)
|
||||
app.AccountService = accountService.NewAccountService(
|
||||
accountRepo,
|
||||
log.New(os.Stderr, "account-repo", DEFAULT_LOG_FLAGS),
|
||||
)
|
||||
|
||||
// handle command arguments
|
||||
if len(os.Args) > 1 {
|
||||
|
|
@ -96,21 +110,18 @@ func main() {
|
|||
switch arg {
|
||||
case "createTOTP":
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` and `name` must be specified for createTOTP.\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` and `name` must be specified for createTOTP.\n")
|
||||
}
|
||||
username := os.Args[2]
|
||||
totpName := os.Args[3]
|
||||
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
secret := controller.GenerateTOTPSecret(controller.TOTP_SECRET_LENGTH)
|
||||
|
|
@ -123,133 +134,116 @@ func main() {
|
|||
err = controller.CreateTOTP(app.DB, &totp)
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "pq: duplicate key") {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" already has a TOTP method named \"%s\"!\n", account.Username, totp.Name)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" already has a TOTP method named \"%s\"!\n", account.Username, totp.Name)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to create TOTP method: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to create TOTP method: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "TOTP method \"%s\" for \"%s\" created via config utility.", totp.Name, account.Username)
|
||||
app.Log.Info(model.LOG_ACCOUNT, "TOTP method \"%s\" for \"%s\" created via config utility.", totp.Name, account.Username)
|
||||
url := controller.GenerateTOTPURI(account.Username, totp.Secret)
|
||||
fmt.Printf("%s\n", url)
|
||||
logger.Printf("%s\n", url)
|
||||
return
|
||||
|
||||
case "deleteTOTP":
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` and `name` must be specified for deleteTOTP.\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` and `name` must be specified for deleteTOTP.\n")
|
||||
}
|
||||
username := os.Args[2]
|
||||
totpName := os.Args[3]
|
||||
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
err = controller.DeleteTOTP(app.DB, account.ID, totpName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to create TOTP method: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to create TOTP method: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "TOTP method \"%s\" for \"%s\" deleted via config utility.", totpName, account.Username)
|
||||
fmt.Printf("TOTP method \"%s\" deleted.\n", totpName)
|
||||
app.Log.Info(model.LOG_ACCOUNT, "TOTP method \"%s\" for \"%s\" deleted via config utility.", totpName, account.Username)
|
||||
logger.Printf("TOTP method \"%s\" deleted.\n", totpName)
|
||||
return
|
||||
|
||||
case "listTOTP":
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` must be specified for listTOTP.\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` must be specified for listTOTP.\n")
|
||||
}
|
||||
username := os.Args[2]
|
||||
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
totps, err := controller.GetTOTPsForAccount(app.DB, account.ID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to create TOTP methods: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to create TOTP methods: %v\n", err)
|
||||
}
|
||||
|
||||
for i, totp := range totps {
|
||||
fmt.Printf("%d. %s - Created %s\n", i + 1, totp.Name, totp.CreatedAt)
|
||||
logger.Printf("%d. %s - Created %s\n", i + 1, totp.Name, totp.CreatedAt)
|
||||
}
|
||||
if len(totps) == 0 {
|
||||
fmt.Printf("\"%s\" has no TOTP methods.\n", account.Username)
|
||||
logger.Printf("\"%s\" has no TOTP methods.\n", account.Username)
|
||||
}
|
||||
return
|
||||
|
||||
case "testTOTP":
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` and `name` must be specified for testTOTP.\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` and `name` must be specified for testTOTP.\n")
|
||||
}
|
||||
username := os.Args[2]
|
||||
totpName := os.Args[3]
|
||||
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
totp, err := controller.GetTOTP(app.DB, account.ID, totpName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch TOTP method \"%s\": %v\n", totpName, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch TOTP method \"%s\": %v\n", totpName, err)
|
||||
}
|
||||
|
||||
if totp == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: TOTP method \"%s\" does not exist for account \"%s\"\n", totpName, username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: TOTP method \"%s\" does not exist for account \"%s\"\n", totpName, username)
|
||||
}
|
||||
|
||||
code := controller.GenerateTOTP(totp.Secret, 0)
|
||||
fmt.Printf("%s\n", code)
|
||||
logger.Printf("%s\n", code)
|
||||
return
|
||||
|
||||
case "cleanTOTP":
|
||||
err := controller.DeleteUnconfirmedTOTPs(app.DB)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to clean up TOTP methods: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to clean up TOTP methods: %v\n", err)
|
||||
}
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "TOTP methods pruned via config utility.")
|
||||
fmt.Printf("Cleaned up dangling TOTP methods successfully.\n")
|
||||
app.Log.Info(model.LOG_ACCOUNT, "TOTP methods pruned via config utility.")
|
||||
logger.Printf("Cleaned up dangling TOTP methods successfully.\n")
|
||||
return
|
||||
|
||||
case "createInvite":
|
||||
fmt.Printf("Creating invite...\n")
|
||||
logger.Printf("Creating invite...\n")
|
||||
invite, err := controller.CreateInvite(app.DB, 16, time.Hour * 24)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to create invite code: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to create invite code: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "Invite generted via config utility (%s).", invite.Code)
|
||||
fmt.Printf(
|
||||
app.Log.Info(model.LOG_ACCOUNT, "Invite generted via config utility (%s).", invite.Code)
|
||||
logger.Printf(
|
||||
"Here you go! This code expires in %d hours: %s\n",
|
||||
int(math.Ceil(invite.ExpiresAt.Sub(invite.CreatedAt).Hours())),
|
||||
invite.Code,
|
||||
|
|
@ -257,28 +251,26 @@ func main() {
|
|||
return
|
||||
|
||||
case "purgeInvites":
|
||||
fmt.Printf("Deleting all invites...\n")
|
||||
logger.Printf("Deleting all invites...\n")
|
||||
err := controller.DeleteAllInvites(app.DB)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to delete invites: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to delete invites: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "Invites purged via config utility.")
|
||||
fmt.Printf("Invites deleted successfully.\n")
|
||||
app.Log.Info(model.LOG_ACCOUNT, "Invites purged via config utility.")
|
||||
logger.Printf("Invites deleted successfully.\n")
|
||||
return
|
||||
|
||||
case "listAccounts":
|
||||
accounts, err := controller.GetAllAccounts(app.DB)
|
||||
accounts, err := app.AccountService.GetAll()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch accounts: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch accounts: %v\n", err)
|
||||
}
|
||||
|
||||
for _, account := range accounts {
|
||||
email := "<none>"
|
||||
if account.Email.Valid { email = account.Email.String }
|
||||
fmt.Printf(
|
||||
logger.Printf(
|
||||
"User: %s\n" +
|
||||
"\tID: %s\n" +
|
||||
"\tEmail: %s\n" +
|
||||
|
|
@ -295,150 +287,140 @@ func main() {
|
|||
|
||||
case "changePassword":
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` and `password` must be specified for changePassword\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` and `password` must be specified for changePassword\n")
|
||||
}
|
||||
|
||||
username := os.Args[2]
|
||||
password := os.Args[3]
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to update password: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to update password: %v\n", err)
|
||||
}
|
||||
account.Password = string(hashedPassword)
|
||||
err = controller.UpdateAccount(app.DB, account)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to update password: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
||||
var email *string = nil
|
||||
if account.Email.Valid { email = &account.Email.String }
|
||||
var avatarURL *string = nil
|
||||
if account.AvatarURL.Valid { email = &account.AvatarURL.String }
|
||||
if err = app.AccountService.Update(
|
||||
account.ID,
|
||||
username, string(hashedPassword),
|
||||
email, avatarURL,
|
||||
); err != nil {
|
||||
logger.Fatalf("FATAL: Failed to update password: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "Password for '%s' updated via config utility.", account.Username)
|
||||
fmt.Printf("Password for \"%s\" updated successfully.\n", account.Username)
|
||||
app.Log.Info(model.LOG_ACCOUNT, "Password for '%s' updated via config utility.", account.Username)
|
||||
logger.Printf("Password for \"%s\" updated successfully.\n", account.Username)
|
||||
return
|
||||
|
||||
case "deleteAccount":
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` must be specified for deleteAccount\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` must be specified for deleteAccount\n")
|
||||
}
|
||||
username := os.Args[2]
|
||||
fmt.Printf("Deleting account \"%s\"...\n", username)
|
||||
logger.Printf("Deleting account \"%s\"...\n", username)
|
||||
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
fmt.Printf("You are about to delete \"%s\". Are you sure? (y/[N]): ", account.Username)
|
||||
logger.Printf("You are about to delete \"%s\". Are you sure? (y/[N]): ", account.Username)
|
||||
res := ""
|
||||
fmt.Scanln(&res)
|
||||
if !strings.HasPrefix(res, "y") {
|
||||
return
|
||||
}
|
||||
|
||||
err = controller.DeleteAccount(app.DB, account.ID)
|
||||
err = app.AccountService.Delete(account.ID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to delete account: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to delete account: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "Account '%s' deleted via config utility.", account.Username)
|
||||
fmt.Printf("Account \"%s\" deleted successfully.\n", account.Username)
|
||||
app.Log.Info(model.LOG_ACCOUNT, "Account '%s' deleted via config utility.", account.Username)
|
||||
logger.Printf("Account \"%s\" deleted successfully.\n", account.Username)
|
||||
return
|
||||
|
||||
case "lockAccount":
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` must be specified for lockAccount\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` must be specified for lockAccount\n")
|
||||
}
|
||||
username := os.Args[2]
|
||||
fmt.Printf("Unlocking account \"%s\"...\n", username)
|
||||
logger.Printf("Unlocking account \"%s\"...\n", username)
|
||||
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
err = controller.LockAccount(app.DB, account.ID)
|
||||
err = app.AccountService.Lock(account.ID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to lock account: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to lock account: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "Account '%s' locked via config utility.", account.Username)
|
||||
fmt.Printf("Account \"%s\" locked successfully.\n", account.Username)
|
||||
app.Log.Info(model.LOG_ACCOUNT, "Account '%s' locked via config utility.", account.Username)
|
||||
logger.Printf("Account \"%s\" locked successfully.\n", account.Username)
|
||||
return
|
||||
|
||||
case "unlockAccount":
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: `username` must be specified for unlockAccount\n")
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: `username` must be specified for unlockAccount\n")
|
||||
}
|
||||
username := os.Args[2]
|
||||
fmt.Printf("Unlocking account \"%s\"...\n", username)
|
||||
logger.Printf("Unlocking account \"%s\"...\n", username)
|
||||
|
||||
account, err := controller.GetAccountByUsername(app.DB, username)
|
||||
account, err := app.AccountService.GetByUsername(username)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch account \"%s\": %v\n", username, err)
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Account \"%s\" does not exist.\n", username)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Account \"%s\" does not exist.\n", username)
|
||||
}
|
||||
|
||||
err = controller.UnlockAccount(app.DB, account.ID)
|
||||
err = app.AccountService.Unlock(account.ID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to unlock account: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to unlock account: %v\n", err)
|
||||
}
|
||||
|
||||
app.Log.Info(log.TYPE_ACCOUNT, "Account '%s' unlocked via config utility.", account.Username)
|
||||
fmt.Printf("Account \"%s\" unlocked successfully.\n", account.Username)
|
||||
app.Log.Info(model.LOG_ACCOUNT, "Account '%s' unlocked via config utility.", account.Username)
|
||||
logger.Printf("Account \"%s\" unlocked successfully.\n", account.Username)
|
||||
return
|
||||
|
||||
case "logs":
|
||||
// TODO: add log search parameters
|
||||
logs, err := app.Log.Search([]log.LogLevel{}, []string{}, "", 100, 0)
|
||||
logs, err := app.Log.Search([]model.LogLevel{}, []string{}, "", 100, 0)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to fetch logs: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to fetch logs: %v\n", err)
|
||||
}
|
||||
for _, item := range(logs) {
|
||||
levelStr := ""
|
||||
switch item.Level {
|
||||
case log.LEVEL_INFO:
|
||||
case model.LEVEL_INFO:
|
||||
levelStr = "INFO"
|
||||
case log.LEVEL_WARN:
|
||||
case model.LEVEL_WARN:
|
||||
levelStr = "WARN"
|
||||
default:
|
||||
levelStr = fmt.Sprintf("? (%d)", item.Level)
|
||||
}
|
||||
fmt.Printf("[%s] %s:\n\t[%s] %s: %s\n", item.CreatedAt.Format(time.UnixDate), item.ID, item.Type, levelStr, item.Content)
|
||||
logger.Printf("[%s] %s:\n\t[%s] %s: %s\n", item.CreatedAt.Format(time.UnixDate), item.ID, item.Type, levelStr, item.Content)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -464,68 +446,66 @@ func main() {
|
|||
}
|
||||
|
||||
// handle DB migrations
|
||||
controller.CheckDBVersionAndMigrate(app.DB)
|
||||
if psqlDB != nil {
|
||||
repo.CheckDBVersionAndMigrate(psqlDB)
|
||||
}
|
||||
|
||||
if app.Config.Twitch != nil {
|
||||
err = controller.TwitchSetup(&app)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to set up Twitch integration: %v\n", err)
|
||||
logger.Printf("WARN: Failed to set up Twitch integration: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// initial invite code
|
||||
accountsCount := 0
|
||||
err = app.DB.Get(&accountsCount, "SELECT count(*) FROM account")
|
||||
accountsCount, err := app.AccountService.GetCount()
|
||||
if err != nil { panic(err) }
|
||||
if accountsCount == 0 {
|
||||
_, err := app.DB.Exec("DELETE FROM invite")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to clear existing invite codes: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to clear existing invite codes: %v\n", err)
|
||||
}
|
||||
|
||||
invite, err := controller.CreateInvite(app.DB, 16, time.Hour * 24)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to create invite code: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to create invite code: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("No accounts exist! Generated invite code: %s\n", invite.Code)
|
||||
logger.Printf("No accounts exist! Generated invite code: %s\n", invite.Code)
|
||||
}
|
||||
|
||||
// delete expired sessions
|
||||
err = controller.DeleteExpiredSessions(app.DB)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to clear expired sessions: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to clear expired sessions: %v\n", err)
|
||||
}
|
||||
|
||||
// delete expired invites
|
||||
err = controller.DeleteExpiredInvites(app.DB)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to clear expired invite codes: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to clear expired invite codes: %v\n", err)
|
||||
}
|
||||
|
||||
// clean up unconfirmed TOTP methods
|
||||
err = controller.DeleteUnconfirmedTOTPs(app.DB)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to clean up unconfirmed TOTP methods: %v\n", err)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("FATAL: Failed to clean up unconfirmed TOTP methods: %v\n", err)
|
||||
}
|
||||
|
||||
go cursor.StartCursor(&app)
|
||||
|
||||
httpLogger := log.New(os.Stderr, "http", DEFAULT_LOG_FLAGS)
|
||||
|
||||
// start the web server!
|
||||
mux := createServeMux(&app)
|
||||
fmt.Printf("Now serving at http://%s:%d\n", app.Config.Host, app.Config.Port)
|
||||
logger.Printf("Now serving at http://%s:%d\n", app.Config.Host, app.Config.Port)
|
||||
stdLog.Fatal(
|
||||
http.ListenAndServe(fmt.Sprintf("%s:%d", app.Config.Host, app.Config.Port),
|
||||
CheckRequest(&app, HTTPLog(DefaultHeaders(mux))),
|
||||
CheckRequest(&app, httpLogger, HTTPLog(httpLogger, DefaultHeaders(mux))),
|
||||
))
|
||||
}
|
||||
|
||||
func createServeMux(app *model.AppState) *http.ServeMux {
|
||||
func createServeMux(app *app.AppState) *http.ServeMux {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle("/admin/", http.StripPrefix("/admin", admin.Handler(app)))
|
||||
|
|
@ -568,7 +548,7 @@ var PoweredByStrings = []string{
|
|||
"30 billion dollars in VC funding",
|
||||
}
|
||||
|
||||
func CheckRequest(app *model.AppState, next http.Handler) http.Handler {
|
||||
func CheckRequest(app *app.AppState, log *log.Logger, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// requests with empty user agents are considered suspicious.
|
||||
// every browser supplies them; hell, even curl supplies them.
|
||||
|
|
@ -585,8 +565,7 @@ func CheckRequest(app *model.AppState, next http.Handler) http.Handler {
|
|||
if strings.HasSuffix(r.URL.Path, ".php") ||
|
||||
strings.HasSuffix(r.URL.Path, ".php7") {
|
||||
http.NotFound(w, r)
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
log.Printf(
|
||||
"WARN: Suspicious activity blocked: {\"path\":\"%s\",\"address\":\"%s\"}\n",
|
||||
r.URL.Path,
|
||||
r.RemoteAddr,
|
||||
|
|
@ -636,7 +615,7 @@ func (lrw *LoggingResponseWriter) WriteHeader(status int) {
|
|||
lrw.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
|
||||
func HTTPLog(next http.Handler) http.Handler {
|
||||
func HTTPLog(log *log.Logger, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
|
|
@ -658,7 +637,7 @@ func HTTPLog(next http.Handler) http.Handler {
|
|||
if lrw.Status - 400 <= 0 { statusColour = colour.White }
|
||||
if lrw.Status - 300 <= 0 { statusColour = colour.Green }
|
||||
|
||||
fmt.Printf("[%s] %s %s - %s%d%s (%sms) (%s)\n",
|
||||
log.Printf("[%s] %s %s - %s%d%s (%sms) (%s)\n",
|
||||
after.Format(time.UnixDate),
|
||||
r.Method,
|
||||
r.URL.Path,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue