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
This commit is contained in:
333
.planning/phases/10-core-plugins/10-01-PLAN.md
Normal file
333
.planning/phases/10-core-plugins/10-01-PLAN.md
Normal file
@@ -0,0 +1,333 @@
|
||||
---
|
||||
phase: 10-core-plugins
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- 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
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "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"
|
||||
artifacts:
|
||||
- path: "plugins/golem15/user/models/FrontendUser.scala"
|
||||
provides: "Frontend user domain model with validation"
|
||||
contains: "case class FrontendUser"
|
||||
- path: "plugins/golem15/user/services/FrontendAuthService.scala"
|
||||
provides: "Authentication logic with JWT session tokens"
|
||||
exports: ["FrontendAuthService", "login", "logout", "getCurrentUser"]
|
||||
- path: "plugins/golem15/user/components/Account.scala"
|
||||
provides: "Registration and login HTMX handlers"
|
||||
exports: ["AccountComponent", "onSignin", "onRegister"]
|
||||
- path: "plugins/golem15/user/components/Session.scala"
|
||||
provides: "Page access control component"
|
||||
exports: ["SessionComponent", "onLogout"]
|
||||
- path: "plugins/golem15/user/resources/db/migration/V10_1_1__user_plugin.sql"
|
||||
provides: "Database schema for frontend users"
|
||||
contains: "CREATE TABLE frontend_users"
|
||||
key_links:
|
||||
- from: "plugins/golem15/user/components/Account.scala"
|
||||
to: "FrontendAuthService"
|
||||
via: "ZIO service injection"
|
||||
pattern: "ZIO\\.service\\[FrontendAuthService\\]"
|
||||
- from: "plugins/golem15/user/services/FrontendAuthService.scala"
|
||||
to: "FrontendUserRepository"
|
||||
via: "repository lookup"
|
||||
pattern: "userRepo\\.findByEmail"
|
||||
- from: "plugins/golem15/user/components/Session.scala"
|
||||
to: "pageContext"
|
||||
via: "user injection"
|
||||
pattern: "pageContext\\.set\\(\"user\""
|
||||
---
|
||||
|
||||
<objective>
|
||||
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
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/home/jin/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/home/jin/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<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)
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: User plugin models and database schema</name>
|
||||
<files>
|
||||
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
|
||||
</files>
|
||||
<action>
|
||||
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
|
||||
</action>
|
||||
<verify>
|
||||
./mill summercms.compile succeeds with new models
|
||||
SQL migration syntax valid (no syntax errors in IDE)
|
||||
</verify>
|
||||
<done>
|
||||
FrontendUser, UserGroup, UserThrottle, UserSettings models exist with Quill mappings.
|
||||
Migration creates all required tables with proper indexes.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Authentication and user services</name>
|
||||
<files>
|
||||
plugins/golem15/user/repositories/FrontendUserRepository.scala
|
||||
plugins/golem15/user/services/FrontendUserService.scala
|
||||
plugins/golem15/user/services/FrontendAuthService.scala
|
||||
plugins/golem15/user/services/ThrottleService.scala
|
||||
</files>
|
||||
<action>
|
||||
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
|
||||
</action>
|
||||
<verify>
|
||||
./mill summercms.compile succeeds
|
||||
All service traits have Live implementations
|
||||
ZLayers compose correctly (no missing dependencies)
|
||||
</verify>
|
||||
<done>
|
||||
FrontendUserRepository provides data access.
|
||||
FrontendUserService handles registration with Argon2id password hashing.
|
||||
FrontendAuthService manages JWT sessions with cookie helpers.
|
||||
ThrottleService prevents brute force attacks.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Session and Account components with templates</name>
|
||||
<files>
|
||||
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
|
||||
</files>
|
||||
<action>
|
||||
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
|
||||
</action>
|
||||
<verify>
|
||||
./mill summercms.compile succeeds
|
||||
Templates have valid Pebble syntax (no unclosed tags)
|
||||
Components registered in Plugin.scala boot method
|
||||
</verify>
|
||||
<done>
|
||||
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.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
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
|
||||
</verification>
|
||||
|
||||
<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>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/10-core-plugins/10-01-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user