feat: account creation on/off toggling

This commit is contained in:
ae 2025-05-05 10:18:07 +03:00
parent dba81d65cb
commit 0ef65937e8
Signed by: ae
GPG Key ID: 995EFD5C1B532B3E
7 changed files with 76 additions and 60 deletions

View File

@ -10,11 +10,14 @@ JWT_SECRET=""
CSRF_SECRET="" # Should be 32 bytes long
ADMIN_USERNAME=""
ADMIN_PASSWORD=""
ACCOUNT_CREATION_ENABLED="0"
LOG_LEVEL="info"
APP_ENV="production"
DOMAIN=""
FRONTEND_URL=""
# Frontend
# Frontend (build-stage)
VITE_ACCOUNT_CREATION_ENABLED="$ACCOUNT_CREATION_ENABLED"
VITE_VIEW_COOKIE_PATH="/"
VITE_COOKIE_SAME_SITE="strict"
VITE_VIEW_COOKIE_DOMAIN="$DOMAIN"

View File

@ -37,6 +37,7 @@ services:
CSRF_SECRET: ${CSRF_SECRET:?csrf secret required}
ADMIN_USERNAME: ${ADMIN_USERNAME:?init admin username required}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?init admin password required}
ACCOUNT_CREATION_ENABLED: ${ACCOUNT_CREATION_ENABLED:-0}
LOG_LEVEL: debug
APP_ENV: development
DOMAIN: localhost

View File

@ -35,6 +35,7 @@ services:
CSRF_SECRET: ${CSRF_SECRET:?csrf secret required}
ADMIN_USERNAME: ${ADMIN_USERNAME:?init admin username required}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?init admin password required}
ACCOUNT_CREATION_ENABLED: ${ACCOUNT_CREATION_ENABLED:-0}
LOG_LEVEL: ${LOG_LEVEL:-info}
APP_ENV: ${APP_ENV:-production}
DOMAIN: ${DOMAIN:-localhost}
@ -53,10 +54,6 @@ services:
- 3000:80 # Container port defined in nginx.conf (bound to 3000 for dev.)
depends_on:
- qnote-server
environment:
VITE_VIEW_COOKIE_PATH: ${VITE_VIEW_COOKIE_PATH:-/}
VITE_VIEW_COOKIE_DOMAIN: ${DOMAIN:-localhost}
VITE_COOKIE_SAME_SITE: ${VITE_COOKIE_SAME_SITE:-strict}
networks:
qnote:

View File

