No description
- TypeScript 99.7%
- Dockerfile 0.2%
* feat(admin): force password change on first login (#33) Accounts created via admin UI now require a password change before accessing the dashboard. Adds password_change_required column, session guard, and dedicated change-password route with integration tests. * fix: address roborev findings for password change feature - Re-set password_change_required when admin resets another user's password (skip when editing own account) - Simplify Mon compte action to single updateAdminUser call path - Use numeric 1/0 instead of string for boolean column values - Add tests for admin reset flag behavior * fix: address second roborev review findings - Remove dead session-clearing branch from Mon compte action - Clear password_change_required on self-edit via admin accounts page - Make passwordChangeRequired an explicit parameter in createAdminUser * chore: add db-state-invariant-audit rule Codifies the lesson from roborev review — when adding a state-tracking column, audit all mutation paths for semantic consistency. * fix(admin): reject reusing current password on forced change Compares new password against current hash to prevent users from keeping the admin-set password, which defeats the forced change. * feat(admin): allow login by email in addition to username The login form now accepts either username or email as the identifier. The query matches on username OR email (email column is nullable, so users without email can still log in by username only). * fix: address roborev round 3 — auth gate, action guard, unique email - Move passwordChangeRequired guard into requireAdminSession so it blocks both loaders and actions (not just layout GET navigations) - Add allowPasswordChange opt-out for change-password and logout routes - Validate passwordChangeRequired flag in change-password action to prevent unauthorized password rotation via direct POST - Add UNIQUE constraint on admin_users.email with migration - Add isEmailTaken check in all email-setting routes (create, edit, self) - Collapse isEditingSelf ternary to !isEditingSelf * chore: add auth-in-session rule, expand db-audit rule - New rule: authorization checks must live in requireAdminSession, not layout loaders (React Router actions bypass parent loaders) - Expanded db-state-invariant-audit with column reuse section — audit constraints when an existing column gains new semantic meaning * fix: resolve redirect loop in password change guard The layout loader is a parent of the change-password route — calling requireAdminSession without an exemption caused an infinite redirect. Replaced the allowPasswordChange option with URL-based path checking inside requireAdminSession, and updated E2E test to walk through the forced password change flow after admin reset. * fix: prevent cross-column collision in login lookup Use @-based column selection instead of OR query — emails always contain @, usernames never do. This makes the lookup deterministic and prevents one user's username from matching another's email. * fix: reject @ in usernames at both route and query level Validates at the Nextcloud import route (user-facing error) and in createAdminUser (invariant guard for any code path). This ensures the @-based login column selection stays deterministic. * fix: normalize empty emails, rename identifier param, add gate test - isEmailTaken returns false for null/empty emails instead of querying - Rename verifyAdminCredentials parameter from username to identifier - Add integration tests for requireAdminSession gate: flagged session redirects on regular routes, passes through on change-password * fix: catch ER_DUP_ENTRY on email update in account edit and Mon compte The isEmailTaken precheck has a TOCTOU window — a concurrent update could still hit the UNIQUE constraint. Catch ER_DUP_ENTRY and return a friendly 409 instead of an unhandled 500. * refactor: extract isDuplicateEntry type guard to shared utility Replaces inline as casts and hasMysqlCode helper with a proper type guard in app/db/errors.server.ts, used by all three routes that catch ER_DUP_ENTRY. |
||
|---|---|---|
| .claude | ||
| .github/workflows | ||
| .vscode | ||
| app | ||
| db | ||
| e2e | ||
| public/rr-assets | ||
| scripts | ||
| tests | ||
| .dockerignore | ||
| .gitignore | ||
| .mise.toml | ||
| .prettierignore | ||
| .prettierrc | ||
| .roborev.toml | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| images.config.ts | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.ts | ||
| react-router.config.ts | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
JDM Frontend
Headless WordPress frontend for jeunessedumonde.fr — built with React Router 7 SSR, Tailwind CSS v4, MariaDB, and Redis.
Prerequisites
Setup
1. Trust and install the Node version
mise trust
mise install
2. Install dependencies
npm install
3. Configure environment variables
Copy the example below into a .env file at the project root:
# WordPress REST API
WP_API_KEY=
# Redis
REDIS_URL=redis://localhost:6379
# MariaDB
DB_HOST=localhost
DB_PORT=3306
DB_USER=jdm
DB_PASSWORD=jdm
DB_NAME=jdm
# SMTP — use Mailpit locally (host=localhost, port=1025)
SMTP_HOST=
SMTP_PORT=
SMTP_USER=
SMTP_PASS=
SMTP_FROM=
SMTP_REJECT_UNAUTHORIZED= # set to "false" to skip TLS cert verification
# Admin session secret (any random string)
SESSION_SECRET=
# Public URL for share links (set to production domain behind a proxy)
APP_URL=
# Facebook App ID for Messenger share button
FACEBOOK_APP_ID=
# Nextcloud directory integration (optional — enables user import on accounts page)
NEXTCLOUD_URL=
NEXTCLOUD_USER=
NEXTCLOUD_PASSWORD=
# AWS SES event routing (optional — enables bounce/complaint tracking)
SES_CONFIGURATION_SET=
4. Start services
docker compose up -d
This starts MariaDB (port 3306), Redis (port 6379), and Mailpit (SMTP port 1025, web UI at http://localhost:8025).
5. Run database migrations and seed an admin account
npm run db:migrate
npm run db:seed
Development
npm run dev
The app is available at http://localhost:5173. Outgoing emails are caught by Mailpit at http://localhost:8025.
Other commands
| Command | Description |
|---|---|
npm run build |
Production build |
npm run start |
Run production server from build artifacts |
npm run typecheck |
TypeScript check + React Router type generation |
npm run format |
Format all files with Prettier |
npm run format:check |
Validate formatting without writing |
npm run db:migrate |
Apply pending SQL migrations |
npm run db:seed |
Create an admin account interactively |
npm run images |
Generate responsive WebP variants from hires sources |