Files
summercms-initial-research/.planning/phases/10-core-plugins/10-01-PLAN.md
Jakub Zych 4d8d5719d5 docs(10): create phase plan
Phase 10: Core Plugins
- 4 plans in 2 waves
- Wave 1: 10-01 (User auth), 10-03 (Blog posts) - parallel
- Wave 2: 10-02 (User profiles), 10-04 (Blog categories/tags) - sequential
- Ready for execution
2026-02-05 16:07:32 +01:00

14 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
10-core-plugins 01 execute 1
plugins/golem15/user/plugin.yaml
plugins/golem15/user/Plugin.scala
plugins/golem15/user/models/FrontendUser.scala
plugins/golem15/user/models/UserGroup.scala
plugins/golem15/user/models/UserThrottle.scala
plugins/golem15/user/models/UserSettings.scala
plugins/golem15/user/services/FrontendUserService.scala
plugins/golem15/user/services/FrontendAuthService.scala
plugins/golem15/user/services/ThrottleService.scala
plugins/golem15/user/components/Session.scala
plugins/golem15/user/components/Account.scala
plugins/golem15/user/resources/db/migration/V10_1_1__user_plugin.sql
plugins/golem15/user/resources/views/account/login.peb
plugins/golem15/user/resources/views/account/register.peb
true
truths artifacts key_links
Frontend visitor can register with email, password, name, surname
Registered user receives activation email if activation mode is user
User can log in with email and password
User can log out and session is terminated
Session component restricts page access based on security mode
Login throttling prevents brute force attacks
path provides contains
plugins/golem15/user/models/FrontendUser.scala Frontend user domain model with validation case class FrontendUser
path provides exports
plugins/golem15/user/services/FrontendAuthService.scala Authentication logic with JWT session tokens
FrontendAuthService
login
logout
getCurrentUser
path provides exports
plugins/golem15/user/components/Account.scala Registration and login HTMX handlers
AccountComponent
onSignin
onRegister
path provides exports
plugins/golem15/user/components/Session.scala Page access control component
SessionComponent
onLogout
path provides contains
plugins/golem15/user/resources/db/migration/V10_1_1__user_plugin.sql Database schema for frontend users CREATE TABLE frontend_users
from to via pattern
plugins/golem15/user/components/Account.scala FrontendAuthService ZIO service injection ZIO.service[FrontendAuthService]
from to via pattern
plugins/golem15/user/services/FrontendAuthService.scala FrontendUserRepository repository lookup userRepo.findByEmail
from to via pattern
plugins/golem15/user/components/Session.scala pageContext user injection pageContext.set("user"
Create the User plugin foundation with registration and authentication functionality.

Purpose: Establish frontend user management as the first core plugin, demonstrating the complete plugin pattern with models, services, components, and migrations.

Output: Working User plugin with:

  • FrontendUser model with validation rules
  • Registration flow with configurable activation
  • Login/logout with JWT cookie sessions
  • Session component for page access control
  • Login throttling for security

<execution_context> @/home/jin/.claude/get-shit-done/workflows/execute-plan.md @/home/jin/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/10-core-plugins/10-CONTEXT.md @.planning/phases/10-core-plugins/10-RESEARCH.md

Reference patterns from prior phases

Phase 2: Plugin manifest and lifecycle patterns

Phase 3: Component system with HTMX handlers

Phase 6: Authentication patterns (Argon2id, JWT)

Task 1: User plugin models and database schema plugins/golem15/user/plugin.yaml plugins/golem15/user/Plugin.scala plugins/golem15/user/models/FrontendUser.scala plugins/golem15/user/models/UserGroup.scala plugins/golem15/user/models/UserThrottle.scala plugins/golem15/user/models/UserSettings.scala plugins/golem15/user/resources/db/migration/V10_1_1__user_plugin.sql Create User plugin structure following Phase 2 plugin patterns:
**plugin.yaml:**
- name: Golem15.User
- description: Frontend user authentication and management
- author: Golem15
- version: 1.0.0
- require: [] (no plugin dependencies)

**Plugin.scala:**
- Extend SummerPlugin trait
- Register components: Session, Account (ResetPassword in plan 02)
- Register settings model
- Boot method for any initialization

**FrontendUser.scala:**
- Case class with fields from research Pattern 1:
  id, email, passwordHash, name, surname, username (Option), avatarPath (Option),
  isActivated, activationCode, activatedAt, persistCode, resetPasswordCode,
  lastLogin, lastSeen, createdIpAddress, lastIpAddress, isGuest, isSuperuser,
  deletedAt (soft delete), createdAt, updatedAt
- Validation rules object: email (required, unique, 6-255), password (required, 8-255),
  name (required, 2-100), surname (required, 2-100)
- Quill table mapping to `frontend_users`

**UserGroup.scala:**
- Case class: id, name, code (unique), description, createdAt, updatedAt
- Quill table mapping to `user_groups`
- UsersGroups pivot case class for many-to-many

**UserThrottle.scala:**
- Case class: id, userId (Option), ipAddress (Option), attempts, lastAttemptAt,
  isSuspended, suspendedAt, isBanned, bannedAt
- Quill table mapping to `user_throttle`

**UserSettings.scala:**
- Sealed traits: ActivateMode (Auto, User, Admin), RememberLogin (Always, Never, Ask)
- Case class UserPluginSettings with fields from research Pattern 2:
  allowRegistration, requireActivation, activateMode, useThrottle,
  useRegisterThrottle, blockPersistence, rememberLogin, minPasswordLength
- Default values matching WinterCMS

**V10_1_1__user_plugin.sql:**
- CREATE TABLE frontend_users (all fields with proper types, indexes)
- CREATE TABLE user_groups (id, name, code unique, description, timestamps)
- CREATE TABLE users_groups (user_id, group_id, composite PK)
- CREATE TABLE user_throttle (id, user_id nullable, ip_address inet, attempts, timestamps, ban flags)
- Indexes on email, username, persist_code, throttle lookups
./mill summercms.compile succeeds with new models SQL migration syntax valid (no syntax errors in IDE) FrontendUser, UserGroup, UserThrottle, UserSettings models exist with Quill mappings. Migration creates all required tables with proper indexes. Task 2: Authentication and user services plugins/golem15/user/repositories/FrontendUserRepository.scala plugins/golem15/user/services/FrontendUserService.scala plugins/golem15/user/services/FrontendAuthService.scala plugins/golem15/user/services/ThrottleService.scala Create service layer for user operations:
**FrontendUserRepository.scala:**
- Trait with ZIO effects following Phase 1 repository pattern
- Methods: findById, findByEmail, findByPersistCode, create, update, delete (soft)
- Live implementation using QuillContext
- Error handling with RepositoryError ADT

**FrontendUserService.scala:**
- Trait following research Pattern 1
- Methods:
  - findByEmail(email): Option[FrontendUser]
  - findById(id): Option[FrontendUser]
  - register(data: RegistrationData): FrontendUser (handles activation code generation)
  - activate(userId, code): FrontendUser
  - updateProfile(userId, data: ProfileUpdate): FrontendUser
  - touchLastSeen(userId): Unit
  - touchIpAddress(userId, ip): Unit
- RegistrationData case class: email, password, name, surname, ipAddress
- ProfileUpdate case class: name, surname, newPassword (Option)
- Use Password4j for hashing (Argon2id, matching Phase 6 patterns)
- Live implementation with ZLayer

**FrontendAuthService.scala:**
- Trait following research Pattern 10
- Methods:
  - login(user, remember): String (returns JWT token)
  - logout: Unit (clears session, rotates persistCode)
  - getCurrentUser: Option[FrontendUser]
  - isAuthenticated: Boolean
  - verifyPassword(userId, password): Boolean
  - validateAndSetSession(token): Unit (for middleware)
- JWT token generation using jwt-scala (15min session, 7day remember)
- Cookie configuration helper (httpOnly, secure in prod, SameSite Lax)
- Session state via Ref[Option[FrontendUser]]
- Live implementation with ZLayer

**ThrottleService.scala:**
- Trait for login/registration throttling
- Methods:
  - isThrottled(ip): Boolean
  - recordAttempt(ip, success): Unit
  - clearAttempts(ip): Unit
  - isBanned(ip): Boolean
- Configurable thresholds (5 attempts, 15 min lockout)
- Live implementation with ZLayer
./mill summercms.compile succeeds All service traits have Live implementations ZLayers compose correctly (no missing dependencies) FrontendUserRepository provides data access. FrontendUserService handles registration with Argon2id password hashing. FrontendAuthService manages JWT sessions with cookie helpers. ThrottleService prevents brute force attacks. Task 3: Session and Account components with templates plugins/golem15/user/components/Session.scala plugins/golem15/user/components/Account.scala plugins/golem15/user/resources/views/session/default.peb plugins/golem15/user/resources/views/account/login.peb plugins/golem15/user/resources/views/account/register.peb plugins/golem15/user/resources/views/account/success.peb Create frontend components following Phase 3 component patterns:
**Session.scala (research Pattern 3):**
- Extend SummerComponent trait
- ComponentDetails: name "Session", description "User session and access control"
- Property schema:
  - security: Dropdown (all/user/guest), default "all"
  - allowedUserGroups: Set, optional group filtering
  - redirect: Dropdown, page to redirect unauthorized users
- onRun lifecycle:
  - Check security mode against current auth state
  - If unauthorized and redirect set, fail with ComponentRedirect
  - If unauthorized and no redirect, fail with AccessDenied
  - Inject "user" into pageContext (null if guest)
  - Touch lastSeen for authenticated users
- HTMX handler onLogout:
  - Call authService.logout
  - Return HtmxResponse with HX-Redirect to configured page
  - Trigger "userLoggedOut" event

**Account.scala (research Pattern 4):**
- Extend SummerComponent trait
- ComponentDetails: name "Account", description "Registration, login, profile"
- Property schema:
  - redirect: Dropdown, page after login/register
  - paramCode: String, activation code URL param name, default "code"
  - requirePassword: Checkbox, require current password for profile update
- HTMX handlers:
  - onSignin: Parse login/password from form, validate length, call authService.authenticate,
    handle errors (InvalidCredentials, AccountBanned, NotActivated) with generic message,
    record IP, return HX-Redirect on success
  - onRegister: Check allowRegistration setting, check throttle, parse form data,
    call userService.register, send activation email if mode is User,
    auto-login if auto-activated, return HX-Redirect on success
  - onActivate: Get code from URL param, call userService.activate, auto-login, redirect

**Templates (Pebble):**
- session/default.peb: Empty by default (component injects user context only)
- account/login.peb:
  - Form with hx-post to onSignin handler
  - Email input, password input, remember checkbox (if settings allow)
  - Error display area with hx-swap="innerHTML"
  - CSRF token hidden field
- account/register.peb:
  - Form with hx-post to onRegister handler
  - Email, password, password_confirmation, name, surname inputs
  - Error display area
  - CSRF token hidden field
- account/success.peb:
  - Success message partial for HTMX responses
./mill summercms.compile succeeds Templates have valid Pebble syntax (no unclosed tags) Components registered in Plugin.scala boot method Session component controls page access with security modes. Account component handles login and registration via HTMX. Templates render forms with CSRF protection and error handling. User plugin fully functional for registration and authentication flows. After all tasks complete: 1. User plugin compiles: `./mill summercms.compile` 2. Migration valid: Check SQL syntax for frontend_users, user_groups, users_groups, user_throttle 3. Component registration: Plugin.scala registers Session and Account components 4. Service wiring: All ZLayers compose without missing dependencies 5. Template syntax: Pebble templates parse without errors

<success_criteria>

  • Frontend visitor can submit registration form (model, service, component exist)
  • Registration creates FrontendUser with hashed password
  • Login authenticates user and sets JWT cookie
  • Session component restricts page access based on security mode
  • Logout clears session and rotates persist code
  • Throttle service tracks failed login attempts </success_criteria>
After completion, create `.planning/phases/10-core-plugins/10-01-SUMMARY.md`