Initial commit

This commit is contained in:
lowcarbdev
2025-11-11 16:40:10 -07:00
commit b79e599640
57 changed files with 11811 additions and 0 deletions
+278
View File
@@ -0,0 +1,278 @@
package internal
import (
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"time"
"github.com/labstack/echo/v4"
)
func HandleRegister(c echo.Context) error {
var req RegisterRequest
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "Invalid request body",
})
}
// Validate input
req.Username = strings.TrimSpace(req.Username)
if req.Username == "" {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "Username is required",
})
}
if len(req.Username) < 3 {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "Username must be at least 3 characters",
})
}
if len(req.Password) < 6 {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "Password must be at least 6 characters",
})
}
// Create user
user, err := CreateUser(req.Username, req.Password)
if err != nil {
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
return c.JSON(http.StatusConflict, AuthResponse{
Success: false,
Error: "Username already exists",
})
}
slog.Error("Error creating user", "error", err)
return c.JSON(http.StatusInternalServerError, AuthResponse{
Success: false,
Error: "Failed to create user",
})
}
// Create session
session, err := CreateSession(user.ID, user.Username)
if err != nil {
slog.Error("Error creating session", "error", err)
return c.JSON(http.StatusInternalServerError, AuthResponse{
Success: false,
Error: "Failed to create session",
})
}
// Set session cookie
c.SetCookie(&http.Cookie{
Name: "session_id",
Value: session.ID,
Expires: session.ExpiresAt,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Path: "/",
})
// Initialize user's database (using UUID as filename)
dbPathPrefix := os.Getenv("DB_PATH_PREFIX")
if dbPathPrefix == "" {
dbPathPrefix = "."
}
userDBPath := fmt.Sprintf("%s/sbv_%s.db", dbPathPrefix, user.ID)
if err := InitUserDB(user.ID, userDBPath); err != nil {
slog.Error("Error initializing user database", "error", err)
return echo.ErrInternalServerError
}
return c.JSON(http.StatusOK, AuthResponse{
Success: true,
User: user,
Session: session,
})
}
func HandleLogin(c echo.Context) error {
var req LoginRequest
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "Invalid request body",
})
}
// Validate input
req.Username = strings.TrimSpace(req.Username)
if req.Username == "" || req.Password == "" {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "Username and password are required",
})
}
// Get user
user, err := GetUserByUsername(req.Username)
if err != nil {
return c.JSON(http.StatusUnauthorized, AuthResponse{
Success: false,
Error: "Invalid username or password",
})
}
// Verify password
if !VerifyPassword(user, req.Password) {
return c.JSON(http.StatusUnauthorized, AuthResponse{
Success: false,
Error: "Invalid username or password",
})
}
// Create session
session, err := CreateSession(user.ID, user.Username)
if err != nil {
slog.Error("Error creating session", "error", err)
return c.JSON(http.StatusInternalServerError, AuthResponse{
Success: false,
Error: "Failed to create session",
})
}
// Set session cookie
c.SetCookie(&http.Cookie{
Name: "session_id",
Value: session.ID,
Expires: session.ExpiresAt,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Path: "/",
})
return c.JSON(http.StatusOK, AuthResponse{
Success: true,
User: user,
Session: session,
})
}
func HandleLogout(c echo.Context) error {
// Get session ID from cookie
cookie, err := c.Cookie("session_id")
if err == nil {
// Delete session from database
DeleteSession(cookie.Value)
}
// Clear cookie
c.SetCookie(&http.Cookie{
Name: "session_id",
Value: "",
Expires: time.Now().Add(-1 * time.Hour),
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Path: "/",
})
return c.JSON(http.StatusOK, map[string]bool{
"success": true,
})
}
func HandleMe(c echo.Context) error {
// Get session from context (set by AuthMiddleware)
session, ok := c.Get("session").(*Session)
if !ok {
return c.JSON(http.StatusUnauthorized, AuthResponse{
Success: false,
Error: "Unauthorized",
})
}
// Get user
user, err := GetUserByUsername(session.Username)
if err != nil {
return c.JSON(http.StatusInternalServerError, AuthResponse{
Success: false,
Error: "Failed to get user info",
})
}
return c.JSON(http.StatusOK, AuthResponse{
Success: true,
User: user,
Session: session,
})
}
func HandleChangePassword(c echo.Context) error {
// Get session from context (set by AuthMiddleware)
session, ok := c.Get("session").(*Session)
if !ok {
return c.JSON(http.StatusUnauthorized, AuthResponse{
Success: false,
Error: "Unauthorized",
})
}
var req ChangePasswordRequest
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "Invalid request body",
})
}
// Validate input
if req.OldPassword == "" || req.NewPassword == "" || req.ConfirmPassword == "" {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "All fields are required",
})
}
if req.NewPassword != req.ConfirmPassword {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "New passwords do not match",
})
}
if len(req.NewPassword) < 6 {
return c.JSON(http.StatusBadRequest, AuthResponse{
Success: false,
Error: "New password must be at least 6 characters",
})
}
// Get user
user, err := GetUserByUsername(session.Username)
if err != nil {
return c.JSON(http.StatusInternalServerError, AuthResponse{
Success: false,
Error: "Failed to get user info",
})
}
// Verify old password
if !VerifyPassword(user, req.OldPassword) {
return c.JSON(http.StatusUnauthorized, AuthResponse{
Success: false,
Error: "Current password is incorrect",
})
}
// Update password
if err := UpdatePassword(user.ID, req.NewPassword); err != nil {
slog.Error("Error updating password", "error", err)
return c.JSON(http.StatusInternalServerError, AuthResponse{
Success: false,
Error: "Failed to update password",
})
}
return c.JSON(http.StatusOK, AuthResponse{
Success: true,
})
}