summaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/auth/auth.go15
-rw-r--r--api/dns/dns.go88
-rw-r--r--api/dns/dns_test.go4
-rw-r--r--api/guestbook/guestbook.go66
-rw-r--r--api/hcaptcha/hcaptcha.go4
-rw-r--r--api/keys/keys.go20
-rw-r--r--api/profiles/profiles.go118
-rw-r--r--api/serve.go36
-rw-r--r--api/types/types.go4
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