diff options
Diffstat (limited to 'api')
| -rw-r--r-- | api/api_keys.go | 11 | ||||
| -rw-r--r-- | api/dns.go | 22 | ||||
| -rw-r--r-- | api/guestbook.go | 143 | ||||
| -rw-r--r-- | api/serve.go | 14 |
4 files changed, 172 insertions, 18 deletions
diff --git a/api/api_keys.go b/api/api_keys.go index 17ed6c9..d636044 100644 --- a/api/api_keys.go +++ b/api/api_keys.go @@ -30,17 +30,22 @@ func CreateAPIKeyContinuation(context *RequestContext, req *http.Request, resp h Errors: []string{}, } - apiKeys, err := database.ListUserAPIKeys(context.DBConn, context.User.ID) + numKeys, err := database.CountUserAPIKeys(context.DBConn, context.User.ID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } - if len(apiKeys) >= MAX_USER_API_KEYS { + if numKeys >= MAX_USER_API_KEYS { formErrors.Errors = append(formErrors.Errors, "max api keys reached") } + if len(formErrors.Errors) > 0 { + (*context.TemplateData)["FormError"] = formErrors + return failure(context, req, resp) + } + _, err = database.SaveAPIKey(context.DBConn, &database.UserApiKey{ UserID: context.User.ID, Key: utils.RandomId(), @@ -50,8 +55,6 @@ func CreateAPIKeyContinuation(context *RequestContext, req *http.Request, resp h resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } - - http.Redirect(resp, req, "/keys", http.StatusFound) return success(context, req, resp) } } @@ -72,25 +72,24 @@ func CreateDNSRecordContinuation(context *RequestContext, req *http.Request, res formErrors.Errors = append(formErrors.Errors, "invalid ttl") } - dnsRecord := &database.DNSRecord{ - UserID: context.User.ID, - Name: name, - Type: recordType, - Content: recordContent, - TTL: ttlNum, - Internal: internal, - } - - dnsRecords, err := database.GetUserDNSRecords(context.DBConn, context.User.ID) + dnsRecordCount, err := database.CountUserDNSRecords(context.DBConn, context.User.ID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } - if len(dnsRecords) >= MAX_USER_RECORDS { + if dnsRecordCount >= MAX_USER_RECORDS { formErrors.Errors = append(formErrors.Errors, "max records reached") } + dnsRecord := &database.DNSRecord{ + UserID: context.User.ID, + Name: name, + Type: recordType, + Content: recordContent, + TTL: ttlNum, + Internal: internal, + } if !userCanFuckWithDNSRecord(context.DBConn, context.User, dnsRecord) { formErrors.Errors = append(formErrors.Errors, "'name' must end with "+context.User.Username+" or you must be a domain owner for internal domains") } @@ -122,7 +121,6 @@ func CreateDNSRecordContinuation(context *RequestContext, req *http.Request, res return success(context, req, resp) } - (*context.TemplateData)["DNSRecords"] = dnsRecords (*context.TemplateData)["FormError"] = &formErrors (*context.TemplateData)["RecordForm"] = dnsRecord diff --git a/api/guestbook.go b/api/guestbook.go new file mode 100644 index 0000000..2037e7e --- /dev/null +++ b/api/guestbook.go @@ -0,0 +1,143 @@ +package api + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "strings" + + "git.hatecomputers.club/hatecomputers/hatecomputers.club/database" + "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils" +) + +type HcaptchaArgs struct { + SiteKey string +} + +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 < 10 || messageLength > 500 { + errors = append(errors, "message must be between 10 and 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 *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { + return func(success Continuation, failure Continuation) ContinuationChain { + name := req.FormValue("name") + message := req.FormValue("message") + hCaptchaResponse := req.FormValue("h-captcha-response") + + formErrors := FormError{ + Errors: []string{}, + } + + if hCaptchaResponse == "" { + formErrors.Errors = append(formErrors.Errors, "hCaptcha is required") + } + + entry := &database.GuestbookEntry{ + ID: utils.RandomId(), + Name: name, + Message: message, + } + + formErrors.Errors = append(formErrors.Errors, validateGuestbookEntry(entry)...) + + if len(formErrors.Errors) > 0 { + (*context.TemplateData)["FormError"] = formErrors + return failure(context, req, resp) + } + + err := verifyHCaptcha(context.Args.HcaptchaSecret, hCaptchaResponse) + if err != nil { + log.Println(err) + + resp.WriteHeader(http.StatusBadRequest) + return failure(context, req, resp) + } + + _, err = database.SaveGuestbookEntry(context.DBConn, entry) + if err != nil { + log.Println(err) + resp.WriteHeader(http.StatusInternalServerError) + return failure(context, req, resp) + } + + return success(context, req, resp) + } +} + +func ListGuestbookContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { + return func(success Continuation, failure Continuation) ContinuationChain { + entries, err := database.GetGuestbookEntries(context.DBConn) + if err != nil { + log.Println(err) + resp.WriteHeader(http.StatusInternalServerError) + return failure(context, req, resp) + } + + (*context.TemplateData)["GuestbookEntries"] = entries + return success(context, req, resp) + } +} + +func HcaptchaArgsContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { + return func(success Continuation, failure Continuation) ContinuationChain { + (*context.TemplateData)["HcaptchaArgs"] = HcaptchaArgs{ + SiteKey: context.Args.HcaptchaSiteKey, + } + log.Println(context.Args.HcaptchaSiteKey) + return success(context, req, resp) + } +} + +func verifyHCaptcha(secret, response string) error { + verifyURL := "https://hcaptcha.com/siteverify" + body := strings.NewReader("secret=" + secret + "&response=" + response) + + req, err := http.NewRequest("POST", verifyURL, body) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return err + } + + jsonResponse := struct { + Success bool `json:"success"` + }{} + err = json.NewDecoder(resp.Body).Decode(&jsonResponse) + if err != nil { + return err + } + + if !jsonResponse.Success { + return fmt.Errorf("hcaptcha verification failed") + } + + defer resp.Body.Close() + return nil +} diff --git a/api/serve.go b/api/serve.go index d16ea99..7cef1c9 100644 --- a/api/serve.go +++ b/api/serve.go @@ -118,7 +118,7 @@ func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server { mux.HandleFunc("POST /dns", func(w http.ResponseWriter, r *http.Request) { requestContext := makeRequestContext() - LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(CreateDNSRecordContinuation, GoLoginContinuation)(IdContinuation, TemplateContinuation("dns.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(ListDNSRecordsContinuation, GoLoginContinuation)(CreateDNSRecordContinuation, FailurePassingContinuation)(TemplateContinuation("dns.html", true), TemplateContinuation("dns.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) }) mux.HandleFunc("POST /dns/delete", func(w http.ResponseWriter, r *http.Request) { @@ -133,7 +133,7 @@ func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server { mux.HandleFunc("POST /keys", func(w http.ResponseWriter, r *http.Request) { requestContext := makeRequestContext() - LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(CreateAPIKeyContinuation, GoLoginContinuation)(IdContinuation, TemplateContinuation("api_keys.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(CreateAPIKeyContinuation, GoLoginContinuation)(ListAPIKeysContinuation, ListAPIKeysContinuation)(TemplateContinuation("api_keys.html", true), TemplateContinuation("api_keys.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) }) mux.HandleFunc("POST /keys/delete", func(w http.ResponseWriter, r *http.Request) { @@ -141,6 +141,16 @@ func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server { LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(DeleteAPIKeyContinuation, GoLoginContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) }) + mux.HandleFunc("GET /guestbook", func(w http.ResponseWriter, r *http.Request) { + requestContext := makeRequestContext() + LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(HcaptchaArgsContinuation, HcaptchaArgsContinuation)(ListGuestbookContinuation, ListGuestbookContinuation)(TemplateContinuation("guestbook.html", true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + }) + + mux.HandleFunc("POST /guestbook", func(w http.ResponseWriter, r *http.Request) { + requestContext := makeRequestContext() + LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(HcaptchaArgsContinuation, HcaptchaArgsContinuation)(SignGuestbookContinuation, FailurePassingContinuation)(ListGuestbookContinuation, ListGuestbookContinuation)(TemplateContinuation("guestbook.html", true), TemplateContinuation("guestbook.html", true))(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) + }) + mux.HandleFunc("GET /{name}", func(w http.ResponseWriter, r *http.Request) { requestContext := makeRequestContext() name := r.PathValue("name") |
