commit a380ebd50901d5978555f53a95eb9c557500bf83
parent 2b458a6ebd52958e9b88045c2c739517f44bbd68
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Mon, 12 Jun 2023 20:35:47 -0700
move code
Diffstat:
3 files changed, 301 insertions(+), 295 deletions(-)
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -9,7 +9,6 @@ import (
v1 "dkforest/pkg/web/handlers/api/v1"
"dkforest/pkg/web/handlers/streamModals"
"encoding/base64"
- "encoding/json"
"fmt"
_ "golang.org/x/image/bmp"
_ "golang.org/x/image/webp"
@@ -39,7 +38,6 @@ import (
"github.com/alecthomas/chroma/formatters/html"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
- "github.com/asaskevich/govalidator"
humanize "github.com/dustin/go-humanize"
"github.com/labstack/echo"
"github.com/pquerna/otp"
@@ -79,124 +77,6 @@ func HomeHandler(c echo.Context) error {
return loginHandler(c)
}
-func protectHomeHandler(c echo.Context) error {
- if c.Request().Method == http.MethodPost {
- return c.NoContent(http.StatusNotFound)
- }
- captchaQuery := c.QueryParam("captcha")
- loginQuery := c.QueryParam("login")
- signupQuery := c.QueryParam("signup")
- if captchaQuery != "" {
- if len(captchaQuery) > 6 || len(loginQuery) > 1 || len(signupQuery) > 1 ||
- !govalidator.IsASCII(captchaQuery) || !govalidator.IsASCII(loginQuery) || !govalidator.IsASCII(signupQuery) {
- time.Sleep(utils.RandSec(3, 7))
- return c.NoContent(http.StatusOK)
- }
- redirectTo := "/login/" + captchaQuery
- if signupQuery == "1" {
- redirectTo = "/signup/" + captchaQuery
- }
- time.Sleep(utils.RandSec(1, 2))
- return c.Redirect(http.StatusFound, redirectTo)
- }
- loginLink, found := tempLoginCache.Get("login_link")
- if !found {
- loginLink.ID, loginLink.Img = captcha.NewWithParams(captcha.Params{Store: tempLoginStore})
- loginLink.ValidUntil = time.Now().Add(3 * time.Minute)
- tempLoginCache.SetD("login_link", loginLink)
- }
-
- waitTime := int64(time.Until(loginLink.ValidUntil).Seconds())
-
- // Generate css frames
- frames := generateCssFrames(waitTime, func(i int64) string {
- return utils.ShortDur(time.Duration(i) * time.Second)
- }, true)
-
- time.Sleep(utils.RandSec(1, 2))
- bufTmp := make([]byte, 0, 1024*4)
- buf := bytes.NewBuffer(bufTmp)
- buf.Write([]byte(`<!DOCTYPE html><html lang="en"><head>
- <link href="/public/img/favicon.ico" rel="icon" type="image/x-icon" />
- <meta charset="UTF-8" />
- <meta name="author" content="n0tr1v">
- <meta name="language" content="English">
- <meta name="revisit-after" content="1 days">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="pragma" content="no-cache">
- <title>DarkForest</title>
- <style>
- body, html { height: 100%; width:100%; display:table; background-color: #222; color: white; line-height: 25px;
- font-family: Lato,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; }
- body { display:table-cell; vertical-align:middle; }
- #parent { display: table; width: 100%; }
- #form_login { display: table; margin: auto; }
- .captcha-img { transition: transform .2s; }
- .captcha-img:hover { transform: scale(2.5); }
- #timer_countdown:before {
- content: "`))
- buf.Write([]byte(utils.ShortDur(time.Duration(waitTime) * time.Second)))
- buf.Write([]byte(`";
- animation: `))
- buf.Write([]byte(utils.FormatInt64(waitTime)))
- buf.Write([]byte(`s 1s forwards timer_countdown_frames;
- }
- @keyframes timer_countdown_frames {`))
- for _, frame := range frames {
- buf.Write([]byte(frame))
- }
- buf.Write([]byte(`
- }
- </style>
-</head>
-<body class="bg">
-
-<div id="parent">
- <div id="form_login">
- <div class="text-center">
- <p>
- To login go to <code>/login/XXXXXX</code><br />
- To register go to <code>/signup/XXXXXX</code><br />
- (replace X by the numbers in the image)<br />
- Link valid for <strong><span id="timer_countdown"></span></strong>
- </p>
- <img src="data:image/png;base64,`))
- buf.Write([]byte(loginLink.Img))
- buf.Write([]byte(`" style="background-color: hsl(0, 0%, 90%);" class="captcha-img" />
- <form method="get">
- <input type="text" name="captcha" maxlength="6" autofocus />
- <button name="login" value="1" type="submit">Login</button>
- <button name="signup" value="1" type="submit">Register</button>
- </form>
- </div>
- </div>
-</div>
-
-</body>
-</html>`))
- return c.HTMLBlob(http.StatusOK, buf.Bytes())
-}
-
-const max2faAttempts = 4
-
-// partialAuthCache keep track of partial auth token -> user id.
-// When a user login and have 2fa enabled, we create a "partial" auth cookie.
-// The token can be used to complete the 2fa authentication.
-var partialAuthCache = cache.New[*PartialAuthItem](2*time.Minute, time.Hour)
-
-type PartialAuthItem struct {
- UserID database.UserID
- Step PartialAuthStep // Inform which type of 2fa the user is supposed to complete
- SessionDuration time.Duration
- Attempt int
-}
-
-func NewPartialAuthItem(userID database.UserID, step PartialAuthStep, sessionDuration time.Duration) *PartialAuthItem {
- return &PartialAuthItem{UserID: userID, Step: step, SessionDuration: sessionDuration}
-}
-
-type PartialAuthStep string
-
func createSessionCookie(value string, sessionDuration time.Duration) *http.Cookie {
return hutils.CreateCookie(hutils.AuthCookieName, value, int64(sessionDuration.Seconds()))
}
@@ -262,66 +142,6 @@ func metaCss(token string) []byte {
return buf.Bytes()
}
-var signupCache = cache.New[SignupInfo](5*time.Minute, 5*time.Minute)
-
-type SignupInfo struct {
- ScreenWidth string
- ScreenHeight string
- HelvaticaLoaded bool
-
- hasSolvedCaptcha bool
- UpdatedAt string
-}
-
-func SignalCss1(c echo.Context) error {
- authUser := c.Get("authUser").(*database.User)
- db := c.Get("database").(*database.DkfDB)
- data := c.Param("data")
- data = strings.TrimRight(data, ".png")
- data = strings.TrimRight(data, ".ttf")
- signalP := c.Param("signal")
- var info SignupInfo
- _ = json.Unmarshal([]byte(authUser.SignupMetadata), &info)
- switch signalP {
- case "sw":
- info.ScreenWidth = data
- case "sh":
- info.ScreenHeight = data
- case "Helvatica":
- info.HelvaticaLoaded = true
- }
- info.UpdatedAt = time.Now().Format(time.RFC3339)
- signupInfoEnc, _ := json.Marshal(info)
- authUser.SignupMetadata = string(signupInfoEnc)
- authUser.DoSave(db)
- return c.NoContent(http.StatusOK)
-}
-
-func SignalCss(c echo.Context) error {
- data := c.Param("data")
- data = strings.TrimRight(data, ".png")
- data = strings.TrimRight(data, ".ttf")
- token := c.Param("signupToken")
- signalP := c.Param("signal")
- var info SignupInfo
- if val, found := signupCache.Get(token); found {
- info = val
- } else {
- info = SignupInfo{}
- }
- switch signalP {
- case "sw":
- info.ScreenWidth = data
- case "sh":
- info.ScreenHeight = data
- case "Helvatica":
- info.HelvaticaLoaded = true
- }
- info.UpdatedAt = time.Now().Format(time.RFC3339)
- signupCache.SetD(token, info)
- return c.NoContent(http.StatusOK)
-}
-
type WaitPageCookiePayload struct {
Token string
Count int64
@@ -379,121 +199,6 @@ func waitPageWrapper(c echo.Context, clb echo.HandlerFunc, cookieName string) er
return clb(c)
}
-// The random wait time 0-15 seconds make sure the load is evenly distributed while under DDoS.
-// Not all requests to the signup endpoint will get the captcha at the same time,
-// so you cannot just refresh the page until you get a captcha that is easier to crack.
-func signupHandler(c echo.Context) error {
- db := c.Get("database").(*database.DkfDB)
- start := c.Get("start").(int64)
- signupToken := c.Get("signupToken").(string)
- var data signupData
- config.SignupPageLoad.Inc()
-
- data.PowEnabled = config.PowEnabled.Load()
- data.CaptchaSec = 120
- data.Frames = generateCssFrames(data.CaptchaSec, nil, true)
-
- hbCookie, hbCookieErr := c.Cookie(hutils.HBCookieName)
- hasHBCookie := hbCookieErr == nil && hbCookie.Value != ""
-
- signupInfo, _ := signupCache.Get(signupToken)
-
- data.HasSolvedCaptcha = signupInfo.hasSolvedCaptcha
- if !signupInfo.hasSolvedCaptcha {
- data.CaptchaID, data.CaptchaImg = captcha.New()
- }
-
- if c.Request().Method == http.MethodPost {
- data.Username = strings.TrimSpace(c.Request().PostFormValue("username"))
- data.Password = c.Request().PostFormValue("password")
- data.RePassword = c.Request().PostFormValue("repassword")
- data.Pow = c.Request().PostFormValue("pow")
- captchaID := c.Request().PostFormValue("captcha_id")
- captchaInput := c.Request().PostFormValue("captcha")
- captchaInputImg := c.Request().PostFormValue("captcha_img")
- if !signupInfo.hasSolvedCaptcha {
- if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
- data.ErrCaptcha = err.Error()
- config.SignupFailed.Inc()
- return c.Render(http.StatusOK, "standalone.signup", data)
- }
- }
- data.Captcha = captchaInput
- data.CaptchaImg = captchaInputImg
-
- signupInfo.hasSolvedCaptcha = true
- data.HasSolvedCaptcha = signupInfo.hasSolvedCaptcha
- signupCache.SetD(signupToken, signupInfo)
-
- // verify POW
- if config.PowEnabled.IsTrue() {
- if !hutils.VerifyPow(data.Username, data.Pow, config.PowDifficulty) {
- data.ErrPow = "invalid proof of work"
- return c.Render(http.StatusOK, "standalone.signup", data)
- }
- }
-
- config.SignupSucceed.Inc()
-
- // If SignupFakeEnabled is enabled, we always say the account was created, but we do not create it.
- if config.SignupFakeEnabled.IsTrue() {
- c.SetCookie(hutils.DeleteCookie(hutils.WaitCookieName))
- return c.Render(http.StatusOK, "flash", FlashResponse{"Your account has been created", "/login", "alert-success"})
- }
-
- // Fuck with kicked users. Prevent them from registering again.
- //authCookie, err := c.Cookie("auth-token")
- //if err == nil && authCookie.Value != "" {
- // return c.Render(http.StatusOK, "flash", FlashResponse{"Your account has been created", "/login", "alert-success"})
- //}
-
- signupInfoEnc, _ := json.Marshal(signupInfo)
-
- registrationDuration := time.Now().UnixMilli() - start
- newUser, errs := db.CreateUser(data.Username, data.Password, data.RePassword, registrationDuration, string(signupInfoEnc))
- if errs.HasError() {
- data.Errors = errs
- return c.Render(http.StatusOK, "standalone.signup", data)
- }
-
- // Fuck with hellbanned users. New account also hellbanned
- if hasHBCookie {
- newUser.IsHellbanned = true
- newUser.DoSave(db)
- }
-
- invitationToken := c.Param("invitationToken")
- if invitationToken != "" {
- if invitation, err := db.GetUnusedInvitationByToken(invitationToken); err == nil {
- invitation.InviteeUserID = newUser.ID
- invitation.DoSave(db)
- }
- }
-
- // If more than 10 users were created in the past minute, auto disable signup for the website
- if db.GetRecentUsersCount() > 10 {
- settings := db.GetSettings()
- settings.SignupEnabled = false
- settings.DoSave(db)
- config.SignupEnabled.SetFalse()
- if userNull, err := db.GetUserByUsername(config.NullUsername); err == nil {
- db.NewAudit(userNull, fmt.Sprintf("auto turn off signup"))
-
- // Display message in chat
- txt := fmt.Sprintf("auto turn off registrations")
- if err := db.CreateSysMsg(txt, txt, "", config.GeneralRoomID, userNull.ID); err != nil {
- logrus.Error(err)
- }
- }
- }
-
- c.SetCookie(hutils.DeleteCookie(hutils.WaitCookieName))
- return c.Render(http.StatusOK, "flash", FlashResponse{"Your account has been created", "/login", "alert-success"})
- }
-
- return c.Render(http.StatusOK, "standalone.signup", data)
-}
-
// RecaptchaResponse ...
type RecaptchaResponse struct {
Success bool `json:"success"`
diff --git a/pkg/web/handlers/login.go b/pkg/web/handlers/login.go
@@ -1,6 +1,7 @@
package handlers
import (
+ "bytes"
"dkforest/pkg/cache"
"dkforest/pkg/captcha"
"dkforest/pkg/config"
@@ -9,6 +10,7 @@ import (
"dkforest/pkg/utils"
hutils "dkforest/pkg/web/handlers/utils"
"fmt"
+ "github.com/asaskevich/govalidator"
"github.com/labstack/echo"
"github.com/pquerna/otp/totp"
"github.com/sirupsen/logrus"
@@ -18,6 +20,26 @@ import (
"time"
)
+const max2faAttempts = 4
+
+// partialAuthCache keep track of partial auth token -> user id.
+// When a user login and have 2fa enabled, we create a "partial" auth cookie.
+// The token can be used to complete the 2fa authentication.
+var partialAuthCache = cache.New[*PartialAuthItem](2*time.Minute, time.Hour)
+
+type PartialAuthItem struct {
+ UserID database.UserID
+ Step PartialAuthStep // Inform which type of 2fa the user is supposed to complete
+ SessionDuration time.Duration
+ Attempt int
+}
+
+func NewPartialAuthItem(userID database.UserID, step PartialAuthStep, sessionDuration time.Duration) *PartialAuthItem {
+ return &PartialAuthItem{UserID: userID, Step: step, SessionDuration: sessionDuration}
+}
+
+type PartialAuthStep string
+
const (
TwoFactorStep PartialAuthStep = "2fa"
PgpSignStep PartialAuthStep = "pgp_sign_2fa"
@@ -624,3 +646,101 @@ func forgotPasswordHandler(c echo.Context) error {
return c.Render(http.StatusOK, "flash", FlashResponse{"should not go here", "/login", "alert-danger"})
}
+
+func protectHomeHandler(c echo.Context) error {
+ if c.Request().Method == http.MethodPost {
+ return c.NoContent(http.StatusNotFound)
+ }
+ captchaQuery := c.QueryParam("captcha")
+ loginQuery := c.QueryParam("login")
+ signupQuery := c.QueryParam("signup")
+ if captchaQuery != "" {
+ if len(captchaQuery) > 6 || len(loginQuery) > 1 || len(signupQuery) > 1 ||
+ !govalidator.IsASCII(captchaQuery) || !govalidator.IsASCII(loginQuery) || !govalidator.IsASCII(signupQuery) {
+ time.Sleep(utils.RandSec(3, 7))
+ return c.NoContent(http.StatusOK)
+ }
+ redirectTo := "/login/" + captchaQuery
+ if signupQuery == "1" {
+ redirectTo = "/signup/" + captchaQuery
+ }
+ time.Sleep(utils.RandSec(1, 2))
+ return c.Redirect(http.StatusFound, redirectTo)
+ }
+ loginLink, found := tempLoginCache.Get("login_link")
+ if !found {
+ loginLink.ID, loginLink.Img = captcha.NewWithParams(captcha.Params{Store: tempLoginStore})
+ loginLink.ValidUntil = time.Now().Add(3 * time.Minute)
+ tempLoginCache.SetD("login_link", loginLink)
+ }
+
+ waitTime := int64(time.Until(loginLink.ValidUntil).Seconds())
+
+ // Generate css frames
+ frames := generateCssFrames(waitTime, func(i int64) string {
+ return utils.ShortDur(time.Duration(i) * time.Second)
+ }, true)
+
+ time.Sleep(utils.RandSec(1, 2))
+ bufTmp := make([]byte, 0, 1024*4)
+ buf := bytes.NewBuffer(bufTmp)
+ buf.Write([]byte(`<!DOCTYPE html><html lang="en"><head>
+ <link href="/public/img/favicon.ico" rel="icon" type="image/x-icon" />
+ <meta charset="UTF-8" />
+ <meta name="author" content="n0tr1v">
+ <meta name="language" content="English">
+ <meta name="revisit-after" content="1 days">
+ <meta http-equiv="expires" content="0">
+ <meta http-equiv="pragma" content="no-cache">
+ <title>DarkForest</title>
+ <style>
+ body, html { height: 100%; width:100%; display:table; background-color: #222; color: white; line-height: 25px;
+ font-family: Lato,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; }
+ body { display:table-cell; vertical-align:middle; }
+ #parent { display: table; width: 100%; }
+ #form_login { display: table; margin: auto; }
+ .captcha-img { transition: transform .2s; }
+ .captcha-img:hover { transform: scale(2.5); }
+ #timer_countdown:before {
+ content: "`))
+ buf.Write([]byte(utils.ShortDur(time.Duration(waitTime) * time.Second)))
+ buf.Write([]byte(`";
+ animation: `))
+ buf.Write([]byte(utils.FormatInt64(waitTime)))
+ buf.Write([]byte(`s 1s forwards timer_countdown_frames;
+ }
+ @keyframes timer_countdown_frames {`))
+ for _, frame := range frames {
+ buf.Write([]byte(frame))
+ }
+ buf.Write([]byte(`
+ }
+ </style>
+</head>
+<body class="bg">
+
+<div id="parent">
+ <div id="form_login">
+ <div class="text-center">
+ <p>
+ To login go to <code>/login/XXXXXX</code><br />
+ To register go to <code>/signup/XXXXXX</code><br />
+ (replace X by the numbers in the image)<br />
+ Link valid for <strong><span id="timer_countdown"></span></strong>
+ </p>
+ <img src="data:image/png;base64,`))
+ buf.Write([]byte(loginLink.Img))
+ buf.Write([]byte(`" style="background-color: hsl(0, 0%, 90%);" class="captcha-img" />
+ <form method="get">
+ <input type="text" name="captcha" maxlength="6" autofocus />
+ <button name="login" value="1" type="submit">Login</button>
+ <button name="signup" value="1" type="submit">Register</button>
+ </form>
+ </div>
+ </div>
+</div>
+
+</body>
+</html>`))
+ return c.HTMLBlob(http.StatusOK, buf.Bytes())
+}
diff --git a/pkg/web/handlers/signup.go b/pkg/web/handlers/signup.go
@@ -1,14 +1,31 @@
package handlers
import (
+ "dkforest/pkg/cache"
"dkforest/pkg/captcha"
"dkforest/pkg/config"
"dkforest/pkg/database"
hutils "dkforest/pkg/web/handlers/utils"
+ "encoding/json"
+ "fmt"
"github.com/labstack/echo"
+ "github.com/sirupsen/logrus"
"net/http"
+ "strings"
+ "time"
)
+var signupCache = cache.New[SignupInfo](5*time.Minute, 5*time.Minute)
+
+type SignupInfo struct {
+ ScreenWidth string
+ ScreenHeight string
+ HelvaticaLoaded bool
+
+ hasSolvedCaptcha bool
+ UpdatedAt string
+}
+
// SignupHandler ...
func SignupHandler(c echo.Context) error {
if config.ProtectHome.IsTrue() {
@@ -50,3 +67,167 @@ func tmpSignupHandler(c echo.Context) error {
}
return waitPageWrapper(c, signupHandler, hutils.WaitCookieName)
}
+
+// The random wait time 0-15 seconds make sure the load is evenly distributed while under DDoS.
+// Not all requests to the signup endpoint will get the captcha at the same time,
+// so you cannot just refresh the page until you get a captcha that is easier to crack.
+func signupHandler(c echo.Context) error {
+ db := c.Get("database").(*database.DkfDB)
+ start := c.Get("start").(int64)
+ signupToken := c.Get("signupToken").(string)
+ var data signupData
+ config.SignupPageLoad.Inc()
+
+ data.PowEnabled = config.PowEnabled.Load()
+ data.CaptchaSec = 120
+ data.Frames = generateCssFrames(data.CaptchaSec, nil, true)
+
+ hbCookie, hbCookieErr := c.Cookie(hutils.HBCookieName)
+ hasHBCookie := hbCookieErr == nil && hbCookie.Value != ""
+
+ signupInfo, _ := signupCache.Get(signupToken)
+
+ data.HasSolvedCaptcha = signupInfo.hasSolvedCaptcha
+ if !signupInfo.hasSolvedCaptcha {
+ data.CaptchaID, data.CaptchaImg = captcha.New()
+ }
+
+ if c.Request().Method == http.MethodPost {
+ data.Username = strings.TrimSpace(c.Request().PostFormValue("username"))
+ data.Password = c.Request().PostFormValue("password")
+ data.RePassword = c.Request().PostFormValue("repassword")
+ data.Pow = c.Request().PostFormValue("pow")
+ captchaID := c.Request().PostFormValue("captcha_id")
+ captchaInput := c.Request().PostFormValue("captcha")
+ captchaInputImg := c.Request().PostFormValue("captcha_img")
+ if !signupInfo.hasSolvedCaptcha {
+ if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
+ data.ErrCaptcha = err.Error()
+ config.SignupFailed.Inc()
+ return c.Render(http.StatusOK, "standalone.signup", data)
+ }
+ }
+ data.Captcha = captchaInput
+ data.CaptchaImg = captchaInputImg
+
+ signupInfo.hasSolvedCaptcha = true
+ data.HasSolvedCaptcha = signupInfo.hasSolvedCaptcha
+ signupCache.SetD(signupToken, signupInfo)
+
+ // verify POW
+ if config.PowEnabled.IsTrue() {
+ if !hutils.VerifyPow(data.Username, data.Pow, config.PowDifficulty) {
+ data.ErrPow = "invalid proof of work"
+ return c.Render(http.StatusOK, "standalone.signup", data)
+ }
+ }
+
+ config.SignupSucceed.Inc()
+
+ // If SignupFakeEnabled is enabled, we always say the account was created, but we do not create it.
+ if config.SignupFakeEnabled.IsTrue() {
+ c.SetCookie(hutils.DeleteCookie(hutils.WaitCookieName))
+ return c.Render(http.StatusOK, "flash", FlashResponse{"Your account has been created", "/login", "alert-success"})
+ }
+
+ // Fuck with kicked users. Prevent them from registering again.
+ //authCookie, err := c.Cookie("auth-token")
+ //if err == nil && authCookie.Value != "" {
+ // return c.Render(http.StatusOK, "flash", FlashResponse{"Your account has been created", "/login", "alert-success"})
+ //}
+
+ signupInfoEnc, _ := json.Marshal(signupInfo)
+
+ registrationDuration := time.Now().UnixMilli() - start
+ newUser, errs := db.CreateUser(data.Username, data.Password, data.RePassword, registrationDuration, string(signupInfoEnc))
+ if errs.HasError() {
+ data.Errors = errs
+ return c.Render(http.StatusOK, "standalone.signup", data)
+ }
+
+ // Fuck with hellbanned users. New account also hellbanned
+ if hasHBCookie {
+ newUser.IsHellbanned = true
+ newUser.DoSave(db)
+ }
+
+ invitationToken := c.Param("invitationToken")
+ if invitationToken != "" {
+ if invitation, err := db.GetUnusedInvitationByToken(invitationToken); err == nil {
+ invitation.InviteeUserID = newUser.ID
+ invitation.DoSave(db)
+ }
+ }
+
+ // If more than 10 users were created in the past minute, auto disable signup for the website
+ if db.GetRecentUsersCount() > 10 {
+ settings := db.GetSettings()
+ settings.SignupEnabled = false
+ settings.DoSave(db)
+ config.SignupEnabled.SetFalse()
+ if userNull, err := db.GetUserByUsername(config.NullUsername); err == nil {
+ db.NewAudit(userNull, fmt.Sprintf("auto turn off signup"))
+
+ // Display message in chat
+ txt := fmt.Sprintf("auto turn off registrations")
+ if err := db.CreateSysMsg(txt, txt, "", config.GeneralRoomID, userNull.ID); err != nil {
+ logrus.Error(err)
+ }
+ }
+ }
+
+ c.SetCookie(hutils.DeleteCookie(hutils.WaitCookieName))
+ return c.Render(http.StatusOK, "flash", FlashResponse{"Your account has been created", "/login", "alert-success"})
+ }
+
+ return c.Render(http.StatusOK, "standalone.signup", data)
+}
+
+func SignalCss1(c echo.Context) error {
+ authUser := c.Get("authUser").(*database.User)
+ db := c.Get("database").(*database.DkfDB)
+ data := c.Param("data")
+ data = strings.TrimRight(data, ".png")
+ data = strings.TrimRight(data, ".ttf")
+ signalP := c.Param("signal")
+ var info SignupInfo
+ _ = json.Unmarshal([]byte(authUser.SignupMetadata), &info)
+ switch signalP {
+ case "sw":
+ info.ScreenWidth = data
+ case "sh":
+ info.ScreenHeight = data
+ case "Helvatica":
+ info.HelvaticaLoaded = true
+ }
+ info.UpdatedAt = time.Now().Format(time.RFC3339)
+ signupInfoEnc, _ := json.Marshal(info)
+ authUser.SignupMetadata = string(signupInfoEnc)
+ authUser.DoSave(db)
+ return c.NoContent(http.StatusOK)
+}
+
+func SignalCss(c echo.Context) error {
+ data := c.Param("data")
+ data = strings.TrimRight(data, ".png")
+ data = strings.TrimRight(data, ".ttf")
+ token := c.Param("signupToken")
+ signalP := c.Param("signal")
+ var info SignupInfo
+ if val, found := signupCache.Get(token); found {
+ info = val
+ } else {
+ info = SignupInfo{}
+ }
+ switch signalP {
+ case "sw":
+ info.ScreenWidth = data
+ case "sh":
+ info.ScreenHeight = data
+ case "Helvatica":
+ info.HelvaticaLoaded = true
+ }
+ info.UpdatedAt = time.Now().Format(time.RFC3339)
+ signupCache.SetD(token, info)
+ return c.NoContent(http.StatusOK)
+}