diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index a882442..0000000 --- a/.editorconfig +++ /dev/null @@ -1,7 +0,0 @@ -root = true - -[*] -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 4 diff --git a/.gitignore b/.gitignore index 2e63958..9bdf788 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,3 @@ docker-compose*.yml !docker-compose.example.yml config*.toml arimelody-web -arimelody-web.tar.gz diff --git a/Makefile b/Makefile deleted file mode 100644 index 11e565a..0000000 --- a/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -EXEC = arimelody-web - -.PHONY: $(EXEC) - -$(EXEC): - GOOS=linux GOARCH=amd64 go build -o $(EXEC) - -bundle: $(EXEC) - tar czf $(EXEC).tar.gz $(EXEC) admin/components/ admin/views/ admin/static/ views/ public/ schema-migration/ - -clean: - rm $(EXEC) $(EXEC).tar.gz diff --git a/README.md b/README.md index f1fd392..e5df7f6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ -# ari melody website +# arimelody.me home to your local SPACEGIRL! 💫 --- -a slightly-overcomplicated webserver built to show off everything i've worked -on, and then some! this server comes complete with twitch live status tracking, -a portfolio database, and a full-fledged admin CMS panel to manage it all! +built up from the initial [static](https://git.arimelody.me/ari/arimelody.me-static) +branch, this powerful, server-side rendered version comes complete with live +updates, powered by a new database and handy admin panel! + +the admin panel currently facilitates live updating of my music discography, +though i plan to expand it towards art portfolio and blog posts in the future. +if all goes well, i'd like to later separate these components into their own +library for others to use in their own sites. exciting stuff! ## build @@ -42,6 +47,3 @@ need to be up for this, making this ideal for some offline maintenance. - `purgeInvites`: Deletes all available invite codes. - `listAccounts`: Lists all active accounts. - `deleteAccount `: Deletes an account with a given `username`. -- `lockAccount `: Locks the account under `username`. -- `unlockAccount `: Unlocks the account under `username`. -- `logs`: Shows system logs. diff --git a/admin/accounthttp.go b/admin/accounthttp.go index 945a507..fc03d77 100644 --- a/admin/accounthttp.go +++ b/admin/accounthttp.go @@ -1,17 +1,17 @@ package admin import ( - "database/sql" - "fmt" - "net/http" - "net/url" - "os" + "database/sql" + "fmt" + "net/http" + "net/url" + "os" - "arimelody-web/controller" - "arimelody-web/log" - "arimelody-web/model" + "arimelody-web/controller" + "arimelody-web/log" + "arimelody-web/model" - "golang.org/x/crypto/bcrypt" + "golang.org/x/crypto/bcrypt" ) func accountHandler(app *model.AppState) http.Handler { @@ -115,7 +115,7 @@ func changePasswordHandler(app *model.AppState) http.Handler { return } - app.Log.Info(log.TYPE_ACCOUNT, "\"%s\" changed password by user request. (%s)", session.Account.Username, controller.ResolveIP(app, r)) + app.Log.Info(log.TYPE_ACCOUNT, "\"%s\" changed password by user request. (%s)", session.Account.Username, controller.ResolveIP(r)) controller.SetSessionError(app.DB, session, "") controller.SetSessionMessage(app.DB, session, "Password updated successfully.") @@ -145,7 +145,7 @@ func deleteAccountHandler(app *model.AppState) http.Handler { // check password if err := bcrypt.CompareHashAndPassword([]byte(session.Account.Password), []byte(r.Form.Get("password"))); err != nil { - app.Log.Warn(log.TYPE_ACCOUNT, "Account \"%s\" attempted account deletion with incorrect password. (%s)", session.Account.Username, controller.ResolveIP(app, r)) + app.Log.Warn(log.TYPE_ACCOUNT, "Account \"%s\" attempted account deletion with incorrect password. (%s)", session.Account.Username, controller.ResolveIP(r)) controller.SetSessionError(app.DB, session, "Incorrect password.") http.Redirect(w, r, "/admin/account", http.StatusFound) return @@ -159,7 +159,7 @@ func deleteAccountHandler(app *model.AppState) http.Handler { return } - app.Log.Info(log.TYPE_ACCOUNT, "Account \"%s\" deleted by user request. (%s)", session.Account.Username, controller.ResolveIP(app, r)) + app.Log.Info(log.TYPE_ACCOUNT, "Account \"%s\" deleted by user request. (%s)", session.Account.Username, controller.ResolveIP(r)) controller.SetSessionAccount(app.DB, session, nil) controller.SetSessionError(app.DB, session, "") diff --git a/admin/artisthttp.go b/admin/artisthttp.go index 9fa6bb2..6dfbbfd 100644 --- a/admin/artisthttp.go +++ b/admin/artisthttp.go @@ -1,9 +1,9 @@ package admin import ( - "fmt" - "net/http" - "strings" + "fmt" + "net/http" + "strings" "arimelody-web/model" "arimelody-web/controller" diff --git a/admin/components/release/release-list-item.html b/admin/components/release/release-list-item.html index 4b8f41e..677318d 100644 --- a/admin/components/release/release-list-item.html +++ b/admin/components/release/release-list-item.html @@ -7,7 +7,7 @@

