diff options
| author | Bobby <[email protected]> | 2025-07-13 11:57:45 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2025-07-13 11:57:45 +0530 |
| commit | bf112649d039f8f02e2135a74d8b506f7c31c784 (patch) | |
| tree | 0598be94ed9a718ac41bed1b8950c4887f381fef | |
| parent | a698f5fde54c96f017a5af600c1e54a20cf051e6 (diff) | |
| download | imageboard-bf112649d039f8f02e2135a74d8b506f7c31c784.tar.xz imageboard-bf112649d039f8f02e2135a74d8b506f7c31c784.zip | |
Login post controller
| -rw-r--r-- | config/types.go | 1 | ||||
| -rw-r--r-- | controllers/constants.go | 37 | ||||
| -rw-r--r-- | controllers/login.go | 74 | ||||
| -rw-r--r-- | database/user.go | 11 | ||||
| -rw-r--r-- | router/routes.go | 5 | ||||
| -rw-r--r-- | static/css/main.css | 1 | ||||
| -rw-r--r-- | templates/login.django | 9 | ||||
| -rw-r--r-- | templates/preferences.django | 4 | ||||
| -rw-r--r-- | utils/shortcuts/render.go | 5 |
9 files changed, 137 insertions, 10 deletions
diff --git a/config/types.go b/config/types.go index 7786915..c8bdd75 100644 --- a/config/types.go +++ b/config/types.go @@ -9,6 +9,7 @@ type ServerConfig struct { AppSecret string `env:"APP_SECRET" default:"default_secret"`
IsDevMode bool `env:"DEV_MODE" default:"true"`
MinPasswordLength int `env:"MIN_PASSWORD_LENGTH" default:"8"`
+ AdminEmail string `env:"ADMIN_EMAIL" default:""`
}
type DatabaseConfig struct {
diff --git a/controllers/constants.go b/controllers/constants.go new file mode 100644 index 0000000..95d75c1 --- /dev/null +++ b/controllers/constants.go @@ -0,0 +1,37 @@ +package controllers + +const ( + // Page titles + PT_HOME = "Home Page" + PT_LOGIN = "Login" + PT_POSTS = "Posts" + PT_PREFERENCES = "Preferences" + PT_REGISTER = "Register" + PT_404 = "Page Not Found" + + // Template names + TEMPLATE_HOME = "home" + TEMPLATE_LOGIN = "login" + TEMPLATE_POSTS = "posts" + TEMPLATE_PREFERENCES = "preferences" + TEMPLATE_REGISTER = "register" + TEMPLATE_404 = "404" + + // URL constants for various routes + URL_HOME = "/" + URL_LOGIN = "/login" + URL_POSTS = "/posts" + URL_PREFERENCES = "/preferences" + URL_REGISTER = "/register" + URL_FORGOT_PASSWORD = "/accounts/forgot-password" + URL_RESEND_VERIFICATION = "/accounts/resend-verification" + + // Error messages + ERR_INVALID_FORM_DATA = "The submitted form data is invalid. Check your input and try again." + ERR_USER_NOT_FOUND = `User with that username not found. Maybe you want to <a href="` + URL_REGISTER + `">register</a>?` + ERR_LOGIN_INVALID_CREDENTIALS = `The credentials you provided are incorrect. Did you <a href="` + URL_FORGOT_PASSWORD + `">forget your password</a>?` + ERR_ACCOUNT_DISABLED = `Your account is disabled or banned. You can reach out to support for assistance.` + ERR_ACCOUNT_UNABLE_TO_LOGIN = `You cannot log in at this time. Verify your email or contact support. If you misplaced your verification email, you can <a href="` + URL_RESEND_VERIFICATION + `">request a new one</a>.` + ERR_SESSION_FAILED_TO_CREATE = "Failed to create session. Please try again later." + ERR_SESSION_FAILED_TO_SAVE = "Failed to save session. Please try again later." +) diff --git a/controllers/login.go b/controllers/login.go index 1ea9caf..12262e3 100644 --- a/controllers/login.go +++ b/controllers/login.go @@ -1,12 +1,82 @@ package controllers import ( + "imageboard/database" + "imageboard/session" "imageboard/utils/shortcuts" "github.com/gofiber/fiber/v2" ) +func getRedirectURL(ctx *fiber.Ctx) string { + referer := ctx.Get("Referer") + if referer != "" && referer != ctx.BaseURL()+URL_LOGIN && referer != ctx.BaseURL()+URL_REGISTER { + return referer + } + return URL_HOME +} + +func renderLoginError(ctx *fiber.Ctx, errorMsg string, statusCode int) error { + return shortcuts.RenderWithStatus(ctx, TEMPLATE_LOGIN, fiber.Map{ + "Error": errorMsg, + "Username": ctx.FormValue("username"), // Preserve username in form + }, statusCode) +} + func LoginPageController(ctx *fiber.Ctx) error { - ctx.Locals("Title", "Login") - return shortcuts.Render(ctx, "login", nil) + ctx.Locals("Title", PT_LOGIN) + sess, err := session.Store.Get(ctx) + if err == nil { + if userID, ok := sess.Get("user_id").(int); ok && userID != 0 { + return ctx.Redirect(getRedirectURL(ctx), fiber.StatusSeeOther) + } + } + + return shortcuts.Render(ctx, TEMPLATE_LOGIN, nil) +} + +func LoginPostController(ctx *fiber.Ctx) error { + ctx.Locals("Title", PT_LOGIN) + type LoginForm struct { + Username string `json:"username" form:"username"` + Password string `json:"password" form:"password"` + } + + var form LoginForm + var err error + if err = ctx.BodyParser(&form); err != nil { + return renderLoginError(ctx, ERR_INVALID_FORM_DATA, fiber.StatusBadRequest) + } + + user, err := database.GetUserByUsername(form.Username) + if err != nil { + return renderLoginError(ctx, ERR_USER_NOT_FOUND, fiber.StatusUnauthorized) + } + + if !user.CheckPassword(form.Password) { + return renderLoginError(ctx, ERR_LOGIN_INVALID_CREDENTIALS, fiber.StatusUnauthorized) + } + + if !user.IsActive() { + return renderLoginError(ctx, ERR_ACCOUNT_DISABLED, fiber.StatusForbidden) + } + + if !user.CanLogin() { + return renderLoginError(ctx, ERR_ACCOUNT_UNABLE_TO_LOGIN, fiber.StatusForbidden) + } + + sess, err := session.Store.Get(ctx) + if err != nil { + return renderLoginError(ctx, ERR_SESSION_FAILED_TO_CREATE, fiber.StatusInternalServerError) + } + sess.Set("user_id", user.ID) + sess.Set("username", user.Username) + if err := sess.Save(); err != nil { + return renderLoginError(ctx, ERR_SESSION_FAILED_TO_SAVE, fiber.StatusInternalServerError) + } + + user.UpdateLastUserLogin(database.DB) + user.UpdateLastUserActivity(database.DB) + + return ctx.Redirect(getRedirectURL(ctx), fiber.StatusSeeOther) } diff --git a/database/user.go b/database/user.go new file mode 100644 index 0000000..c512038 --- /dev/null +++ b/database/user.go @@ -0,0 +1,11 @@ +package database + +import "imageboard/models" + +func GetUserByUsername(username string) (*models.User, error) { + var user models.User + if err := DB.Where("username = ?", username).First(&user).Error; err != nil { + return nil, err + } + return &user, nil +} diff --git a/router/routes.go b/router/routes.go index 91b3816..d72e990 100644 --- a/router/routes.go +++ b/router/routes.go @@ -9,12 +9,15 @@ import ( func Initialize(router *fiber.App) {
main := router.Group("/")
main.Get("/", controllers.HomePageController)
- main.Get("/login", controllers.LoginPageController)
main.Get("/register", controllers.RegisterPageController)
main.Get("/preferences", controllers.PreferencesPageController)
posts := router.Group("/posts")
posts.Get("/", controllers.PostsController)
+ login := router.Group("/login")
+ login.Get("/", controllers.LoginPageController)
+ login.Post("/", controllers.LoginPostController)
+
router.Use(controllers.NotFoundController)
}
diff --git a/static/css/main.css b/static/css/main.css index aa92e1f..af99d59 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -363,5 +363,4 @@ footer::before { padding: 8px; margin-bottom: 16px; text-align: center; - display: none; }
\ No newline at end of file diff --git a/templates/login.django b/templates/login.django index 1f6ba78..3c2e39b 100644 --- a/templates/login.django +++ b/templates/login.django @@ -6,10 +6,10 @@ <img src="/static/images/25631a9833b39de4053f9eed8b2d3ae6.webp" alt="Login Image" class="q-img" /> <h1>Login to {{ Appname }}</h1> <p>Welcome back! Please enter your credentials to continue.</p> - {% if Error %} - <div class="error">{{ Error }}</div> - {% endif %} <form action="/login" method="POST" class="ibform"> + {% if Error %} + <div class="error">{{ Error|safe }}</div> + {% endif %} <div class="fgroup"> <div class="fg-main"> <label for="username">Username</label> @@ -29,7 +29,8 @@ </div> <div class="fbtngrp"> <input type="submit" value="Login" /> - <input type="button" value="Reset" /> + <input type="button" value="Clear" onclick="this.form.reset();" /> + <input type="button" value="Forgot Password?" onclick="window.location.href='/accounts/forgot-password';" /> </div> </form> <p class="text-center"> diff --git a/templates/preferences.django b/templates/preferences.django index 6b5f0e3..bb489f9 100644 --- a/templates/preferences.django +++ b/templates/preferences.django @@ -5,7 +5,7 @@ <h1>Site Preferences</h1> <p>Customize your experience on {{ Appname }}. These settings are stored in your cookies and will persist across sessions. However, they are not coupled with your account, so they don't apply to other devices or browsers.</p> <form action="javascript:setPreferences()" method="post" class="ibform"> - <div class="error" id="error-message"></div> + <div class="error" style="display: none;" id="error-message"></div> <div class="fgroup"> <div class="fg-main"> <label for="sidebar-width">Sidebar Width</label> @@ -57,7 +57,7 @@ </div> <div class="fg-sub"> <input type="number" id="posts-per-page" name="posts-per-page" value="{{ Preferences.PostsPerPage }}" min="1" max="100" /> - <small>Set the number of posts displayed per page. This can help manage load times</small> + <small>Set the number of posts displayed per page. This can help manage load times.</small> </div> </div> <div class="fbtngrp"> diff --git a/utils/shortcuts/render.go b/utils/shortcuts/render.go index ba83a81..8d42ab2 100644 --- a/utils/shortcuts/render.go +++ b/utils/shortcuts/render.go @@ -31,6 +31,11 @@ func Render(ctx *fiber.Ctx, name string, bind any) error { return ctx.Render(name, finalData)
}
+func RenderWithStatus(ctx *fiber.Ctx, name string, bind any, statusCode int) error {
+ ctx.Status(statusCode)
+ return Render(ctx, name, bind)
+}
+
func structToMap(obj any) map[string]any {
result := make(map[string]any)
|
