aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/types.go1
-rw-r--r--controllers/constants.go37
-rw-r--r--controllers/login.go74
-rw-r--r--database/user.go11
-rw-r--r--router/routes.go5
-rw-r--r--static/css/main.css1
-rw-r--r--templates/login.django9
-rw-r--r--templates/preferences.django4
-rw-r--r--utils/shortcuts/render.go5
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)