commit a40f5b88b37468af2b01bb41187829c49d159730
parent 3ebc107aef28a6f1e402d707647a441d389c263c
Author: David Eisinger <[email protected]>
Date: Mon, 6 Oct 2025 21:20:45 -0400
user.go
Diffstat:
2 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/internal/migrations/0006_users.sql b/internal/migrations/0006_users.sql
@@ -0,0 +1,12 @@
+-- Users table for authentication
+CREATE TABLE IF NOT EXISTS users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ username TEXT NOT NULL CHECK (username <> '' AND username GLOB '[a-z0-9]*'),
+ encrypted_password TEXT NOT NULL,
+ api_token TEXT,
+ created_at TEXT NOT NULL,
+ updated_at TEXT NOT NULL
+);
+
+CREATE UNIQUE INDEX IF NOT EXISTS idx_users_username ON users(username);
+
diff --git a/internal/user.go b/internal/user.go
@@ -0,0 +1,30 @@
+package internal
+
+import (
+ "errors"
+)
+
+// User represents an application user.
+// The username must be unique and contain only lowercase letters and digits.
+type User struct {
+ ID int64
+ Username string
+ EncryptedPassword string
+ APIToken *string
+}
+
+// ValidateUsername verifies that the username is non-empty and contains only
+// lowercase ASCII letters and digits.
+func ValidateUsername(u string) error {
+ if u == "" {
+ return errors.New("username is required")
+ }
+ for i := 0; i < len(u); i++ {
+ c := u[i]
+ if (c < 'a' || c > 'z') && (c < '0' || c > '9') {
+ return errors.New("username must be lowercase letters and digits only")
+ }
+ }
+ return nil
+}
+