{{.Title}} - {{.ReleaseDate.Year}} + {{.GetReleaseYear}} {{if not .Visible}}(hidden){{end}}

diff --git a/admin/http.go b/admin/http.go index 245a152..c70dd1d 100644 --- a/admin/http.go +++ b/admin/http.go @@ -1,20 +1,20 @@ package admin import ( - "context" - "database/sql" - "fmt" - "net/http" - "os" - "path/filepath" - "strings" - "time" + "context" + "database/sql" + "fmt" + "net/http" + "os" + "path/filepath" + "strings" + "time" - "arimelody-web/controller" - "arimelody-web/log" - "arimelody-web/model" + "arimelody-web/controller" + "arimelody-web/log" + "arimelody-web/model" - "golang.org/x/crypto/bcrypt" + "golang.org/x/crypto/bcrypt" ) func Handler(app *model.AppState) http.Handler { @@ -201,7 +201,7 @@ func registerAccountHandler(app *model.AppState) http.Handler { return } - app.Log.Info(log.TYPE_ACCOUNT, "Account \"%s\" (%s) created using invite \"%s\". (%s)", account.Username, account.ID, invite.Code, controller.ResolveIP(app, r)) + app.Log.Info(log.TYPE_ACCOUNT, "Account \"%s\" (%s) created using invite \"%s\". (%s)", account.Username, account.ID, invite.Code, controller.ResolveIP(r)) err = controller.DeleteInvite(app.DB, invite.Code) if err != nil { @@ -274,20 +274,11 @@ func loginHandler(app *model.AppState) http.Handler { render() return } - if account.Locked { - controller.SetSessionError(app.DB, session, "This account is locked.") - render() - return - } err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(password)) if err != nil { - app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" attempted login with incorrect password. (%s)", account.Username, controller.ResolveIP(app, r)) - if locked := handleFailedLogin(app, account, r); locked { - controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.") - } else { - controller.SetSessionError(app.DB, session, "Invalid username or password.") - } + app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" attempted login with incorrect password. (%s)", account.Username, controller.ResolveIP(r)) + controller.SetSessionError(app.DB, session, "Invalid username or password.") render() return } @@ -308,15 +299,13 @@ func loginHandler(app *model.AppState) http.Handler { render() return } - controller.SetSessionMessage(app.DB, session, "") - controller.SetSessionError(app.DB, session, "") http.Redirect(w, r, "/admin/totp", http.StatusFound) return } // login success! // TODO: log login activity to user - app.Log.Info(log.TYPE_ACCOUNT, "\"%s\" logged in. (%s)", account.Username, controller.ResolveIP(app, r)) + app.Log.Info(log.TYPE_ACCOUNT, "\"%s\" logged in. (%s)", account.Username, controller.ResolveIP(r)) app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" does not have any TOTP methods assigned.", account.Username) err = controller.SetSessionAccount(app.DB, session, account) @@ -374,7 +363,7 @@ func loginTOTPHandler(app *model.AppState) http.Handler { totpCode := r.FormValue("totp") if len(totpCode) != controller.TOTP_CODE_LENGTH { - app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" failed login (Invalid TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(app, r)) + app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" failed login (Invalid TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(r)) controller.SetSessionError(app.DB, session, "Invalid TOTP.") render() return @@ -388,19 +377,13 @@ func loginTOTPHandler(app *model.AppState) http.Handler { return } if totpMethod == nil { - app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" failed login (Incorrect TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(app, r)) - if locked := handleFailedLogin(app, session.AttemptAccount, r); locked { - controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.") - controller.SetSessionAttemptAccount(app.DB, session, nil) - http.Redirect(w, r, "/admin", http.StatusFound) - } else { - controller.SetSessionError(app.DB, session, "Incorrect TOTP.") - } + app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" failed login (Invalid TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(r)) + controller.SetSessionError(app.DB, session, "Invalid TOTP.") render() return } - app.Log.Info(log.TYPE_ACCOUNT, "\"%s\" logged in with TOTP method \"%s\". (%s)", session.AttemptAccount.Username, totpMethod.Name, controller.ResolveIP(app, r)) + app.Log.Info(log.TYPE_ACCOUNT, "\"%s\" logged in with TOTP method \"%s\". (%s)", session.AttemptAccount.Username, totpMethod.Name, controller.ResolveIP(r)) err = controller.SetSessionAccount(app.DB, session, session.AttemptAccount) if err != nil { @@ -483,7 +466,7 @@ func staticHandler() http.Handler { func enforceSession(app *model.AppState, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session, err := controller.GetSessionFromRequest(app, r) + session, err := controller.GetSessionFromRequest(app.DB, r) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve session: %v\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -513,30 +496,3 @@ func enforceSession(app *model.AppState, next http.Handler) http.Handler { next.ServeHTTP(w, r.WithContext(ctx)) }) } - -func handleFailedLogin(app *model.AppState, account *model.Account, r *http.Request) bool { - locked, err := controller.IncrementAccountFails(app.DB, account.ID) - if err != nil { - fmt.Fprintf( - os.Stderr, - "WARN: Failed to increment login failures for \"%s\": %v\n", - account.Username, - err, - ) - app.Log.Warn( - log.TYPE_ACCOUNT, - "Failed to increment login failures for \"%s\"", - account.Username, - ) - } - if locked { - app.Log.Warn( - log.TYPE_ACCOUNT, - "Account \"%s\" was locked: %d failed login attempts (IP: %s)", - account.Username, - model.MAX_LOGIN_FAIL_ATTEMPTS, - controller.ResolveIP(app, r), - ) - } - return locked -} diff --git a/admin/logshttp.go b/admin/logshttp.go index 7249b16..93dc5b7 100644 --- a/admin/logshttp.go +++ b/admin/logshttp.go @@ -1,12 +1,12 @@ package admin import ( - "arimelody-web/log" - "arimelody-web/model" - "fmt" - "net/http" - "os" - "strings" + "arimelody-web/log" + "arimelody-web/model" + "fmt" + "net/http" + "os" + "strings" ) func logsHandler(app *model.AppState) http.Handler { diff --git a/admin/releasehttp.go b/admin/releasehttp.go index c6b68ab..7ef4d37 100644 --- a/admin/releasehttp.go +++ b/admin/releasehttp.go @@ -1,12 +1,12 @@ package admin import ( - "fmt" - "net/http" - "strings" + "fmt" + "net/http" + "strings" - "arimelody-web/controller" - "arimelody-web/model" + "arimelody-web/controller" + "arimelody-web/model" ) func serveRelease(app *model.AppState) http.Handler { diff --git a/admin/static/logs.css b/admin/static/logs.css index f0df299..6ed91b5 100644 --- a/admin/static/logs.css +++ b/admin/static/logs.css @@ -71,7 +71,6 @@ th.log-type { td.log-content, td.log-content { width: 100%; - white-space: collapse; } .log:hover { diff --git a/admin/templates.go b/admin/templates.go index 606d569..12cdf08 100644 --- a/admin/templates.go +++ b/admin/templates.go @@ -1,12 +1,12 @@ package admin import ( - "arimelody-web/log" - "fmt" - "html/template" - "path/filepath" - "strings" - "time" + "arimelody-web/log" + "fmt" + "html/template" + "path/filepath" + "strings" + "time" ) var indexTemplate = template.Must(template.ParseFiles( diff --git a/admin/trackhttp.go b/admin/trackhttp.go index 93eacdb..a92f81a 100644 --- a/admin/trackhttp.go +++ b/admin/trackhttp.go @@ -1,9 +1,9 @@ package admin import ( - "fmt" - "net/http" - "strings" + "fmt" + "net/http" + "strings" "arimelody-web/model" "arimelody-web/controller" diff --git a/admin/views/layout.html b/admin/views/layout.html index fdeda9b..52b0620 100644 --- a/admin/views/layout.html +++ b/admin/views/layout.html @@ -18,7 +18,7 @@