diff options
| author | Elizabeth <elizabeth@simponic.xyz> | 2024-04-09 18:39:14 -0400 |
|---|---|---|
| committer | simponic <simponic@hatecomputers.club> | 2024-04-09 18:39:14 -0400 |
| commit | 1d75bf7489527925217bd5611ba7910c0ffe077c (patch) | |
| tree | 3b6e6056912648a88e1e42c1e42ed7e58e2d4701 /api | |
| parent | ee49015cc90e6c136ad94243fffc9241b9506a36 (diff) | |
| download | hatecomputers.club-1d75bf7489527925217bd5611ba7910c0ffe077c.tar.gz hatecomputers.club-1d75bf7489527925217bd5611ba7910c0ffe077c.zip | |
profiles (#7)
Reviewed-on: https://git.hatecomputers.club/hatecomputers/hatecomputers.club/pulls/7
Co-authored-by: Elizabeth <elizabeth@simponic.xyz>
Co-committed-by: Elizabeth <elizabeth@simponic.xyz>
Diffstat (limited to 'api')
| -rw-r--r-- | api/auth/auth.go | 15 | ||||
| -rw-r--r-- | api/dns/dns.go | 88 | ||||
| -rw-r--r-- | api/dns/dns_test.go | 4 | ||||
| -rw-r--r-- | api/guestbook/guestbook.go | 66 | ||||
| -rw-r--r-- | api/hcaptcha/hcaptcha.go | 4 | ||||
| -rw-r--r-- | api/keys/keys.go | 20 | ||||
| -rw-r--r-- | api/profiles/profiles.go | 118 | ||||
| -rw-r--r-- | api/serve.go | 36 | ||||
| -rw-r--r-- | api/types/types.go | 4 |
9 files changed, 265 insertions, 90 deletions
diff --git a/api/auth/auth.go b/api/auth/auth.go index 0ffbf9c..04d6c12 100644 --- a/api/auth/auth.go +++ b/api/auth/auth.go @@ -18,6 +18,18 @@ import ( "golang.org/x/oauth2" ) +func ListUsersContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + users, err := database.ListUsers(context.DBConn) + if err != nil { + return failure(context, req, resp) + } + + (*context.TemplateData)["Users"] = users + return success(context, req, resp) + } +} + func StartSessionContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { verifier := utils.RandomId() + utils.RandomId() @@ -158,6 +170,7 @@ func VerifySessionContinuation(context *types.RequestContext, req *http.Request, func GoLoginContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + log.Println("GoLoginContinuation") http.SetCookie(resp, &http.Cookie{ Name: "redirect", Value: req.URL.Path, @@ -216,7 +229,7 @@ func getOauthUser(dbConn *sql.DB, client *http.Client, uri string) (*database.Us return nil, err } - user, err := database.FindOrSaveUser(dbConn, userStruct) + user, err := database.FindOrSaveBaseUser(dbConn, userStruct) if err != nil { return nil, err } diff --git a/api/dns/dns.go b/api/dns/dns.go index aa2f356..6357dfc 100644 --- a/api/dns/dns.go +++ b/api/dns/dns.go @@ -8,39 +8,15 @@ import ( "strconv" "strings" - "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters" + "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/external_dns" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/types" "git.hatecomputers.club/hatecomputers/hatecomputers.club/database" "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils" ) -func userCanFuckWithDNSRecord(dbConn *sql.DB, user *database.User, record *database.DNSRecord, ownedInternalDomainFormats []string) bool { - ownedByUser := (user.ID == record.UserID) - if !ownedByUser { - return false - } - - if !record.Internal { - for _, format := range ownedInternalDomainFormats { - domain := fmt.Sprintf(format, user.Username) - - isInSubDomain := strings.HasSuffix(record.Name, "."+domain) - if domain == record.Name || isInSubDomain { - return true - } - } - return false - } - - owner, err := database.FindFirstDomainOwnerId(dbConn, record.Name) - if err != nil { - log.Println(err) - return false - } +const MaxUserRecords = 100 - userIsOwnerOfDomain := owner == user.ID - return ownedByUser && userIsOwnerOfDomain -} +var UserOwnedInternalFmtDomains = []string{"%s", "%s.endpoints"} func ListDNSRecordsContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { @@ -59,8 +35,8 @@ func ListDNSRecordsContinuation(context *types.RequestContext, req *http.Request func CreateDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter, maxUserRecords int, allowedUserDomainFormats []string) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { - formErrors := types.FormError{ - Errors: []string{}, + formErrors := types.BannerMessages{ + Messages: []string{}, } internal := req.FormValue("internal") == "on" || req.FormValue("internal") == "true" @@ -77,7 +53,7 @@ func CreateDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter, max ttlNum, err := strconv.Atoi(ttl) if err != nil { resp.WriteHeader(http.StatusBadRequest) - formErrors.Errors = append(formErrors.Errors, "invalid ttl") + formErrors.Messages = append(formErrors.Messages, "invalid ttl") } dnsRecordCount, err := database.CountUserDNSRecords(context.DBConn, context.User.ID) @@ -88,7 +64,7 @@ func CreateDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter, max } if dnsRecordCount >= maxUserRecords { resp.WriteHeader(http.StatusTooManyRequests) - formErrors.Errors = append(formErrors.Errors, "max records reached") + formErrors.Messages = append(formErrors.Messages, "max records reached") } dnsRecord := &database.DNSRecord{ @@ -102,10 +78,10 @@ func CreateDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter, max if !userCanFuckWithDNSRecord(context.DBConn, context.User, dnsRecord, allowedUserDomainFormats) { resp.WriteHeader(http.StatusUnauthorized) - formErrors.Errors = append(formErrors.Errors, "'name' must end with "+context.User.Username+" or you must be a domain owner for internal domains") + formErrors.Messages = append(formErrors.Messages, "'name' must end with "+context.User.Username+" or you must be a domain owner for internal domains") } - if len(formErrors.Errors) == 0 { + if len(formErrors.Messages) == 0 { if dnsRecord.Internal { dnsRecord.ID = utils.RandomId() } else { @@ -113,24 +89,28 @@ func CreateDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter, max if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) - formErrors.Errors = append(formErrors.Errors, err.Error()) + formErrors.Messages = append(formErrors.Messages, err.Error()) } } } - if len(formErrors.Errors) == 0 { + if len(formErrors.Messages) == 0 { _, err := database.SaveDNSRecord(context.DBConn, dnsRecord) if err != nil { log.Println(err) - formErrors.Errors = append(formErrors.Errors, "error saving record") + formErrors.Messages = append(formErrors.Messages, "error saving record") } } - if len(formErrors.Errors) == 0 { + if len(formErrors.Messages) == 0 { + formSuccess := types.BannerMessages{ + Messages: []string{"record added."}, + } + (*context.TemplateData)["Success"] = formSuccess return success(context, req, resp) } - (*context.TemplateData)["FormError"] = &formErrors + (*context.TemplateData)[""] = &formErrors (*context.TemplateData)["RecordForm"] = dnsRecord return failure(context, req, resp) } @@ -168,7 +148,39 @@ func DeleteDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter) fun return failure(context, req, resp) } + formSuccess := types.BannerMessages{ + Messages: []string{"record deleted."}, + } + (*context.TemplateData)["Success"] = formSuccess return success(context, req, resp) } } } + +func userCanFuckWithDNSRecord(dbConn *sql.DB, user *database.User, record *database.DNSRecord, ownedInternalDomainFormats []string) bool { + ownedByUser := (user.ID == record.UserID) + if !ownedByUser { + return false + } + + if !record.Internal { + for _, format := range ownedInternalDomainFormats { + domain := fmt.Sprintf(format, user.Username) + + isInSubDomain := strings.HasSuffix(record.Name, "."+domain) + if domain == record.Name || isInSubDomain { + return true + } + } + return false + } + + owner, err := database.FindFirstDomainOwnerId(dbConn, record.Name) + if err != nil { + log.Println(err) + return false + } + + userIsOwnerOfDomain := owner == user.ID + return ownedByUser && userIsOwnerOfDomain +} diff --git a/api/dns/dns_test.go b/api/dns/dns_test.go index 43dc680..30baedf 100644 --- a/api/dns/dns_test.go +++ b/api/dns/dns_test.go @@ -39,7 +39,7 @@ func setup() (*sql.DB, *types.RequestContext, func()) { Mail: "test@test.com", DisplayName: "test", } - database.FindOrSaveUser(testDb, user) + database.FindOrSaveBaseUser(testDb, user) context := &types.RequestContext{ DBConn: testDb, @@ -246,7 +246,7 @@ func TestThatUserMustOwnRecordToRemove(t *testing.T) { defer testServer.Close() nonOwnerUser := &database.User{ID: "n/a", Username: "testuser"} - _, err := database.FindOrSaveUser(db, nonOwnerUser) + _, err := database.FindOrSaveBaseUser(db, nonOwnerUser) if err != nil { t.Error(err) } diff --git a/api/guestbook/guestbook.go b/api/guestbook/guestbook.go index 60a7b4b..c0c7892 100644 --- a/api/guestbook/guestbook.go +++ b/api/guestbook/guestbook.go @@ -10,37 +10,13 @@ import ( "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils" ) -func validateGuestbookEntry(entry *database.GuestbookEntry) []string { - errors := []string{} - - if entry.Name == "" { - errors = append(errors, "name is required") - } - - if entry.Message == "" { - errors = append(errors, "message is required") - } - - messageLength := len(entry.Message) - if messageLength > 500 { - errors = append(errors, "message cannot be longer than 500 characters") - } - - newLines := strings.Count(entry.Message, "\n") - if newLines > 10 { - errors = append(errors, "message cannot contain more than 10 new lines") - } - - return errors -} - func SignGuestbookContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { name := req.FormValue("name") message := req.FormValue("message") - formErrors := types.FormError{ - Errors: []string{}, + formErrors := types.BannerMessages{ + Messages: []string{}, } entry := &database.GuestbookEntry{ @@ -48,24 +24,28 @@ func SignGuestbookContinuation(context *types.RequestContext, req *http.Request, Name: name, Message: message, } - formErrors.Errors = append(formErrors.Errors, validateGuestbookEntry(entry)...) + formErrors.Messages = append(formErrors.Messages, validateGuestbookEntry(entry)...) - if len(formErrors.Errors) == 0 { + if len(formErrors.Messages) == 0 { _, err := database.SaveGuestbookEntry(context.DBConn, entry) if err != nil { log.Println(err) - formErrors.Errors = append(formErrors.Errors, "failed to save entry") + formErrors.Messages = append(formErrors.Messages, "failed to save entry") } } - if len(formErrors.Errors) > 0 { - (*context.TemplateData)["FormError"] = formErrors + if len(formErrors.Messages) > 0 { + (*context.TemplateData)["Error"] = formErrors (*context.TemplateData)["EntryForm"] = entry resp.WriteHeader(http.StatusBadRequest) return failure(context, req, resp) } + formSuccess := types.BannerMessages{ + Messages: []string{"entry added."}, + } + (*context.TemplateData)["Success"] = formSuccess return success(context, req, resp) } } @@ -83,3 +63,27 @@ func ListGuestbookContinuation(context *types.RequestContext, req *http.Request, return success(context, req, resp) } } + +func validateGuestbookEntry(entry *database.GuestbookEntry) []string { + errors := []string{} + + if entry.Name == "" { + errors = append(errors, "name is required") + } + + if entry.Message == "" { + errors = append(errors, "message is required") + } + + messageLength := len(entry.Message) + if messageLength > 500 { + errors = append(errors, "message cannot be longer than 500 characters") + } + + newLines := strings.Count(entry.Message, "\n") + if newLines > 10 { + errors = append(errors, "message cannot contain more than 10 new lines") + } + + return errors +} diff --git a/api/hcaptcha/hcaptcha.go b/api/hcaptcha/hcaptcha.go index 007190d..e8ea238 100644 --- a/api/hcaptcha/hcaptcha.go +++ b/api/hcaptcha/hcaptcha.go @@ -62,8 +62,8 @@ func CaptchaVerificationContinuation(context *types.RequestContext, req *http.Re err := verifyCaptcha(secretKey, hCaptchaResponse) if err != nil { - (*context.TemplateData)["FormError"] = types.FormError{ - Errors: []string{"hCaptcha verification failed"}, + (*context.TemplateData)["Error"] = types.BannerMessages{ + Messages: []string{"hCaptcha verification failed"}, } resp.WriteHeader(http.StatusBadRequest) diff --git a/api/keys/keys.go b/api/keys/keys.go index cef3f3c..7702f3d 100644 --- a/api/keys/keys.go +++ b/api/keys/keys.go @@ -27,8 +27,8 @@ func ListAPIKeysContinuation(context *types.RequestContext, req *http.Request, r func CreateAPIKeyContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { - formErrors := types.FormError{ - Errors: []string{}, + formErrors := types.BannerMessages{ + Messages: []string{}, } numKeys, err := database.CountUserAPIKeys(context.DBConn, context.User.ID) @@ -39,11 +39,11 @@ func CreateAPIKeyContinuation(context *types.RequestContext, req *http.Request, } if numKeys >= MAX_USER_API_KEYS { - formErrors.Errors = append(formErrors.Errors, "max types keys reached") + formErrors.Messages = append(formErrors.Messages, "max types keys reached") } - if len(formErrors.Errors) > 0 { - (*context.TemplateData)["FormError"] = formErrors + if len(formErrors.Messages) > 0 { + (*context.TemplateData)["Error"] = formErrors return failure(context, req, resp) } @@ -56,6 +56,11 @@ func CreateAPIKeyContinuation(context *types.RequestContext, req *http.Request, resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } + + formSuccess := types.BannerMessages{ + Messages: []string{"key created."}, + } + (*context.TemplateData)["Success"] = formSuccess return success(context, req, resp) } } @@ -82,6 +87,11 @@ func DeleteAPIKeyContinuation(context *types.RequestContext, req *http.Request, return failure(context, req, resp) } + formSuccess := types.BannerMessages{ + Messages: []string{"key deleted."}, + } + (*context.TemplateData)["Success"] = formSuccess + return success(context, req, resp) } } diff --git a/api/profiles/profiles.go b/api/profiles/profiles.go new file mode 100644 index 0000000..8e10e5f --- /dev/null +++ b/api/profiles/profiles.go @@ -0,0 +1,118 @@ +package profiles + +import ( + "log" + "net/http" + "strings" + + "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/files" + "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/types" + "git.hatecomputers.club/hatecomputers/hatecomputers.club/database" +) + +const MaxAvatarSize = 1024 * 1024 * 2 // 2MB +const AvatarPath = "avatars/" +const AvatarPrefix = "/uploads/avatars/" + +func GetProfileContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + if context.User == nil { + return failure(context, req, resp) + } + + (*context.TemplateData)["Profile"] = context.User + return success(context, req, resp) + } +} + +func UpdateProfileContinuation(fileAdapter files.FilesAdapter, maxAvatarSize int, avatarPath string, avatarPrefix string) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { + return func(success types.Continuation, failure types.Continuation) types.ContinuationChain { + formErrors := types.BannerMessages{ + Messages: []string{}, + } + + err := req.ParseMultipartForm(int64(maxAvatarSize)) + if err != nil { + formErrors.Messages = append(formErrors.Messages, "avatar file too large") + } + + if len(formErrors.Messages) == 0 { + file, _, err := req.FormFile("avatar") + if file != nil && err != nil { + formErrors.Messages = append(formErrors.Messages, "error uploading avatar") + } else if file != nil { + defer file.Close() + reader := http.MaxBytesReader(resp, file, int64(maxAvatarSize)) + defer reader.Close() + + _, err = fileAdapter.CreateFile(avatarPath+context.User.ID, reader) + if err != nil { + log.Println(err) + formErrors.Messages = append(formErrors.Messages, "error saving avatar (is it too big?)") + } + } + } + + context.User.Bio = strings.Trim(req.FormValue("bio"), "\n") + context.User.Pronouns = req.FormValue("pronouns") + context.User.Location = req.FormValue("location") + context.User.Website = req.FormValue("website") + context.User.Avatar = avatarPrefix + context.User.ID + formErrors.Messages = append(formErrors.Messages, validateProfileUpdate(context.User)...) + + if len(formErrors.Messages) == 0 { + _, err = database.SaveUser(context.DBConn, context.User) + if err != nil { + formErrors.Messages = append(formErrors.Messages, "error saving profile") + } + } + + (*context.TemplateData)["Profile"] = context.User + (*context.TemplateData)["Error"] = formErrors + + if len(formErrors.Messages) > 0 { + log.Println(formErrors.Messages) + + resp.WriteHeader(http.StatusBadRequest) + return failure(context, req, resp) + } + + formSuccess := types.BannerMessages{ + Messages: []string{"profile updated"}, + } + (*context.TemplateData)["Success"] = formSuccess + return success(context, req, resp) + } + } +} + +func validateProfileUpdate(user *database.User) []string { + errors := []string{} + + if (!strings.HasPrefix(user.Website, "https://") && !strings.HasPrefix(user.Website, "http://")) || len(user.Website) < 8 { + errors = append(errors, "website must be a valid URL") + } + if len(user.Website) > 64 { + errors = append(errors, "website cannot be longer than 64 characters") + } + + if len(user.Pronouns) > 64 { + errors = append(errors, "pronouns cannot be longer than 64 characters") + } + + if len(user.Bio) > 128 { + errors = append(errors, "bio cannot be longer than 128 characters") + } + + newLines := strings.Count(user.Bio, "\n") + if newLines > 8 { + errors = append(errors, "message cannot contain more than 8 new lines") + } + + if len(user.Location) > 32 { + errors = append(errors, "location cannot be longer than 64 characters") + } + + return errors +} diff --git a/api/serve.go b/api/serve.go index c8775d8..a688445 100644 --- a/api/serve.go +++ b/api/serve.go @@ -7,12 +7,14 @@ import ( "net/http" "time" - "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/cloudflare" + "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/external_dns/cloudflare" + "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/files/filesystem" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/auth" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/dns" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/guestbook" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/hcaptcha" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/keys" + "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/profiles" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/template" "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/types" "git.hatecomputers.club/hatecomputers/hatecomputers.club/args" @@ -32,7 +34,6 @@ func LogRequestContinuation(context *types.RequestContext, req *http.Request, re func LogExecutionTimeContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain { return func(success types.Continuation, _failure types.Continuation) types.ContinuationChain { end := time.Now() - log.Println(context.Id, "took", end.Sub(context.Start)) return success(context, req, resp) @@ -70,13 +71,15 @@ func CacheControlMiddleware(next http.Handler, maxAge int) http.Handler { func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server { mux := http.NewServeMux() - fileServer := http.FileServer(http.Dir(argv.StaticPath)) - mux.Handle("GET /static/", http.StripPrefix("/static/", CacheControlMiddleware(fileServer, 3600))) - + // "dependency injection" cloudflareAdapter := &cloudflare.CloudflareExternalDNSAdapter{ APIToken: argv.CloudflareToken, ZoneId: argv.CloudflareZone, } + uploadAdapter := &filesystem.FilesystemAdapter{ + BasePath: argv.UploadPath, + Permissions: 0777, + } makeRequestContext := func() *types.RequestContext { return &types.RequestContext{ @@ -86,9 +89,14 @@ func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server { } } + staticFileServer := http.FileServer(http.Dir(argv.StaticPath)) + uploadFileServer := http.FileServer(http.Dir(argv.UploadPath)) + mux.Handle("GET /static/", http.StripPrefix("/static/", CacheControlMiddleware(staticFileServer, 3600))) + mux.Handle("GET /uploads/", http.StripPrefix("/uploads/", CacheControlMiddleware(uploadFileServer, 60))) + mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) { requestContext := makeRequestContext() - LogRequestContinuation(requestContext, r, w)(auth.VerifySessionContinuation, FailurePassingContinuation)(IdContinuation, IdContinuation)(template.TemplateContinuation("home.html", true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + LogRequestContinuation(requestContext, r, w)(auth.VerifySessionContinuation, FailurePassingContinuation)(IdContinuation, IdContinuation)(auth.ListUsersContinuation, auth.ListUsersContinuation)(template.TemplateContinuation("home.html", true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) }) mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) { @@ -111,16 +119,26 @@ func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server { LogRequestContinuation(requestContext, r, w)(auth.LogoutContinuation, FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) }) + mux.HandleFunc("GET /profile", func(w http.ResponseWriter, r *http.Request) { + requestContext := makeRequestContext() + LogRequestContinuation(requestContext, r, w)(auth.VerifySessionContinuation, FailurePassingContinuation)(profiles.GetProfileContinuation, auth.GoLoginContinuation)(template.TemplateContinuation("profile.html", true), template.TemplateContinuation("profile.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + }) + + mux.HandleFunc("POST /profile", func(w http.ResponseWriter, r *http.Request) { + requestContext := makeRequestContext() + updateProfileContinuation := profiles.UpdateProfileContinuation(uploadAdapter, profiles.MaxAvatarSize, profiles.AvatarPath, profiles.AvatarPrefix) + + LogRequestContinuation(requestContext, r, w)(auth.VerifySessionContinuation, FailurePassingContinuation)(updateProfileContinuation, auth.GoLoginContinuation)(profiles.GetProfileContinuation, FailurePassingContinuation)(template.TemplateContinuation("profile.html", true), template.TemplateContinuation("profile.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + }) + mux.HandleFunc("GET /dns", func(w http.ResponseWriter, r *http.Request) { requestContext := makeRequestContext() LogRequestContinuation(requestContext, r, w)(auth.VerifySessionContinuation, FailurePassingContinuation)(dns.ListDNSRecordsContinuation, auth.GoLoginContinuation)(template.TemplateContinuation("dns.html", true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) }) - const MAX_USER_RECORDS = 100 - var USER_OWNED_INTERNAL_FMT_DOMAINS = []string{"%s", "%s.endpoints"} mux.HandleFunc("POST /dns", func(w http.ResponseWriter, r *http.Request) { requestContext := makeRequestContext() - LogRequestContinuation(requestContext, r, w)(auth.VerifySessionContinuation, FailurePassingContinuation)(dns.ListDNSRecordsContinuation, auth.GoLoginContinuation)(dns.CreateDNSRecordContinuation(cloudflareAdapter, MAX_USER_RECORDS, USER_OWNED_INTERNAL_FMT_DOMAINS), FailurePassingContinuation)(dns.ListDNSRecordsContinuation, dns.ListDNSRecordsContinuation)(template.TemplateContinuation("dns.html", true), template.TemplateContinuation("dns.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + LogRequestContinuation(requestContext, r, w)(auth.VerifySessionContinuation, FailurePassingContinuation)(dns.ListDNSRecordsContinuation, auth.GoLoginContinuation)(dns.CreateDNSRecordContinuation(cloudflareAdapter, dns.MaxUserRecords, dns.UserOwnedInternalFmtDomains), FailurePassingContinuation)(dns.ListDNSRecordsContinuation, dns.ListDNSRecordsContinuation)(template.TemplateContinuation("dns.html", true), template.TemplateContinuation("dns.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) }) mux.HandleFunc("POST /dns/delete", func(w http.ResponseWriter, r *http.Request) { diff --git a/api/types/types.go b/api/types/types.go index bbc25ea..84ed93c 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -20,8 +20,8 @@ type RequestContext struct { User *database.User } -type FormError struct { - Errors []string +type BannerMessages struct { + Messages []string } type Continuation func(*RequestContext, *http.Request, http.ResponseWriter) ContinuationChain |