@ -96,8 +96,10 @@ func (rs authResource) Routes() chi.Router {
r := chi.NewRouter()
// Public routes (the only outside facing endpoints in the whole API with no requirement for auth)
r.Post("/signup", rs.Create) // POST /auth/signup - registration (rate-limited)
r.Post("/login", rs.Login) // POST /auth/login - login (rate-limited)
if rs.Config.IsRegEnabled {
r.Post("/signup", rs.Create) // POST /auth/signup - registration (rate-limited)
}
r.Post("/login", rs.Login) // POST /auth/login - login (rate-limited)
// Protected routes (access token required)
r.Group(func(r chi.Router) {

View File

@ -17,11 +17,12 @@ import (
const authRateLimit = 5 // req/min
type SvcConfig struct {
JWTSecret string
CSRFSecret string
IsProd bool
Domain string
FrontendURL string
JWTSecret string
CSRFSecret string
IsProd bool
IsRegEnabled bool
Domain string
FrontendURL string
}
func (sc *SvcConfig) allowedOrigins() []string {

View File

@ -26,19 +26,18 @@ var (
)
type Config struct {
JWTSecret string `env:"JWT_SECRET,notEmpty"`
CSRFSecret string `env:"CSRF_SECRET,notEmpty"`
DatabaseURL string `env:"DB_URL,notEmpty"`
AdminUsername string `env:"ADMIN_USERNAME,notEmpty,unset"`
AdminPassword string `env:"ADMIN_PASSWORD,notEmpty,unset"`
Domain string `env:"DOMAIN" envDefault:"localhost"`
FrontendURL string `env:"FRONTEND_URL" envDefault:"http://localhost:5173"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
AppEnv string `env:"APP_ENV" envDefault:"production"`
JWTSecret string `env:"JWT_SECRET,notEmpty"`
CSRFSecret string `env:"CSRF_SECRET,notEmpty"`
DatabaseURL string `env:"DB_URL,notEmpty"`
AdminUsername string `env:"ADMIN_USERNAME,notEmpty,unset"`
AdminPassword string `env:"ADMIN_PASSWORD,notEmpty,unset"`
AccountCreationEnabled bool `env:"ACCOUNT_CREATION_ENABLED" envDefault:"0"`
Domain string `env:"DOMAIN" envDefault:"localhost"`
FrontendURL string `env:"FRONTEND_URL" envDefault:"http://localhost:5173"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
AppEnv string `env:"APP_ENV" envDefault:"production"`
}
// TODO: add flag to enable/disable registration of new users
func init() {
config = Config{}
if err := env.Parse(&config); err != nil {
@ -47,14 +46,15 @@ func init() {
initLogger()
svcConfig = service.SvcConfig{
JWTSecret: config.JWTSecret,
CSRFSecret: config.CSRFSecret,
IsProd: config.AppEnv == "production",
Domain: config.Domain,
FrontendURL: config.FrontendURL,
JWTSecret: config.JWTSecret,
CSRFSecret: config.CSRFSecret,
IsProd: config.AppEnv == "production",
IsRegEnabled: config.AccountCreationEnabled,
Domain: config.Domain,
FrontendURL: config.FrontendURL,
}
log.Debug().Msgf("IsProd=%t, Domain='%s', FrontendURL='%s'", svcConfig.IsProd, svcConfig.Domain, svcConfig.FrontendURL)
log.Debug().Msgf("IsProd=%t, IsRegEnabled=%t, Domain='%s', FrontendURL='%s'", svcConfig.IsProd, svcConfig.IsRegEnabled, svcConfig.Domain, svcConfig.FrontendURL)
log.Debug().Msg("Initialization completed")
}

View File

@ -8,6 +8,10 @@
export let bottomText: string
export let bottomLink: string
const ACCOUNT_CREATION_ENABLED = (import.meta.env.VITE_ACCOUNT_CREATION_ENABLED || "0") === "1"
console.log(`[VIEW] Account creation enabled: ${ACCOUNT_CREATION_ENABLED}`)
// local state
let username = ""
let password = ""
let usernameError = ""
@ -73,44 +77,52 @@
</div>
{/if}
<form class="space-y-5" on:submit|preventDefault={handleSubmit}>
<div class="form-group">
<input
type="text"
id="username"
bind:value={username}
placeholder="Username"
required
autocomplete="username"
class="auth-input-field"
/>
{#if !ACCOUNT_CREATION_ENABLED && formName === "Register"}
<div>
<p>Registration disabled.</p>
</div>
{:else}
<form class="space-y-5" on:submit|preventDefault={handleSubmit}>
<div class="form-group">
<input
type="text"
id="username"
bind:value={username}
placeholder="Username"
required
autocomplete="username"
class="auth-input-field"
/>
</div>
<div class="form-group">
<input
type="password"
id="password"
bind:value={password}
placeholder="Password"
required
autocomplete={formName === "Login" ? "current-password" : "new-password"}
class="auth-input-field"
/>
</div>
<div class="form-group">
<input
type="password"
id="password"
bind:value={password}
placeholder="Password"
required
autocomplete={formName === "Login" ? "current-password" : "new-password"}
class="auth-input-field"
/>
</div>
<button type="submit" class="auth-button">
{formName}
</button>
</form>
<button type="submit" class="auth-button">
{formName}
</button>
</form>
{/if}
<p class="text-center text-sm font-medium">
{bottomText}
<a href={bottomLink}> here </a>
</p>
{#if ACCOUNT_CREATION_ENABLED}
<p class="text-center text-sm font-medium">
{bottomText}
<a href={bottomLink}> here </a>
</p>
{/if}
</div>
</div>
<!-- TODO: add footer with a randomly picked quote (font-copernicus) -->
<!-- TODO: add footer with randomized quotes (see KF impl., font-copernicus) -->
<div class="absolute right-4 top-4">
<ThemeToggle />