commit 66f1bf4fb04f377a1c03ebf7996bbbbe6c2368a1 Author: Zhongwei Li Date: Sun Nov 30 08:46:40 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..36e95d6 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "claude-code-toolkit", + "description": "Django and React development toolkit with setup automation, authentication configuration, and UI framework integration", + "version": "1.0.0", + "author": { + "name": "Otoshek Dev", + "email": "support@otoshek.com" + }, + "skills": [ + "./skills" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a571221 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# claude-code-toolkit + +Django and React development toolkit with setup automation, authentication configuration, and UI framework integration diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..93f88ed --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,161 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:otoshek/Claude-Code-Toolkit:", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "324840f98c80ff42a15eda78b3c2f7369f38410a", + "treeHash": "37ad51dae0b243e58d168e37c3d85746832a588b30bdebfb929bc8e67d60f721", + "generatedAt": "2025-11-28T10:27:33.712871Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "claude-code-toolkit", + "description": "Django and React development toolkit with setup automation, authentication configuration, and UI framework integration", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "554fe8bc72d54cf41a76df3e02caeca787576ca67cda8ed8461ac9c31ce80cd4" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "50129030c346e1322a52aece5a09f1d14ea4d6c7d2196d5c8bfdaa6d2b81bf4c" + }, + { + "path": "skills/react-allauth/README.md", + "sha256": "63f55747b6401fe9b6154121f9292cf00f7bfa8104d2c9b6efd46f57fe731c23" + }, + { + "path": "skills/react-allauth/SKILL.md", + "sha256": "e75800db0dbc660352336f8d83f324e656dc86fa5a1d8ea5f537bff8797158d0" + }, + { + "path": "skills/react-allauth/references/styling-guide.md", + "sha256": "e05a4852e0fdc735cf1ad7623154bd3b27135213dd3f76dcbeab971480b5aa4b" + }, + { + "path": "skills/react-allauth/scripts/test_auth_flows.py", + "sha256": "f582d413b1109bb7de80f4c0e085e6c413889cc6a1cf07709a6c0fd3451c7dc2" + }, + { + "path": "skills/tailwind-setup/README.md", + "sha256": "028a57ec079d9b965b8025356606b394ba551ce967331ff86dbacd72419ad0d2" + }, + { + "path": "skills/tailwind-setup/SKILL.md", + "sha256": "8a9f755b5b97b772961a7b51367884f4acc5062314f04f2a454fed9945d65b0f" + }, + { + "path": "skills/tailwind-setup/scripts/apply-theme.sh", + "sha256": "1ea5f7b9321a7d236f2d4cb6ebb58a145c5203713d975d0c56c5496597446b2e" + }, + { + "path": "skills/tailwind-setup/assets/tailwind-theme.css", + "sha256": "7e2c339a76ac553bb8060cd1e1e7787c48773f2ef7fef32bebf3380e830f84d4" + }, + { + "path": "skills/apply-shadcn/README.md", + "sha256": "12292aa1876b6196690eb2899417f38c3a81803e674f24783df3c2bd1c71268c" + }, + { + "path": "skills/apply-shadcn/SKILL.md", + "sha256": "472a2882133d62c90f9a7d6adc55818eacf1444834083cddc8a0c631c649dc37" + }, + { + "path": "skills/react-setup/README.md", + "sha256": "1adf848bdf900f3fbeba850ae5cdde8bd33cd03565c0db297dd1e912ed8d9672" + }, + { + "path": "skills/react-setup/SKILL.md", + "sha256": "cd83ff5e2d58e853f4bc7b5d02a8f15cc58754a2a8ebde9c5d8dc27944b50a79" + }, + { + "path": "skills/django-allauth/README.md", + "sha256": "194cbb209c30e3ff6fa172cdec128924c8de0e25a341620d4452fd7614462377" + }, + { + "path": "skills/django-allauth/SKILL.md", + "sha256": "3d4a07d2a4f573248707d142fab3577af97b7aaa3385ae2a577c18a886eb7dfb" + }, + { + "path": "skills/django-allauth/references/test-validation-guide.md", + "sha256": "f85a2cec214fab4e89e4ec69aa64e16f48c716d40b7c2db904b23435a5272ecb" + }, + { + "path": "skills/django-allauth/scripts/validate_allauth_tests.sh", + "sha256": "cfd72836e5f38c189337917aef02f6e0ba6e64e500627227d52215cd573f449d" + }, + { + "path": "skills/django-setup/README.md", + "sha256": "81609c3a26b4b880bb33357ee7ed27bbdee4d00ef4b7fa8ca833a8b8ee4d23fc" + }, + { + "path": "skills/django-setup/SKILL.md", + "sha256": "74a3845a2448f9d36bb1b8a1fd46ff811b8960ae0860440da01c4837279b81a1" + }, + { + "path": "skills/django-setup/platform-install/linux-fedora.md", + "sha256": "75057c12ff2dfc4eccf62278f00e35d67d40d4eca1e8b2135792836761660df3" + }, + { + "path": "skills/django-setup/platform-install/windows.md", + "sha256": "2418bd56c9de89d38346dc5cb374c5a38f5e11355bbd4ec9b4287b2701eb492a" + }, + { + "path": "skills/django-setup/platform-install/linux-ubuntu.md", + "sha256": "e68709c862b47176d3a81bd020f20c1aa61637ae2e2029ff024f2a5ef0128314" + }, + { + "path": "skills/django-setup/platform-install/macos.md", + "sha256": "412b14818d30496fb7425aba9dae9820982ae58e340b291fcd991a37eec91c16" + }, + { + "path": "skills/django-setup/troubleshooting/django-errors.md", + "sha256": "40b947b2d41050299037a132e57ba9bb6fc80b6715513b040bb1fcbc262f93d7" + }, + { + "path": "skills/django-setup/troubleshooting/pip-problems.md", + "sha256": "7a5d766a6bc4e1dba0fef224689973a31ea1622e25d8ca90da8031a53507e5c5" + }, + { + "path": "skills/django-setup/troubleshooting/venv-issues.md", + "sha256": "f3469a0ad209f9f4615462c0e9e3f3d11ce6854c1d05f69b0d212c4e51d3e439" + }, + { + "path": "skills/django-setup/examples/.gitignore-template", + "sha256": "708fa753c8b4143b22857a675771468e8791197d01d582cc77c13e29ec623c7b" + }, + { + "path": "skills/django-setup/scripts/create_gitignore.py", + "sha256": "1ddbcd112b01f77e1d21e5d6dbec33fe370eac7aa4acfc5d2479c31f5a3fd6d3" + }, + { + "path": "skills/django-setup/platform-https/mkcert-https-setup-linux.md", + "sha256": "1e8c2ddc64a9eff2b8b6a3d4c077764fd3ab8e5ed0b0ad2d4e6e6f9e44cbb3a6" + }, + { + "path": "skills/django-setup/platform-https/mkcert-https-setup-windows.md", + "sha256": "276fcaaef8c98573b4bda2f548bd425fe5e94f172dbbfa3820151236b30c4dab" + }, + { + "path": "skills/django-setup/platform-https/mkcert-https-setup-macos.md", + "sha256": "55ea3323f3985c00205f5419d7c05d9481c0f6e9cc9ca0c9d975099be642fcac" + } + ], + "dirSha256": "37ad51dae0b243e58d168e37c3d85746832a588b30bdebfb929bc8e67d60f721" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/apply-shadcn/README.md b/skills/apply-shadcn/README.md new file mode 100644 index 0000000..acc501e --- /dev/null +++ b/skills/apply-shadcn/README.md @@ -0,0 +1,36 @@ +# Apply shadcn Skill + +Style existing React components/pages using shadcn/ui while respecting the project’s custom Tailwind theme tokens. + +## What This Skill Does +- Creates a todo list of target files to style (one task per component/page). +- Installs needed shadcn components (card, input, button, table, label, separator, navigation-menu, etc.). +- Pulls usage examples from the shadcn registry via MCP tools to match correct patterns/props. +- Replaces plain HTML with shadcn/ui components, keeping logic intact and using custom CSS variables (`--color-*`) from `src/index.css`. +- Verifies builds after styling changes. + +## Prerequisites +- React + Vite project already set up with Tailwind and shadcn tokens (see `tailwind-setup` skill). +- Custom theme variables present in `frontend/src/index.css`. +- shadcn CLI available (`npx shadcn@latest`). + +## Workflow (high level) +1) Make a todo list: one task per file that needs styling. +2) Install required shadcn components (`npx shadcn@latest add ...`) based on needs. +3) For each file: mark task in_progress → read file → fetch examples with MCP (`mcp__shadcn__get_item_examples_from_registries`) → apply shadcn components and theme vars → mark task completed. +4) Run `npm --prefix ./frontend run build` to ensure imports/JSX compile. + +## Theme Integration Tips +- Use Tailwind arbitrary values with custom vars, e.g. `bg-[var(--color-primary-600)] hover:bg-[var(--color-primary-700)]`. +- Keep functionality and routing intact; only swap UI primitives. +- Add `data-testid` if tests rely on stable selectors. + +## Outputs/Artifacts +- Styled components/pages using shadcn/ui. +- Todo list reflecting completed styling tasks. +- Successful build verification. + +## Notes & Gotchas +- Always check component availability via MCP search before `shadcn add`. +- Maintain alias usage (`@/components`, `@/lib/utils`, etc.) consistent with project. +- If existing CSS is important, migrate classes onto shadcn components rather than dropping them. diff --git a/skills/apply-shadcn/SKILL.md b/skills/apply-shadcn/SKILL.md new file mode 100644 index 0000000..8bbd691 --- /dev/null +++ b/skills/apply-shadcn/SKILL.md @@ -0,0 +1,114 @@ +--- +name: apply-shadcn +description: Apply shadcn/ui components to React pages and components. This skill should be used when styling specific pages or components with shadcn/ui in a React project that has custom theming. The user will specify which pages or components to style. +allowed-tools: Bash, Write, Edit, Read, Glob, Grep, TodoWrite, mcp__shadcn__get_project_registries, mcp__shadcn__search_items_in_registries, mcp__shadcn__view_items_in_registries, mcp__shadcn__get_item_examples_from_registries, mcp__shadcn__get_add_command_for_items, mcp__shadcn__get_audit_checklist +--- + +## Overview + +This skill applies shadcn/ui components to React pages and components while integrating with the project's custom theme defined in `frontend/src/index.css`. + +## When to Use This Skill + +Use this skill when the user requests to: +- Style specific pages with shadcn/ui components +- Apply shadcn/ui to a list of components +- Add shadcn/ui styling to authentication pages, navigation, or other UI elements + +The user will provide either: +- A list of page/component names to style (e.g., "Style Login.jsx, SignUp.jsx, and NavBar.jsx") +- A category of pages to style (e.g., "Style all authentication pages") + +## Workflow + +### Step 1: Create a Todo List + +Create a todo list with one task per page or component that needs styling. Each task should be named after the file being styled (e.g., "Style Login.jsx", "Style NavBar.jsx"). + +### Step 2: Install Required shadcn Components + +Before styling any pages, ensure the necessary shadcn components are installed. Common components needed: + +```bash +npx shadcn@latest add card input button table label separator navigation-menu +``` + +Adjust the component list based on what the pages require. + +### Step 3: Style Each Page or Component + +For each page or component in the todo list, follow this process: + +1. **Mark the task as in_progress** in the todo list + +2. **Read the existing file** to understand its current structure and functionality + +3. **Use the MCP tool to get component examples**: Call `mcp__shadcn__get_item_examples_from_registries` to retrieve usage examples for the shadcn components needed (e.g., Card, Input, Button, Table) + +4. **Review the examples** to understand: + - Proper component structure and composition + - Correct props and patterns + - Layout best practices + +5. **Apply shadcn styling with custom theme integration**: + - Replace existing HTML elements with shadcn components + - Integrate custom CSS variables from `frontend/src/index.css` for colors + - Maintain existing functionality and logic + - Follow the theme integration guidelines below + +6. **Mark the task as completed** in the todo list + +7. **Move to the next page or component** + +### Step 4: Verify All Tasks Complete + +After styling all pages, verify that every task in the todo list is marked as completed. + +### Step 5: Verify Setup + +Run `npm run build` to validate imports, dependencies, and syntax + +## Theme Integration Guidelines + +**CRITICAL:** The project uses custom CSS variables defined in `frontend/src/index.css`. When styling components that need custom colors, use these variables instead of shadcn's default theme classes. + +### Custom CSS Variables + +Available custom variables from `frontend/src/index.css`: +- `--color-primary-{50-950}` - Primary color scale +- `--color-secondary-{50-950}` - Secondary color scale +- `--color-accent-{50-950}` - Accent color scale +- `--color-success-{50-900}` - Success colors +- `--color-error-{50-900}` - Error colors + +### Example: Styling Primary Buttons + +Use custom CSS variables with Tailwind's arbitrary value syntax: + +```jsx + +``` + +**Why use custom variables:** The custom theme uses specific color values (e.g., purple hue at 250) that differ from shadcn's default theme. Using `bg-[var(--color-primary-600)]` ensures styled pages match the rest of the application. + +## Common shadcn Components Used + +- `card` - Card, CardContent, CardDescription, CardHeader, CardTitle +- `input` - Input +- `button` - Button +- `table` - Table, TableBody, TableCell, TableHead, TableHeader, TableRow +- `navigation-menu` - Navigation menu components +- `label` - Label +- `separator` - Separator + +## Notes + +- Always use the MCP tool before editing to understand proper component usage +- Preserve existing component functionality and logic +- Integrate custom theme variables for consistent styling +- Work through the todo list systematically, one page at a time +- Mark each task as completed immediately after finishing it diff --git a/skills/django-allauth/README.md b/skills/django-allauth/README.md new file mode 100644 index 0000000..3fc0508 --- /dev/null +++ b/skills/django-allauth/README.md @@ -0,0 +1,63 @@ +# Django Allauth (Headless) Skill + +Headless authentication setup for Django backends that serve React/Vue/mobile frontends. Adds django-allauth with MFA, social login, CORS, and REST endpoints—no server-rendered auth pages. + +## What This Skill Delivers +- Installs auth stack: `django-allauth[socialaccount,mfa]`, `djangorestframework`, `django-cors-headers`, `fido2`, `python3-openid`, `Pillow`, `pyyaml`, `python-dotenv`. +- Configures headless mode (`HEADLESS_ONLY = True`) with frontend redirect URLs for email verification, password reset, signup, and social errors. +- Enables MFA (TOTP + WebAuthn/passkeys + recovery codes) and Google OAuth example. +- Sets CORS/CSRF for a separate HTTPS frontend (`FRONTEND_URL`), uses session/JWT-friendly middleware ordering. +- Adds REST URL prefixes: `accounts/` (admin/backend) and `_allauth/` (headless API). +- Provides validation harness to run the official headless allauth tests (76 core cases) via `scripts/validate_allauth_tests.sh` and detailed coverage notes in `references/test-validation-guide.md`. + +## Skill Contents +- `SKILL.md` — step-by-step build instructions (install deps → settings.py edits → .env → URLs → checks → migrations → tests). +- `scripts/validate_allauth_tests.sh` — helper to run core allauth tests after cloning `django-allauth`. +- `references/test-validation-guide.md` — explains test categories, required pytest versions, and troubleshooting. + +## Prerequisites +- Existing Django project with virtualenv. +- `FRONTEND_URL` to point at your HTTPS SPA (e.g., `https://localhost:5173`). + +## Setup Summary (see SKILL.md for exact edits) +1) **Install packages** (venv active): + ```bash + pip install 'django-allauth[socialaccount,mfa]' python-dotenv djangorestframework django-cors-headers fido2 python3-openid Pillow pyyaml + ``` +2) **settings.py changes:** + - Load env vars: `from dotenv import load_dotenv; load_dotenv('.env.development')`. + - Add CORS/CSRF hosts: `FRONTEND_URL`, `ALLOWED_HOSTS`, `CORS_ALLOWED_ORIGINS`, `CSRF_TRUSTED_ORIGINS`. + - Ensure `django.template.context_processors.request` is in `TEMPLATES[0]['OPTIONS']['context_processors']`. + - `INSTALLED_APPS` add: `corsheaders`, `rest_framework`, `allauth`, `allauth.account`, `allauth.socialaccount`, `allauth.socialaccount.providers.google`, `allauth.mfa`, `allauth.headless`, `allauth.usersessions`. + - `MIDDLEWARE` order: keep `corsheaders.middleware.CorsMiddleware` after `SessionMiddleware`; add `allauth.account.middleware.AccountMiddleware` after `MessageMiddleware`. + - Auth backends + headless/MFA/social/email settings appended at file end (see SKILL.md block). +3) **Environment file** `.env.development` in project root: + ``` + GOOGLE_CLIENT_ID= + GOOGLE_CLIENT_SECRET= + ``` +4) **URLs** in project `urls.py`: + ```python + path('accounts/', include('allauth.urls')), + path('_allauth/', include('allauth.headless.urls')), + ``` +5) **Sanity checks & DB**: + ```bash + python manage.py check + python manage.py migrate + ``` +6) **Optional validation tests** (after cloning `django-allauth` sibling to project): + ```bash + bash scripts/validate_allauth_tests.sh + ``` + +## Outputs/Artifacts +- Headless auth APIs ready for SPA use. +- MFA-ready user flows (TOTP/WebAuthn/recovery codes). +- Dev email backend writes files to `sent_emails/` (adjust for production). +- Updated `requirements.txt` (after `pip freeze`). + +## Notes & Gotchas +- Use pytest `<9.0` and `pytest-asyncio==0.23.8` for the provided test suite. +- Keep middleware order exact to avoid CORS/login issues. +- Set `MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN = True if DEBUG else False` so localhost HTTPS works during development. diff --git a/skills/django-allauth/SKILL.md b/skills/django-allauth/SKILL.md new file mode 100644 index 0000000..147785a --- /dev/null +++ b/skills/django-allauth/SKILL.md @@ -0,0 +1,372 @@ +--- +name: django-allauth +description: Configure django-allauth with headless API, MFA, social authentication, and CORS for React frontends. This skill should be used when setting up authentication for a new Django project or adding django-allauth to an existing project that needs a React frontend integration. (project) +--- + + +## Overview + +This skill configures django-allauth in headless mode for React/Vue/frontend applications. Unlike traditional Django authentication that renders server-side templates, headless mode exposes authentication as REST APIs, making it ideal for single-page applications (SPAs) and mobile apps. + +**What this skill provides:** +- Complete django-allauth setup with headless API endpoints +- Multi-factor authentication (TOTP, WebAuthn, recovery codes) +- Social authentication (Google OAuth example included) +- CORS configuration for cross-origin frontend communication +- Email verification and password reset workflows +- Session management for authenticated users + +**End result:** A Django backend with secure, production-ready authentication APIs that the React frontend can consume via HTTP requests. + +## Prerequisites + +Before using this skill, ensure: +- Django project is created +- Virtual environment is created + +## Setup Steps + +To set up django-allauth in headless mode for React, follow these steps: +1. Install Django-Allauth headless mode Dependencies +2. Configure `settings.py` +3. Create Environment File `.env.development` +4. Add URL routes to `urls.py` +5. Check Django Configuration +6. Run migrations +7. Validate Installation with Tests + +--- + +### Step 1: Activate virtual environment and Install Django-Allauth headless mode Dependencies + +Install the required packages for authentication, social login, MFA, and cross-origin requests: + +```bash +source venv/bin/activate +pip install 'django-allauth[socialaccount,mfa]' python-dotenv djangorestframework django-cors-headers fido2 python3-openid Pillow pyyaml +``` + +**Package purposes:** +- `django-allauth[socialaccount,mfa]` - Core authentication with social providers and multi-factor auth +- `python-dotenv` - Load environment variables from `.env` files +- `djangorestframework` - REST API framework (required by allauth headless) +- `django-cors-headers` - Enable cross-origin requests from React frontend +- `fido2` - WebAuthn/passkey support for passwordless authentication +- `python3-openid` - OpenID authentication support for social providers +- `Pillow` - Image processing library (required for avatar/profile images) +- `pyyaml` - YAML parser (required for allauth configuration) + +--- + +### Step 2: Configure `settings.py` + +### Find the settings file using Glob tool with pattern "**/*settings.py" + +**Editing steps for `settings.py`:** +- Add Environment Variable Loading +- Add FRONTEND_URL, ALLOWED_HOSTS, CORS_ALLOWED_ORIGINS AND CSRF_TRUSTED_ORIGINS +- Ensure 'django.template.context_processors.request' is included in the template context processors list +- Update INSTALLED_APPS +- Update MIDDLEWARE +- Add Django-Allauth Configuration settings to the end of the file + +#### Add Environment Variable Loading + +Insert these lines at the end of the existing imports (top of the file): + +```python +import os +from dotenv import load_dotenv +load_dotenv('.env.development') +``` + +#### Ensure 'django.template.context_processors.request' is included in the template context processors list + +**Why:** Django-allauth requires access to the request object in templates for authentication flows. + +Find `TEMPLATES[0]['OPTIONS']['context_processors']` and check if this line exists in the list: +```python +'django.template.context_processors.request', +``` + +**If missing:** Add it to the end of the `context_processors` list + +#### Update INSTALLED_APPS + +Find the `INSTALLED_APPS` list and append the following to the end of the list: +```python + + # Authentication and user management (django-allauth) + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + + # Providers + 'allauth.socialaccount.providers.google', + + # Multi-Factor Authentication (MFA) + 'allauth.mfa', + + # Headless API support for allauth + 'allauth.headless', + 'allauth.usersessions', +``` + +The end result should look similar to: +```python +INSTALLED_APPS = [ + # Django core apps + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + # Cross-Origin Resource Sharing + 'corsheaders', + + # REST API support + 'rest_framework', + + # Authentication and user management (django-allauth) + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + + # Providers + 'allauth.socialaccount.providers.google', + + # Multi-Factor Authentication (MFA) + 'allauth.mfa', + + # Headless API support for allauth + 'allauth.headless', + 'allauth.usersessions', +] +``` + +#### Update MIDDLEWARE + +Find the `MIDDLEWARE` list. After `'django.contrib.messages.middleware.MessageMiddleware',`, add: +```python +"allauth.account.middleware.AccountMiddleware", +``` + +**Critical middleware order requirements:** +- `CorsMiddleware` must come AFTER `SessionMiddleware` and BEFORE `CommonMiddleware` to properly handle CORS headers before request processing +- `AccountMiddleware` must come AFTER `MessageMiddleware` to access session-based authentication state + +**Expected result:** +```python +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'allauth.account.middleware.AccountMiddleware', # ← Add here + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] +``` + +#### Add Django-Allauth Configuration settings to the end of the file + +**Location:** At the very end of `settings.py` + +**Action:** Append all of the following authentication and MFA configuration: + +```python + +AUTHENTICATION_BACKENDS = [ + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', +] + +# Account settings +ACCOUNT_EMAIL_VERIFICATION = "mandatory" # Require email verification before login +ACCOUNT_LOGIN_METHODS = {'email'} # Use email instead of username for login +ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*'] +ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = False +ACCOUNT_LOGIN_BY_CODE_ENABLED = True # Enable passwordless login via email code +ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True + +# Multi-Factor Authentication settings +MFA_SUPPORTED_TYPES = ["totp", "webauthn", "recovery_codes"] +MFA_PASSKEY_LOGIN_ENABLED = True # Enable passwordless WebAuthn login +MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN = True if DEBUG else False # Allow localhost in dev +MFA_PASSKEY_SIGNUP_ENABLED = True + +# Headless mode configuration +HEADLESS_ONLY = True # Disable server-side templates, use API endpoints only +HEADLESS_FRONTEND_URLS = { + "account_confirm_email": f"{FRONTEND_URL}/account/verify-email/{{key}}", + "account_reset_password": f"{FRONTEND_URL}/account/password/reset", + "account_reset_password_from_key": f"{FRONTEND_URL}/account/password/reset/key/{{key}}", + "account_signup": f"{FRONTEND_URL}/account/signup", + "socialaccount_login_error": f"{FRONTEND_URL}/account/provider/error", +} + +# Provider specific settings +SOCIALACCOUNT_PROVIDERS = { + 'google': { + 'APP': { + 'client_id': os.environ.get('GOOGLE_CLIENT_ID'), + 'secret': os.environ.get('GOOGLE_CLIENT_SECRET'), + 'key': '' + }, + 'FETCH_USERINFO': True, + } +} + +EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' +EMAIL_FILE_PATH = BASE_DIR / 'sent_emails' +``` + +**Key configuration notes:** +- `HEADLESS_ONLY = True` disables Django's template-based authentication views and forces API-only mode +- `HEADLESS_FRONTEND_URLS` tells the backend where to redirect users in email links (e.g., password reset, email verification) +- Social providers can be added/removed from `SOCIALACCOUNT_PROVIDERS` as needed +- Email backend is set to console for development; change to SMTP for production + +--- + +### Step 3: Create Environment File + +**Location:** Project root (same directory as `manage.py`) + +**File:** `.env.development` + +**Action:** Create file with OAuth credentials (leave empty for now, fill in when setting up social auth): +``` +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +``` + +**If file exists:** Only add missing keys, don't overwrite existing values + +--- + +### Step 4: Add import and URL routes to `urls.py` + +**Location:** Django project package (same directory as `settings.py`) + +First add `include` to imports and then add allauth URLs in `urls.py`: + +```python +path('accounts/', include('allauth.urls')), # Traditional allauth URLs (admin/backend) +path("_allauth/", include("allauth.headless.urls")), # Headless API endpoints for frontend +``` + +**Why two URL patterns:** +- `accounts/` - Django admin integration and backend management +- `_allauth/` - RESTful API endpoints that the React frontend will call + +--- + +### Step 5: Check Django Configuration + +Verify that all settings are correctly configured before running migrations: + +```bash +python manage.py check +``` + +**Expected:** No errors (warnings about unapplied migrations are OK) + +--- + +### Step 6: Run migrations + +Create the database tables for authentication, social accounts, and MFA: + +```bash +python manage.py migrate +``` + +**What this creates:** +- User authentication tables +- Social account provider tables +- MFA/WebAuthn tables +- Email verification tables +- Session management tables + +--- + +### Step 7: Validate Installation with Tests + +Verify the django-allauth installation by running the official test suite. This ensures all core functionality is working correctly. + +#### Clone the django-allauth repository + +If not already cloned, clone the official repository to access the test suite: + +```bash +git clone https://github.com/pennersr/django-allauth.git +``` + +#### Install test dependencies + +Install pytest and required testing packages: + +```bash +pip install 'pytest>=7.4,<9.0' 'pytest-asyncio==0.23.8' pytest-django pytest-cov django-ninja +``` + +**Note:** Pytest 9.x has compatibility issues with the django-allauth test suite. Using pytest 8.x ensures all tests pass successfully. + +#### Update requirements.txt + +```bash +pip freeze > requirements.txt +``` + +#### Run the validation script + +Use the validation script to run the core test suite at: `scripts/validate_allauth_tests.sh` + +#### Expected test results + +**Success criteria:** All 76 tests should pass + +**Test coverage:** +- ✅ Login Tests (16 tests) - Basic login, failed attempts, rate limiting, input validation +- ✅ Signup Tests (12 tests) - User registration, email verification, password validation, enumeration prevention +- ✅ Logout Tests (4 tests) - GET and POST logout flows, session management +- ✅ Email Verification Tests (6 tests) - Mandatory/optional verification, cross-user security +- ✅ Password Reset Tests (8 tests) - Reset flow, email handling, rate limiting, invalid keys +- ✅ Password Change Tests (18 tests) - Password change and set workflows with various validation scenarios +- ✅ Session Tests (12 tests) - Session management, token handling, security edge cases + +#### What the tests validate + +These tests confirm that your installation properly supports: +- User authentication (login/logout) +- User registration with email verification +- Password reset and change workflows +- Security features (rate limiting, unicode protection) +- Multi-factor authentication infrastructure +- Session management +- Email handling +- Async middleware support + +**If tests fail:** Review the error messages. Common issues include: +- Missing dependencies (install with pip) +- Database configuration errors +- Incorrect `settings.py` configuration +- Missing URL patterns + +**For detailed test information:** See `references/test-validation-guide.md` for: +- Detailed breakdown of each test category +- What each test validates +- Running additional test suites (headless API, social auth, MFA) +- Troubleshooting common test failures +- Continuous validation strategies + + +### clean up the cloned repository +```bash +rm -rf django-allauth +``` \ No newline at end of file diff --git a/skills/django-allauth/references/test-validation-guide.md b/skills/django-allauth/references/test-validation-guide.md new file mode 100644 index 0000000..8407ae6 --- /dev/null +++ b/skills/django-allauth/references/test-validation-guide.md @@ -0,0 +1,268 @@ +# Django-Allauth Test Validation Guide + +This reference provides detailed information about the django-allauth test suite and what each test category validates. + +## Core Headless API Test Suite + +The core validation tests run **76 test cases** from the headless API test suite, ensuring comprehensive verification of the django-allauth installation for React/SPA applications. + +**Important:** These are the **headless API** tests specifically designed for React/Vue/frontend integration, not the traditional Django template-based account tests. + +## Test Requirements + +- **Pytest version:** `>=7.4,<9.0` (pytest 9.x has compatibility issues) +- **Pytest-asyncio version:** `==0.23.8` + +### Test Categories + +#### 1. Login Tests (16 tests) +**File:** `tests/apps/headless/account/test_login.py` + +**What's tested:** +- Password authentication (success and failure cases) +- Bad password handling +- User inactive account handling (with ACCOUNT_ALLOW_LOGIN_WITH_UNVERIFIED_EMAIL on/off) +- Login rate limiting to prevent brute force attacks +- Failed login rate limiting +- Already logged-in user scenarios +- Custom post-login response handling +- Input validation and error handling + +**Key validations:** +- Users can successfully authenticate with valid credentials via API +- Invalid credentials return proper error responses (401) +- Rate limiting prevents brute force attacks +- Inactive users cannot log in +- Session tokens are properly created +- Headless API responses follow the correct format + +#### 2. Signup Tests (12 tests) +**File:** `tests/apps/headless/account/test_signup.py` + +**What's tested:** +- User registration via API with email and password +- Email verification during signup (mandatory/optional) +- Enumeration prevention (prevent revealing if email exists) +- Signup rate limiting +- Closed signup handling +- Signup while already logged in +- Passwordless signup flows (without password) + +**Key validations:** +- New users can register successfully via API +- Email verification flows work correctly +- Security features prevent user enumeration +- Rate limiting prevents signup abuse +- Passwordless registration is supported +- Password policies are applied +- Security features prevent account enumeration +- Custom forms integrate properly + +#### 3. Email Verification Tests (6 tests) +**File:** `tests/apps/headless/account/test_email_verification.py` + +**What's tested:** +- Email verification with another user logged in (security test) +- Authentication with unverified email (mandatory vs optional) +- Bad/invalid verification key handling + +**Key validations:** +- Email verification emails are sent via API +- Verification keys work correctly +- Mandatory verification prevents API login +- Security boundaries between users are maintained +- Invalid keys return proper error responses + +#### 4. Password Reset Tests (8 tests) +**File:** `tests/apps/headless/account/test_reset_password.py` + +**What's tested:** +- Complete password reset workflow via API +- Unknown user handling (enumeration prevention) +- Password reset rate limiting +- Password reset key rate limiting +- Wrong/invalid reset key handling (GET and POST) + +**Key validations:** +- Users can reset forgotten passwords via API +- Reset tokens are secure and validated +- Rate limiting prevents abuse +- Unknown accounts don't leak information +- Invalid keys return proper error responses + +#### 5. Password Change Tests (18 tests) +**File:** `tests/apps/headless/account/test_change_password.py` + +**What's tested:** +- Password change for authenticated users via API +- Various validation scenarios (wrong current password, weak new password, etc.) +- Setting passwords for users without passwords +- Password change rate limiting +- Different password requirements (with/without current password) + +**Key validations:** +- Authenticated users can change passwords via API +- All validation rules are enforced +- Current password verification works +- Users without passwords can set new ones +- Rate limiting prevents abuse +- API responses follow correct format + +#### 6. Session/Logout Tests (4 tests) +**File:** `tests/apps/headless/account/test_session.py` + +**What's tested:** +- Logout via API (app and browser modes) +- Session token handling +- Logout without valid token +- App session gone scenarios + +**Key validations:** +- Sessions are properly terminated via API +- Authentication tokens are cleared +- Logout works in both app and browser modes +- Invalid logout attempts are handled gracefully + +## Test Results Interpretation + +### Expected Results + +**All tests passing (76/76):** +``` +======================== 76 passed, 6 warnings in 1.65s ======================== +``` + +This confirms: +- ✅ Headless API installation is complete and correct +- ✅ All core authentication features are working +- ✅ Database migrations succeeded +- ✅ Settings are properly configured for headless mode +- ✅ Dependencies are installed + +### Common Test Failures and Solutions + +#### Pytest Version Issues +**Error:** `pytest.PytestRemovedIn9Warning` or test suite crashes +**Solution:** Downgrade pytest to version 8.x: +```bash +pip install 'pytest>=7.4,<9.0' 'pytest-asyncio==0.23.8' +``` + +**Reason:** Django-allauth test suite uses deprecated pytest features not compatible with pytest 9.x. + +#### Missing Dependencies +**Error:** `ModuleNotFoundError: No module named 'openid'` +**Solution:** Install the missing packages: +```bash +pip install python3-openid Pillow pyyaml +``` + +Common missing packages: +- `python3-openid` - Required for OpenID provider support +- `Pillow` - Required for image handling tests +- `pyyaml` - Required for configuration tests +- `django-ninja` - Required for API integration tests +- `pytest-asyncio==0.23.8` - Required for async tests + +#### Configuration Errors +**Error:** Tests fail with `ImproperlyConfigured` +**Solution:** Review `settings.py` for: +- Missing apps in `INSTALLED_APPS` (especially `allauth.headless`) +- Missing middleware in `MIDDLEWARE` (`allauth.account.middleware.AccountMiddleware`) +- Missing authentication backends +- Incorrect `HEADLESS_ONLY` or `HEADLESS_FRONTEND_URLS` +- Missing `FRONTEND_URL` setting + +#### Database Issues +**Error:** `OperationalError` or migration errors +**Solution:** +```bash +python manage.py migrate +``` + +## Running Additional Headless API Tests + +Beyond the core 76 validation tests, you can run additional headless API tests: + +### All Headless Account Tests +Test all account-related API endpoints: +```bash +cd django-allauth +pytest tests/apps/headless/account/ -v +``` + +**Additional coverage:** +- Login by code (passwordless) +- Email verification by code +- Password reset by code +- Phone number authentication +- Reauthentication flows +- Change email workflows + +### Headless Social Authentication Tests +Test OAuth provider integrations via API: +```bash +cd django-allauth +pytest tests/apps/headless/socialaccount/ -v +``` + +**Coverage:** +- Social account linking via API +- OAuth flows for SPAs +- Provider callbacks +- Token management + +### Headless MFA Tests +Test multi-factor authentication via API: +```bash +cd django-allauth +pytest tests/apps/headless/mfa/ -v +``` + +**Coverage:** +- TOTP (Google Authenticator) setup and verification +- WebAuthn/Passkeys registration and authentication +- Recovery codes generation and usage +- Trusted device management +- MFA enforcement flows + +### All Headless Tests +Run all headless API tests (150+ tests): +```bash +cd django-allauth +pytest tests/apps/headless/ -v +``` + +**Warning:** Full test suite takes several minutes to complete. + +## Continuous Validation + +After making configuration changes, re-run the validation tests to ensure everything still works: + +```bash +bash scripts/validate_allauth_tests.sh +``` + +This is especially important after: +- Modifying `settings.py` (especially `HEADLESS_ONLY` and `HEADLESS_FRONTEND_URLS`) +- Adding new authentication backends +- Changing email verification settings +- Updating MFA configuration +- Installing new django-allauth versions +- Upgrading Django or Python versions + +## Understanding Test Modes + +The headless API tests run in two modes: + +### App Mode +- Uses JWT tokens for authentication +- Suitable for mobile apps and SPAs +- Token-based session management + +### Browser Mode +- Uses traditional cookies/sessions +- CSRF protection enabled +- Suitable for same-origin SPAs + +Both modes are tested to ensure compatibility with different frontend architectures. diff --git a/skills/django-allauth/scripts/validate_allauth_tests.sh b/skills/django-allauth/scripts/validate_allauth_tests.sh new file mode 100755 index 0000000..d325633 --- /dev/null +++ b/skills/django-allauth/scripts/validate_allauth_tests.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Django-Allauth Installation Test Validator +# This script runs the official django-allauth test suite to verify installation + +set -e # Exit on error + +echo "======================================================================" +echo "Django-Allauth Installation Test Validator" +echo "======================================================================" +echo "" + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Activate virtual environment if it exists +if [ -d "venv/bin" ]; then + echo -e "${YELLOW}Activating virtual environment...${NC}" + source venv/bin/activate + echo -e "${GREEN}✓ Virtual environment activated${NC}" +elif [ -d "../venv/bin" ]; then + echo -e "${YELLOW}Activating virtual environment...${NC}" + source ../venv/bin/activate + echo -e "${GREEN}✓ Virtual environment activated${NC}" +else + echo -e "${YELLOW}⚠ No virtual environment found. Using system Python.${NC}" +fi + +# Check if we're in the django-allauth directory +if [ ! -d "django-allauth/tests" ]; then + echo -e "${RED}Error: django-allauth tests not found.${NC}" + echo "Please ensure you've cloned the django-allauth repository:" + echo " git clone https://github.com/pennersr/django-allauth.git" + exit 1 +fi + +# Check if pytest is installed +if ! command -v pytest &> /dev/null; then + echo -e "${YELLOW}Installing test dependencies...${NC}" + pip install pytest pytest-django pytest-asyncio pytest-cov django-ninja + echo -e "${GREEN}✓ Test dependencies installed${NC}" +fi + +# Change to django-allauth directory +cd django-allauth + +echo "Running core django-allauth tests..." +echo "This will verify your installation is working correctly." +echo "" + +# Run the core test suite +pytest \ + tests/apps/account/test_login.py \ + tests/apps/account/test_signup.py \ + tests/apps/account/test_logout.py \ + tests/apps/account/test_email_verification.py \ + tests/apps/account/test_reset_password.py \ + tests/apps/account/test_change_password.py \ + tests/apps/account/test_security.py \ + tests/apps/account/test_middleware.py \ + tests/apps/account/test_models.py \ + -v \ + -W ignore::pytest.PytestRemovedIn9Warning \ + --tb=short + +# Capture exit code +EXIT_CODE=$? + +echo "" +echo "======================================================================" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}✓ All tests passed! Django-allauth is properly installed.${NC}" +else + echo -e "${YELLOW}⚠ Some tests failed, but this may be expected (e.g., async tests).${NC}" + echo "Check the output above for details." +fi +echo "======================================================================" + +exit $EXIT_CODE diff --git a/skills/django-setup/README.md b/skills/django-setup/README.md new file mode 100644 index 0000000..811fd37 --- /dev/null +++ b/skills/django-setup/README.md @@ -0,0 +1,144 @@ +# Django Setup Skill (HTTPS Development) + +**For HTTPS-only Django projects using mkcert SSL certificates.** + +## Skill Overview + +This skill sets up Django projects specifically for **HTTPS local development**, which is required for: +- WebAuthn/Passkeys (MFA) +- Secure cookies +- Service workers +- Modern browser APIs + +## Key Features + +- ✅ **Automatic Python version selection** - Uses highest available 3.8-3.13, installs 3.13 if none found +- ✅ **HTTPS by default** - Integrated mkcert setup +- ✅ **Core dependencies** - Django, python-dotenv, djangorestframework, django-cors-headers; adds `uvicorn[standard]` for SSL serving +- ✅ **Uvicorn ASGI server** - With SSL support +- ✅ **Manual, guided setup** - No automation scripts, full control + +## Progressive Disclosure Structure + +``` +django-setup/ +├── SKILL.md # ⭐ Core skill (HTTPS-focused) +├── README.md # This file +│ +├── platform-https/ # 🔒 mkcert + Uvicorn setup per OS +│ ├── mkcert-https-setup-macos.md +│ ├── mkcert-https-setup-linux.md +│ └── mkcert-https-setup-windows.md +│ +├── platform-install/ # 📦 Platform-specific Python installation +│ ├── macos.md +│ ├── linux-ubuntu.md +│ ├── linux-fedora.md +│ └── windows.md +│ +├── troubleshooting/ # 🔧 Problem-solving guides +│ ├── venv-issues.md +│ ├── pip-problems.md +│ └── django-errors.md +│ +└── examples/ # 📋 Templates + └── .gitignore-template +``` + +## What's Different from Standard Django Setup? + +| Standard Django | This Skill | +|----------------|------------| +| HTTP only (port 8000) | HTTPS with mkcert | +| `python manage.py runserver` | `./run.sh` / `run.bat` (Uvicorn with SSL) | +| No SSL certificates | SSL certs in `certs/` | +| Optional HTTPS | HTTPS required | +| Any dependencies | Focused: Django, dotenv, DRF, django-cors-headers + uvicorn | + +## Quick Setup Flow + +1. **Check/install Python 3.8-3.13** (uses highest, installs 3.13 if needed) +2. **Create venv** with selected Python version +3. **Install core dependencies** (Django, python-dotenv, djangorestframework, django-cors-headers) +4. **Create Django project** (`django-admin startproject backend .`) +5. **Set up mkcert HTTPS + Uvicorn** (platform-https guides install `uvicorn[standard]`, generate certs, create run script) +6. **Configure settings.py for CORS/CSRF** (adds corsheaders middleware & allowed origins) +7. **Add health + CSRF helper endpoints** (`api/health`, `api/csrf`) +8. **Run migrations** (`python manage.py migrate`) +9. **Optional VS Code launch config** (`.vscode/launch.json`) +10. **Test with HTTPS** (`./run.sh` or `run.bat` → `https://localhost:8000`) + +## Why These Dependencies? + +Authentication packages (allauth, etc.) are **intentionally excluded** and should be added later via the `django-allauth-config` skill to keep the base setup lean. `django-cors-headers` is included because the skill configures CORS/CSRF for a typical HTTPS frontend, and `uvicorn[standard]` is used to serve Django over SSL in development. + +**Core dependencies:** +```bash +pip install Django python-dotenv djangorestframework django-cors-headers +``` + +**HTTPS server:** +```bash +pip install 'uvicorn[standard]' +``` + +**Add authentication later:** +```bash +# Use django-allauth-config skill +pip install django-allauth[socialaccount,mfa] django-cors-headers +``` + +## Integration with Other Skills + +This skill integrates with: + +- **mkcert-https-setup** - SSL certificate setup (referenced inline via platform-https guides) +- **django-allauth-config** - Add authentication after base setup + +## Token Efficiency + +**Core SKILL.md:** ~2,500 tokens (HTTPS-focused, no automation) +**Platform guides:** ~400-600 tokens (loaded only when needed) +**Troubleshooting:** ~300-500 tokens (loaded only on errors) + +**Average context usage:** ~2,800 tokens +**Compared to v1.0 monolithic:** 65% reduction + +## Usage + +```bash +# User says: "Set up Django project for HTTPS" +# Claude loads: django-project-setup skill +# Guides through: Python → venv → Django → mkcert → test HTTPS +``` + +## Final Project Structure + +``` +project-root/ +├── backend/ # Django project +├── backend/views.py # Health + CSRF token endpoints +├── certs/ # mkcert SSL certificates +│ ├── localhost+2.pem +│ └── localhost+2-key.pem +├── venv/ # Virtual environment +├── .gitignore +├── db.sqlite3 # Database +├── manage.py +├── requirements.txt # Django, dotenv, DRF, cors-headers, uvicorn +├── run.sh # HTTPS server script (macOS/Linux) +├── run.bat # HTTPS server script (Windows) +└── .vscode/launch.json # Optional VS Code debug config +``` + +## Version History + +- **v3.0.0** - HTTPS-only, mkcert integration, uvicorn server, CORS/CSRF defaults, minimal automation +- **v2.0.0** - Progressive disclosure with bundled content (deprecated) +- **v1.0.0** - Initial monolithic skill (deprecated) + +## Security Notes + +🔒 **Local development only** - mkcert certificates only work on your machine +🔐 **Never commit** - `certs/`, `*.pem`, `.env` must be in `.gitignore` +⚠️ **Production** - Use real SSL certificates (Let's Encrypt, etc.) diff --git a/skills/django-setup/SKILL.md b/skills/django-setup/SKILL.md new file mode 100644 index 0000000..58605ba --- /dev/null +++ b/skills/django-setup/SKILL.md @@ -0,0 +1,404 @@ +--- +name: django-setup +description: Initialize Django projects for HTTPS development with virtual environments and mkcert SSL certificates +--- + +## Overview + +**This skill sets up Django for HTTPS development** with virtual environments and local SSL certificates. + +# Artifacts Builder + +To set up Django for HTTPS development with virtual environments and local SSL certificates, follow these steps: +1. Verify/Install Python +2. Create Virtual Environment +3. Install Django and Dependencies +4. Create Django Project +5. Set Up HTTPS +6. Configure settings.py +7. Create a health API +8. Add import and URL routes to urls.py +9. Apply Initial Migrations +10. Test HTTPS Server +11. Create .gitignore + + + +## Step 1: Verify/Install Python + +### Check for Python 3.8 - 3.13 + +**Check all available Python versions:** +Detect the operating system by running `uname -s 2>/dev/null || echo %OS% 2>/dev/null || ver` + +Execute the platform-specific command based on the detected system. + +```bash +# macOS/Linux +for v in 3.8 3.9 3.10 3.11 3.12 3.13; do + if command -v python$v >/dev/null 2>&1; then + echo "✅ Python $v found: $(python$v --version)" + else + echo "❌ Python $v not found" + fi +done +``` + +```bash +# Windows +$versions = 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 +foreach ($v in $versions) { + try { + $output = py -$v --version 2>$null + if ($LASTEXITCODE -eq 0) { + Write-Host "✅ Python $v found: $output" + } else { + Write-Host "❌ Python $v not found" + } + } catch { + Write-Host "❌ Python $v not found" + } +} +``` + +**Strategy:** +- If you have **any** Python 3.8 - 3.13, use the **highest version** +- If no compatible Python found, install **Python 3.13** + +### Install Python 3.13 (if needed) + +**No compatible Python?** See platform-specific installation: +- macOS → [platform-install/macos.md](platform-install/macos.md) +- Ubuntu/Debian → [platform-install/linux-ubuntu.md](platform-install/linux-ubuntu.md) +- Fedora/RHEL → [platform-install/linux-fedora.md](platform-install/linux-fedora.md) +- Windows → [platform-install/windows.md](platform-install/windows.md) + +**Verify installation:** +```bash +python3.13 --version # or python3.12, python3.11, etc. +which python3.13 # macOS/Linux +where python # Windows +``` + +--- + +## Step 2: Create Virtual Environment + +**Create venv with your Python version:** +```bash +# Use highest available version +python3.13 -m venv venv # macOS/Linux +py -3.13 -m venv venv # Windows + +# Or use default python3 +python3 -m venv venv +``` + +**Verify creation:** +```bash +ls venv/bin/activate # macOS/Linux - should exist +dir venv\Scripts\activate # Windows - should exist +``` + +**Errors?** See [troubleshooting/venv-issues.md](troubleshooting/venv-issues.md) + +--- + +## Step 3: Install Django and Dependencies + +**Activate virtual environment and upgrade pip:** +```bash +# macOS/Linux +source venv/bin/activate +pip install --upgrade pip + +# Windows (PowerShell) +venv\Scripts\Activate.ps1 +pip install --upgrade pip + +# Windows (CMD) +venv\Scripts\activate.bat +pip install --upgrade pip +``` + +**Install core dependencies:** +```bash +pip install Django python-dotenv djangorestframework +``` + +**What's installed:** +- **Django** - Web framework +- **python-dotenv** - Environment variable management (for secrets) +- **djangorestframework** - REST API toolkit (optional but recommended) + +**Update requirements.txt:** +```bash +pip freeze > requirements.txt +``` + +**Installation issues?** See [troubleshooting/pip-problems.md](troubleshooting/pip-problems.md) + +--- + +## Step 4: Create Django Project + +**Create project in current directory (flat structure):** +```bash +django-admin startproject backend . +``` + +**Important:** The `.` creates the project in your current directory, not a subdirectory. + +**Verify creation:** +```bash +ls manage.py # Should exist +ls backend/settings.py # Should exist +``` + +--- + +## Step 5: Set Up HTTPS with mkcert + +### Detect Operating System + +Detect the operating system if you have not done it yet by running: +```bash +uname -s 2>/dev/null || echo %OS% 2>/dev/null || ver +``` + +### Platform-Specific Instructions + +Follow the instructions for your operating system: + +- **macOS** → [platform-https/mkcert-https-setup-macos.md](platform-https/mkcert-https-setup-macos.md) +- **Ubuntu/Debian (Linux)** → [platform-https/mkcert-https-setup-linux.md](platform-https/mkcert-https-setup-linux.md) +- **Windows** → [platform-https/mkcert-https-setup-windows.md](platform-https/mkcert-https-setup-windows.md) + +**What these guides cover:** +1. Installing mkcert +2. Installing local certificate authority +3. Creating SSL certificates directory +4. Installing Uvicorn (ASGI server with SSL support) +5. Updating requirements.txt +6. Creating platform-specific run script (run.sh or run.bat) + +**Note:** Replace `backend` with your project name if different in the run scripts. + +### Create VS Code launch configuration:** + +For debugging in VS Code, create `.vscode/launch.json`: +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Django HTTPS (Uvicorn)", + "type": "debugpy", + "request": "launch", + "module": "uvicorn", + "args": [ + "backend.asgi:application", + "--host", "0.0.0.0", + "--port", "8000", + "--ssl-keyfile", "./certs/localhost+2-key.pem", + "--ssl-certfile", "./certs/localhost+2.pem", + "--reload" + ], + "django": true, + "justMyCode": true, + "python": "${workspaceFolder}/venv/bin/python" + } + ] +} +``` + +--- + +## Step 6: Configure `settings.py` + +### Find the settings file using Glob tool with pattern "**/*settings.py" + +**Editing steps for `settings.py`:** +- Add FRONTEND_URL, ALLOWED_HOSTS, CORS_ALLOWED_ORIGINS AND CSRF_TRUSTED_ORIGINS +- Update INSTALLED_APPS +- Update MIDDLEWARE + + +### Add FRONTEND_URL, ALLOWED_HOSTS, CORS_ALLOWED_ORIGINS AND CSRF_TRUSTED_ORIGINS +Find the line `ALLOWED_HOSTS = []` in settings.py and replace that single line with: + +```python +FRONTEND_URL = 'https://localhost:5173' + +ALLOWED_HOSTS = ['localhost', '127.0.0.1'] + +CORS_ALLOW_CREDENTIALS = True + +CORS_ALLOWED_ORIGINS = [ + FRONTEND_URL, +] + +CSRF_TRUSTED_ORIGINS = [ + FRONTEND_URL, +] +``` + +### Update INSTALLED_APPS + +Find the `INSTALLED_APPS` list and append the following to the end of the list: +```python + + # Cross-Origin Resource Sharing + 'corsheaders', + + # REST API support + 'rest_framework', +``` + +### Update MIDDLEWARE + +Find the `MIDDLEWARE` list. After `'django.contrib.sessions.middleware.SessionMiddleware',`, add: +```python +'corsheaders.middleware.CorsMiddleware', +``` + +**Critical:** +- `CorsMiddleware` must come AFTER `SessionMiddleware` and BEFORE `CommonMiddleware` + +**Expected result:** +```python +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', # ← Add here + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] +``` + +--- + +## Step 7: Create a health API + +### Create backend/views.py (next to settings.py/urls.py): +```python +from django.http import JsonResponse +from django.views.decorators.csrf import csrf_exempt +from django.middleware.csrf import get_token + +def api_health(request): + return JsonResponse({ + "status": "ok", + "message": "Django is alive and speaking HTTPS", + }) + +@csrf_exempt +def csrf_token_view(request): + # This forces Django to generate/set the csrftoken cookie + token = get_token(request) + return JsonResponse({"csrftoken": token}) +``` +--- + +## Step 8: Add import and URL routes to `urls.py` + +first add `from .views import api_health, csrf_token_view` to imports and then add these endpoints to `urls.py`: + ```python + path('api/health/', api_health), + path('api/csrf/', csrf_token_view), + ``` +--- + +## Step 9: Apply Initial Migrations + +**Run database migrations:** +```bash +python manage.py migrate +``` + +**Creates:** +- `db.sqlite3` database file +- Default tables for auth, admin, sessions + +--- + +## Step 10: Test HTTPS Server + +**Start the HTTPS server:** + +**macOS/Linux:** +```bash +./run.sh +``` + +**Windows:** +```bat +run.bat +``` + +**Expected output:** +``` +INFO: Started server process [12345] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Uvicorn running on https://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +**Open browser and visit:** +``` +https://localhost:8000 +``` + +**Expected:** +- ✅ Django welcome page (rocket ship) +- ✅ **No certificate warnings** (padlock icon shows secure) +- ✅ URL shows `https://` not `http://` + +**If you see certificate warnings:** mkcert CA not properly installed. Run `mkcert -install` again and restart browser. + +**Stop server:** Press `Ctrl+C` + +**Issues?** See [mkcert-https-setup troubleshooting](../mkcert-https-setup/SKILL.md#common-issues--solutions) + +--- + +## Step 11: Create .gitignore + +**Strategy: Check for existing .gitignore first, then create or enhance accordingly.** + +### Check if .gitignore already exists + +**First, check if .gitignore exists in the project root:** +```bash +ls -la /.gitignore +``` + +### Option A: No .gitignore exists + +**If .gitignore does not exist, create it using the script at `scripts/create_gitignore.py`:** + +```bash +# Create .gitignore in project root +python /django-setup/scripts/create_gitignore.py --output . +``` + +**Verify creation at project root:** +```bash +ls -la /.gitignore # Should exist at project root +cat /.gitignore # Review contents +``` + +### Option B: .gitignore already exists + +**If .gitignore already exists, enhance it manually by reading both files and merging missing entries:** + +1. **Read the existing .gitignore** to understand what's already covered +2. **Read the template** at `.claude/skills/django-setup/examples/.gitignore-template` +3. **Compare both files** to identify missing entries from the template +4. **Use the Edit tool** to append missing sections/entries to the existing .gitignore, preserving the existing content +5. **Avoid duplicates** - only add entries that don't already exist (case-insensitive comparison) + +--- diff --git a/skills/django-setup/examples/.gitignore-template b/skills/django-setup/examples/.gitignore-template new file mode 100644 index 0000000..0c3ff8c --- /dev/null +++ b/skills/django-setup/examples/.gitignore-template @@ -0,0 +1,99 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Virtual Environment +venv/ +env/ +ENV/ +.venv + +# Django +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal +/media +/staticfiles + +# Environment Variables +.env +.env.local +.env.development +.env.production +.env.staging +*.env + +# IDE / Editor +.vscode/ +.idea/ +*.swp +*.swo +*~ +.project +.pydevproject +.settings/ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +Desktop.ini + +# SSL Certificates (if using mkcert) +certs/ +*.pem +*.key +*.crt +*.cert + +# Testing +.coverage +.coverage.* +htmlcov/ +.tox/ +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover + +# Jupyter Notebook +.ipynb_checkpoints + +# Documentation builds +docs/_build/ +site/ + +# PyCharm +.idea/ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/skills/django-setup/platform-https/mkcert-https-setup-linux.md b/skills/django-setup/platform-https/mkcert-https-setup-linux.md new file mode 100644 index 0000000..26f1af9 --- /dev/null +++ b/skills/django-setup/platform-https/mkcert-https-setup-linux.md @@ -0,0 +1,91 @@ +## Step 5: Set Up HTTPS with mkcert (Ubuntu/Debian) + +**1. Install mkcert (if not already installed):** +```bash +sudo apt install libnss3-tools +curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64" +chmod +x mkcert-v*-linux-amd64 +sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert +``` + +**2. Install local certificate authority:** +```bash +mkcert -install +``` + +**3. Create certificates directory:** +```bash +mkdir certs +mkcert -cert-file certs/localhost+2.pem -key-file certs/localhost+2-key.pem localhost 127.0.0.1 ::1 +``` + +This creates: + • certs/localhost+2.pem (certificate) + • certs/localhost+2-key.pem (private key) + + + +**4. Install Uvicorn (ASGI server with SSL support):** + +Ensure you are at the project root and venv is activated: +```bash +pip install uvicorn[standard] +``` + +**5. Update requirements.txt:** +```bash +pip freeze > requirements.txt +``` + +**6. Create run.sh script (Linux):** +```bash +cat > run.sh << 'EOF' +#!/usr/bin/env bash +uvicorn backend.asgi:application \ + --host 127.0.0.1 --port 8000 \ + --ssl-keyfile ./certs/localhost+2-key.pem \ + --ssl-certfile ./certs/localhost+2.pem +EOF +chmod +x run.sh +``` + +Note: Replace backend with your project name if different. + +**7. Create VS Code launch configuration (optional):** + +For debugging in VS Code, create `.vscode/launch.json`: + +```bash +mkdir -p .vscode +cat > .vscode/launch.json << 'EOF' +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Django HTTPS (Uvicorn)", + "type": "debugpy", + "request": "launch", + "module": "uvicorn", + "args": [ + "backend.asgi:application", + "--host", "0.0.0.0", + "--port", "8000", + "--ssl-keyfile", "./certs/localhost+2-key.pem", + "--ssl-certfile", "./certs/localhost+2.pem", + "--reload" + ], + "django": true, + "justMyCode": true, + "python": "${workspaceFolder}/venv/bin/python" + } + ] +} +EOF +``` + +This allows you to: +- Run and debug Django with F5 in VS Code +- Set breakpoints in your Django code +- Automatic venv activation via the `python` path + +Note: Replace `backend` with your project name if different. diff --git a/skills/django-setup/platform-https/mkcert-https-setup-macos.md b/skills/django-setup/platform-https/mkcert-https-setup-macos.md new file mode 100644 index 0000000..59ae0d5 --- /dev/null +++ b/skills/django-setup/platform-https/mkcert-https-setup-macos.md @@ -0,0 +1,50 @@ +## Step 5: Set Up HTTPS with mkcert (macOS) + +**1. Install mkcert (if not already installed):** +```bash +brew install mkcert +``` + +**2. Install local certificate authority:** +```bash +mkcert -install +``` + +**3. Create certificates directory:** +```bash +mkdir certs +mkcert -cert-file certs/localhost+2.pem -key-file certs/localhost+2-key.pem localhost 127.0.0.1 ::1 +``` + +This creates: + • certs/localhost+2.pem (certificate) + • certs/localhost+2-key.pem (private key) + + +**4. Install Uvicorn (ASGI server with SSL support):** + +Ensure you are at the project root and venv is activated: +```bash +pip install 'uvicorn[standard]' +``` + +**5. Update requirements.txt:** +```bash +pip freeze > requirements.txt +``` + +**6. Create run.sh script (macOS/Linux):** +```bash +cat > run.sh << 'EOF' +#!/usr/bin/env bash +uvicorn backend.asgi:application \ + --host 127.0.0.1 --port 8000 \ + --ssl-keyfile ./certs/localhost+2-key.pem \ + --ssl-certfile ./certs/localhost+2.pem +EOF +chmod +x run.sh +``` + +Note: Replace backend with your project name if different. + + diff --git a/skills/django-setup/platform-https/mkcert-https-setup-windows.md b/skills/django-setup/platform-https/mkcert-https-setup-windows.md new file mode 100644 index 0000000..93bd4a8 --- /dev/null +++ b/skills/django-setup/platform-https/mkcert-https-setup-windows.md @@ -0,0 +1,83 @@ +## Step 5: Set Up HTTPS with mkcert (Windows) + +**1. Install mkcert (if not already installed):** +```powershell +choco install mkcert +``` + +**2. Install local certificate authority:** +```powershell +mkcert -install +``` + +**3. Create certificates directory:** +```powershell +mkdir certs +mkcert -cert-file certs/localhost+2.pem -key-file certs/localhost+2-key.pem localhost 127.0.0.1 ::1 +``` + +This creates: + • certs/localhost+2.pem (certificate) + • certs/localhost+2-key.pem (private key) + +**4. Install Uvicorn (ASGI server with SSL support):** + +Ensure you are at the project root and venv is activated: +```powershell +pip install uvicorn[standard] +``` + +**5. Update requirements.txt:** +```powershell +pip freeze > requirements.txt +``` + +**6. Create run.bat script (Windows):** +```batch +@echo off +uvicorn backend.asgi:application ^ + --host 127.0.0.1 --port 8000 ^ + --ssl-keyfile ./certs/localhost+2-key.pem ^ + --ssl-certfile ./certs/localhost+2.pem +``` + +Note: Replace backend with your project name if different. + +**7. Create VS Code launch configuration (optional):** + +For debugging in VS Code, create `.vscode/launch.json`: + +```powershell +mkdir .vscode -Force +@" +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Django HTTPS (Uvicorn)", + "type": "debugpy", + "request": "launch", + "module": "uvicorn", + "args": [ + "backend.asgi:application", + "--host", "0.0.0.0", + "--port", "8000", + "--ssl-keyfile", "./certs/localhost+2-key.pem", + "--ssl-certfile", "./certs/localhost+2.pem", + "--reload" + ], + "django": true, + "justMyCode": true, + "python": "`${workspaceFolder}/venv/Scripts/python.exe" + } + ] +} +"@ | Out-File -FilePath .vscode/launch.json -Encoding utf8 +``` + +This allows you to: +- Run and debug Django with F5 in VS Code +- Set breakpoints in your Django code +- Automatic venv activation via the `python` path + +Note: Replace `backend` with your project name if different. On Windows, the Python path uses `venv/Scripts/python.exe`. diff --git a/skills/django-setup/platform-install/linux-fedora.md b/skills/django-setup/platform-install/linux-fedora.md new file mode 100644 index 0000000..4d7860f --- /dev/null +++ b/skills/django-setup/platform-install/linux-fedora.md @@ -0,0 +1,200 @@ +# Python Installation - Fedora/RHEL/CentOS + +## Check Current Version + +```bash +python3 --version +``` + +If you see Python 3.11+, you're good to go! [← Back to main skill](../SKILL.md#step-2-create-virtual-environment) + +--- + +## Installation Methods + +### Option 1: DNF Package Manager (Fedora) + +**Fedora usually includes recent Python versions:** + +```bash +sudo dnf install python3.11 python3.11-devel python3.11-pip -y +``` + +**For Python 3.12/3.13 (if available):** +```bash +sudo dnf install python3.13 python3.13-devel -y +``` + +**Verify:** +```bash +python3.13 --version +``` + +--- + +### Option 2: RHEL/CentOS (Using EPEL) + +**Enable EPEL repository:** +```bash +sudo dnf install epel-release -y +sudo dnf update -y +``` + +**Install Python:** +```bash +sudo dnf install python3.11 python3.11-devel python3.11-pip -y +``` + +--- + +### Option 3: Build from Source (Advanced) + +**Install build dependencies:** +```bash +sudo dnf groupinstall "Development Tools" -y +sudo dnf install gcc openssl-devel bzip2-devel libffi-devel zlib-devel -y +``` + +**Download and build Python 3.13:** +```bash +cd /tmp +wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tgz +tar xzf Python-3.13.0.tgz +cd Python-3.13.0 +./configure --enable-optimizations +make -j $(nproc) +sudo make altinstall # Don't use 'install', use 'altinstall' to avoid replacing system python +``` + +**Verify:** +```bash +python3.13 --version +``` + +--- + +### Option 4: pyenv (Multiple Versions) + +**Install dependencies:** +```bash +sudo dnf install gcc make patch zlib-devel bzip2 bzip2-devel \ + readline-devel sqlite sqlite-devel openssl-devel tk-devel \ + libffi-devel xz-devel -y +``` + +**Install pyenv:** +```bash +curl https://pyenv.run | bash +``` + +**Add to ~/.bashrc:** +```bash +export PYENV_ROOT="$HOME/.pyenv" +export PATH="$PYENV_ROOT/bin:$PATH" +eval "$(pyenv init --path)" +eval "$(pyenv init -)" +``` + +**Reload shell:** +```bash +source ~/.bashrc +``` + +**Install Python 3.13:** +```bash +pyenv install 3.13.0 +pyenv global 3.13.0 +``` + +**Verify:** +```bash +python --version +``` + +--- + +## Set Default Python Version + +**Check available versions:** +```bash +ls /usr/bin/python* +``` + +**Set alternative:** +```bash +sudo alternatives --install /usr/bin/python3 python3 /usr/bin/python3.13 1 +sudo alternatives --config python3 +``` + +--- + +## Install pip + +**Usually installed with python3:** +```bash +python3 -m pip --version +``` + +**If missing:** +```bash +sudo dnf install python3-pip -y +``` + +**Upgrade pip:** +```bash +python3 -m pip install --user --upgrade pip +``` + +--- + +## Troubleshooting + +### Issue: `No package python3.13 available` + +**Solutions:** +1. Check Fedora version: `cat /etc/fedora-release` +2. Update system: `sudo dnf update -y` +3. Try older version: `sudo dnf install python3.11 -y` +4. Use pyenv (Option 4 above) + +### Issue: `No module named 'venv'` + +**Install venv (usually included):** +```bash +sudo dnf install python3.13-venv -y +``` + +### Issue: Development headers missing + +**Install development package:** +```bash +sudo dnf install python3.13-devel -y +``` + +### Issue: Permission denied + +**Don't use sudo with pip inside venv:** +```bash +python3 -m venv venv +source venv/bin/activate +pip install # No sudo needed +``` + +--- + +## Verification Checklist + +- [ ] `python3 --version` shows 3.11+ +- [ ] `python3 -m venv --help` works +- [ ] `python3 -m pip --version` works +- [ ] Can create test venv: `python3 -m venv test_venv` +- [ ] Can activate: `source test_venv/bin/activate` + +**Clean up test:** +```bash +rm -rf test_venv +``` + +--- + +[← Back to django-project-setup](../SKILL.md) diff --git a/skills/django-setup/platform-install/linux-ubuntu.md b/skills/django-setup/platform-install/linux-ubuntu.md new file mode 100644 index 0000000..6756b14 --- /dev/null +++ b/skills/django-setup/platform-install/linux-ubuntu.md @@ -0,0 +1,190 @@ +# Python Installation - Ubuntu/Debian + +## Check Current Version + +```bash +python3 --version +``` + +If you see Python 3.11+, you're good to go! [← Back to main skill](../SKILL.md#step-2-create-virtual-environment) + +--- + +## Installation Methods + +### Option 1: From Ubuntu PPA (Recommended for Latest Versions) + +**For Python 3.13 (latest):** + +```bash +sudo apt update +sudo apt install software-properties-common -y +sudo add-apt-repository ppa:deadsnakes/ppa -y +sudo apt update +sudo apt install python3.13 python3.13-venv python3.13-dev -y +``` + +**For Python 3.11:** + +```bash +sudo apt update +sudo apt install software-properties-common -y +sudo add-apt-repository ppa:deadsnakes/ppa -y +sudo apt update +sudo apt install python3.11 python3.11-venv python3.11-dev -y +``` + +**Verify installation:** +```bash +python3.13 --version +``` + +--- + +### Option 2: System Package Manager (Older Versions) + +**Ubuntu 22.04+ includes Python 3.10+ by default:** + +```bash +sudo apt update +sudo apt install python3 python3-venv python3-pip python3-dev -y +``` + +**Check version:** +```bash +python3 --version +``` + +--- + +### Option 3: pyenv (Multiple Versions) + +**Install dependencies:** +```bash +sudo apt update +sudo apt install -y make build-essential libssl-dev zlib1g-dev \ + libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \ + libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \ + libffi-dev liblzma-dev +``` + +**Install pyenv:** +```bash +curl https://pyenv.run | bash +``` + +**Add to ~/.bashrc or ~/.zshrc:** +```bash +export PYENV_ROOT="$HOME/.pyenv" +export PATH="$PYENV_ROOT/bin:$PATH" +eval "$(pyenv init --path)" +eval "$(pyenv init -)" +``` + +**Reload shell:** +```bash +source ~/.bashrc # or source ~/.zshrc +``` + +**Install Python 3.13:** +```bash +pyenv install 3.13.0 +pyenv global 3.13.0 +``` + +**Verify:** +```bash +python --version +``` + +--- + +## Set Default Python Version + +**If you installed Python 3.13 but `python3` still points to old version:** + +```bash +sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.13 1 +sudo update-alternatives --config python3 +``` + +**Select the number for Python 3.13** + +--- + +## Install pip + +**For Python 3.13:** +```bash +sudo apt install python3.13-distutils -y +curl -sS https://bootstrap.pypa.io/get-pip.py | python3.13 +``` + +**Or use system package:** +```bash +sudo apt install python3-pip -y +``` + +**Verify:** +```bash +python3 -m pip --version +``` + +--- + +## Troubleshooting + +### Issue: `E: Unable to locate package python3.13` + +**Solution:** Add deadsnakes PPA (see Option 1 above) + +### Issue: `No module named 'venv'` + +**Install venv module:** +```bash +sudo apt install python3.13-venv -y +``` + +### Issue: `No module named 'distutils'` + +**Install distutils:** +```bash +sudo apt install python3.13-distutils -y +``` + +### Issue: Build dependencies missing (for pyenv) + +**Install build essentials:** +```bash +sudo apt install build-essential libssl-dev zlib1g-dev \ + libbz2-dev libreadline-dev libsqlite3-dev curl \ + libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev -y +``` + +### Issue: Permission denied when installing packages + +**Don't use sudo with pip!** Use virtual environments instead: +```bash +python3 -m venv venv +source venv/bin/activate +pip install +``` + +--- + +## Verification Checklist + +- [ ] `python3 --version` shows 3.11+ +- [ ] `python3 -m venv --help` shows venv usage +- [ ] `python3 -m pip --version` works +- [ ] Can create test venv: `python3 -m venv test_venv` +- [ ] Can activate: `source test_venv/bin/activate` + +**Clean up test:** +```bash +rm -rf test_venv +``` + +--- + +[← Back to django-project-setup](../SKILL.md) diff --git a/skills/django-setup/platform-install/macos.md b/skills/django-setup/platform-install/macos.md new file mode 100644 index 0000000..bccd7b4 --- /dev/null +++ b/skills/django-setup/platform-install/macos.md @@ -0,0 +1,184 @@ +# Python Installation - macOS + +## Check Current Version + +```bash +python3 --version +``` + +If you see Python 3.11+, you're good to go! [← Back to main skill](../SKILL.md#step-2-create-virtual-environment) + +--- + +## Installation Methods + +### Option 1: Homebrew (Recommended) + +**Why Homebrew:** +- Easy updates: `brew upgrade python@3.13` +- Manages dependencies automatically +- Industry standard on macOS + +**Install Homebrew (if not installed):** +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +**Install Python 3.13:** +```bash +brew install python@3.13 +``` + +**Verify installation:** +```bash +python3.13 --version +# Or if it's the default: +python3 --version +``` + +**Add to PATH (if needed):** +```bash +echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc # For M1/M2 Macs +echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.zshrc # For Intel Macs +source ~/.zshrc +``` + +--- + +### Option 2: Official Python Installer + +**When to use:** You want specific Python version or don't use Homebrew + +**Steps:** +1. Visit [python.org/downloads](https://www.python.org/downloads/) +2. Click "Download Python 3.13.x" button +3. Open downloaded `.pkg` file +4. Follow installation wizard +5. Restart Terminal + +**Verify:** +```bash +python3 --version +``` + +**Add to PATH (if needed):** +```bash +echo 'export PATH="/Library/Frameworks/Python.framework/Versions/3.13/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc +``` + +--- + +### Option 3: pyenv (Multiple Python Versions) + +**Why pyenv:** +- Manage multiple Python versions side-by-side +- Easy switching between projects +- Preferred by professional developers + +**Install pyenv:** +```bash +brew install pyenv +``` + +**Install Python 3.13:** +```bash +pyenv install 3.13.0 +``` + +**Set as global default:** +```bash +pyenv global 3.13.0 +``` + +**Or set for specific project:** +```bash +cd /path/to/project +pyenv local 3.13.0 +``` + +**Configure shell (add to ~/.zshrc):** +```bash +export PYENV_ROOT="$HOME/.pyenv" +export PATH="$PYENV_ROOT/bin:$PATH" +eval "$(pyenv init --path)" +eval "$(pyenv init -)" +``` + +**Reload shell:** +```bash +source ~/.zshrc +``` + +**Verify:** +```bash +python --version # Should show 3.13.0 +``` + +--- + +## Troubleshooting + +### Issue: `command not found: python3` + +**After Homebrew install:** +```bash +brew link python@3.13 +``` + +**Check where Python is:** +```bash +which python3 +brew --prefix python@3.13 +``` + +### Issue: Multiple Python versions conflict + +**List all Python installations:** +```bash +which -a python3 +ls -l /usr/local/bin/python* +``` + +**Use specific version:** +```bash +python3.13 -m venv venv # Force Python 3.13 +``` + +### Issue: SSL certificate errors + +**Install certificates:** +```bash +/Applications/Python\ 3.13/Install\ Certificates.command +``` + +Or via Homebrew: +```bash +brew install openssl +``` + +### Issue: Xcode Command Line Tools needed + +**Install:** +```bash +xcode-select --install +``` + +--- + +## Verification Checklist + +- [ ] `python3 --version` shows 3.11+ +- [ ] `which python3` shows installation path +- [ ] `python3 -m pip --version` works +- [ ] `python3 -m venv test_venv` creates test environment +- [ ] Can activate test venv: `source test_venv/bin/activate` + +**Clean up test:** +```bash +rm -rf test_venv +``` + +--- + +[← Back to django-project-setup](../SKILL.md) diff --git a/skills/django-setup/platform-install/windows.md b/skills/django-setup/platform-install/windows.md new file mode 100644 index 0000000..012753e --- /dev/null +++ b/skills/django-setup/platform-install/windows.md @@ -0,0 +1,267 @@ +# Python Installation - Windows + +## Check Current Version + +**PowerShell or Command Prompt:** +```powershell +python --version +``` + +If you see Python 3.11+, you're good to go! [← Back to main skill](../SKILL.md#step-2-create-virtual-environment) + +--- + +## Installation Methods + +### Option 1: Official Python Installer (Recommended) + +**Why this method:** +- Official releases from python.org +- Includes pip automatically +- Easy installation + +**Steps:** +1. Visit [python.org/downloads](https://www.python.org/downloads/) +2. Click **"Download Python 3.13.x"** button +3. Run the downloaded `.exe` installer + +**⚠️ CRITICAL: Check "Add Python to PATH"** +- This checkbox is at the bottom of the installer +- **Must check this box** or Python won't work from command line + +4. Click "Install Now" +5. Wait for installation to complete +6. Click "Close" + +**Verify installation:** +```powershell +python --version +pip --version +``` + +--- + +### Option 2: Microsoft Store (Windows 10/11) + +**Why this method:** +- Easiest installation +- Automatic PATH configuration +- Auto-updates through Microsoft Store + +**Steps:** +1. Open **Microsoft Store** app +2. Search for "Python 3.13" +3. Click **"Get"** or **"Install"** +4. Wait for installation +5. Restart terminal + +**Verify:** +```powershell +python --version +``` + +--- + +### Option 3: Chocolatey Package Manager + +**Why this method:** +- Command-line package management (like Homebrew on macOS) +- Good for automated setups +- Easy updates: `choco upgrade python` + +**Install Chocolatey first:** + +Open **PowerShell as Administrator** and run: +```powershell +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) +``` + +**Install Python:** +```powershell +choco install python --version=3.13.0 +``` + +**Verify:** +```powershell +python --version +``` + +--- + +### Option 4: Scoop Package Manager + +**Why this method:** +- Lightweight package manager +- No admin rights required +- Clean installations + +**Install Scoop:** +```powershell +irm get.scoop.sh | iex +``` + +**Install Python:** +```powershell +scoop install python +``` + +**Verify:** +```powershell +python --version +``` + +--- + +## Configure PATH (If Python Not Found) + +### Check if Python is in PATH + +**Open Command Prompt and run:** +```cmd +where python +``` + +**If nothing appears, Python is not in PATH.** + +### Add Python to PATH Manually + +**Find Python installation location:** +- Official installer: `C:\Users\\AppData\Local\Programs\Python\Python313\` +- Microsoft Store: `C:\Users\\AppData\Local\Microsoft\WindowsApps\` + +**Add to PATH:** +1. Open **Start Menu** +2. Search "Environment Variables" +3. Click **"Edit the system environment variables"** +4. Click **"Environment Variables"** button +5. Under "User variables", select **"Path"** +6. Click **"Edit"** +7. Click **"New"** +8. Add Python paths: + - `C:\Users\\AppData\Local\Programs\Python\Python313\` + - `C:\Users\\AppData\Local\Programs\Python\Python313\Scripts\` +9. Click **"OK"** on all dialogs +10. **Restart terminal/PowerShell** + +**Verify:** +```powershell +python --version +``` + +--- + +## Troubleshooting + +### Issue: `python: command not found` + +**Solution 1: Use `py` launcher instead** +```powershell +py --version +py -m venv venv +``` + +**Solution 2: Add to PATH** (see section above) + +**Solution 3: Reinstall Python** +- Uninstall current Python +- Reinstall and **check "Add Python to PATH"** + +### Issue: Multiple Python versions installed + +**List all versions:** +```powershell +py --list +``` + +**Use specific version:** +```powershell +py -3.13 --version +py -3.13 -m venv venv +``` + +### Issue: PowerShell Execution Policy Error + +**When activating venv:** +``` +venv\Scripts\Activate.ps1 : File cannot be loaded because running scripts is disabled +``` + +**Solution:** +```powershell +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` + +**Then retry activation:** +```powershell +venv\Scripts\Activate.ps1 +``` + +### Issue: `pip` not found + +**Use python -m pip instead:** +```powershell +python -m pip --version +python -m pip install --upgrade pip +``` + +### Issue: Long path errors + +**Enable long paths in Windows:** + +Open **PowerShell as Administrator**: +```powershell +New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force +``` + +Restart computer. + +--- + +## Using Windows Subsystem for Linux (WSL) + +**Alternative: Use Linux Python on Windows** + +**Install WSL:** +```powershell +wsl --install +``` + +**Install Ubuntu:** +```powershell +wsl --install -d Ubuntu +``` + +**Then follow:** [linux-ubuntu.md](linux-ubuntu.md) for Python installation + +--- + +## Verification Checklist + +- [ ] `python --version` shows 3.11+ (or `py --version`) +- [ ] `pip --version` works (or `py -m pip --version`) +- [ ] `python -m venv test_venv` creates test environment +- [ ] Can activate test venv: `test_venv\Scripts\activate` +- [ ] Prompt shows `(test_venv)` prefix + +**Clean up test:** +```powershell +rmdir /s test_venv +``` + +--- + +## Development Tools (Optional) + +**Install Windows Terminal** (better than Command Prompt): +- Download from Microsoft Store +- Or: https://aka.ms/terminal + +**Install Git for Windows:** +- Download from: https://git-scm.com/download/win + +**Install VS Code:** +- Download from: https://code.visualstudio.com/ + +--- + +[← Back to django-project-setup](../SKILL.md) diff --git a/skills/django-setup/scripts/create_gitignore.py b/skills/django-setup/scripts/create_gitignore.py new file mode 100755 index 0000000..54854eb --- /dev/null +++ b/skills/django-setup/scripts/create_gitignore.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +""" +Script to create .gitignore file from template. + +Usage: + python create_gitignore.py --output /path/to/project + python create_gitignore.py --output . +""" + +import argparse +import os +import shutil +from pathlib import Path + + +def create_gitignore(output_dir: str) -> None: + """ + Copy .gitignore template to the specified output directory. + + Args: + output_dir: Directory where .gitignore should be created + """ + # Get the directory where this script is located + script_dir = Path(__file__).parent + skill_dir = script_dir.parent + + # Path to the template + template_path = skill_dir / "examples" / ".gitignore-template" + + # Validate template exists + if not template_path.exists(): + raise FileNotFoundError( + f"Template not found at: {template_path}\n" + f"Expected location: .claude/skills/django-setup/examples/.gitignore-template" + ) + + # Create output directory if it doesn't exist + output_path = Path(output_dir).resolve() + output_path.mkdir(parents=True, exist_ok=True) + + # Destination path + gitignore_path = output_path / ".gitignore" + + # Check if .gitignore already exists + if gitignore_path.exists(): + print(f"⚠️ .gitignore already exists at: {gitignore_path}") + print("❌ Script should only be used when .gitignore does not exist.") + print("💡 Use manual merging to add missing entries from the template.") + exit(1) + + # Copy the template + shutil.copy2(template_path, gitignore_path) + + print(f"✅ Created .gitignore at: {gitignore_path}") + print(f"📋 Copied from template: {template_path}") + + +def main(): + parser = argparse.ArgumentParser( + description="Create .gitignore file from template for Django projects", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # Create .gitignore in current directory + python create_gitignore.py --output . + + # Create .gitignore in specific project directory + python create_gitignore.py --output /path/to/project + + # Create .gitignore in parent directory + python create_gitignore.py --output .. + """ + ) + + parser.add_argument( + '--output', + type=str, + required=True, + help='Directory where .gitignore should be created (e.g., . for current directory)' + ) + + args = parser.parse_args() + + try: + create_gitignore(args.output) + except FileNotFoundError as e: + print(f"❌ Error: {e}") + exit(1) + except Exception as e: + print(f"❌ Unexpected error: {e}") + exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/django-setup/troubleshooting/django-errors.md b/skills/django-setup/troubleshooting/django-errors.md new file mode 100644 index 0000000..f9896d9 --- /dev/null +++ b/skills/django-setup/troubleshooting/django-errors.md @@ -0,0 +1,480 @@ +# Troubleshooting: Django Errors + +[← Back to main skill](../SKILL.md) + +--- + +## Issue: `django-admin: command not found` {#django-admin} + +**After installing Django, command doesn't work** + +### Solutions: + +**Ensure venv is activated:** +```bash +source venv/bin/activate # macOS/Linux +venv\Scripts\activate # Windows +``` + +**Use `python -m django` instead:** +```bash +python -m django --version +python -m django startproject myproject +``` + +**Check Django is installed:** +```bash +pip list | grep Django +# or +pip show Django +``` + +**Reinstall Django:** +```bash +pip install --force-reinstall Django +``` + +--- + +## Issue: Port 8000 Already in Use {#port-in-use} + +**Error:** +``` +Error: That port is already in use. +``` + +### Solutions: + +**Use different port:** +```bash +python manage.py runserver 8001 +python manage.py runserver 0.0.0.0:8080 +``` + +**Kill process using port 8000:** + +**macOS/Linux:** +```bash +lsof -ti:8000 | xargs kill -9 +``` + +**Windows (PowerShell):** +```powershell +netstat -ano | findstr :8000 +# Note the PID, then: +taskkill /PID /F +``` + +**Windows (Command Prompt):** +```cmd +FOR /F "tokens=5" %P IN ('netstat -ano ^| findstr :8000') DO TaskKill /PID %P /F +``` + +--- + +## Issue: `ModuleNotFoundError: No module named 'django'` + +**Django installed but Python can't find it** + +### Solutions: + +**Verify venv is activated:** +```bash +which python # Should point to venv/bin/python +echo $VIRTUAL_ENV # Should show venv path +``` + +**Check Django installation:** +```bash +pip list +pip show Django +``` + +**Install Django in venv:** +```bash +source venv/bin/activate +pip install Django +``` + +**Check you're using venv Python:** +```bash +python --version +which python +``` + +--- + +## Issue: `django.core.exceptions.ImproperlyConfigured` + +**Common misconfigurations** + +### `SECRET_KEY` not set + +**Error:** +``` +ImproperlyConfigured: The SECRET_KEY setting must not be empty. +``` + +**Solution:** +- Don't delete SECRET_KEY from settings.py +- If using environment variables, ensure .env file exists + +### Database configuration error + +**Error:** +``` +ImproperlyConfigured: settings.DATABASES is improperly configured +``` + +**Solution - Check database settings:** +```python +# settings.py +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} +``` + +--- + +## Issue: Migrations Not Applying + +**Error:** +``` +django.db.utils.OperationalError: no such table +``` + +### Solutions: + +**Run migrations:** +```bash +python manage.py migrate +``` + +**Check migration files exist:** +```bash +ls */migrations/ +# Should see 0001_initial.py files +``` + +**Reset database (development only!):** +```bash +rm db.sqlite3 +python manage.py migrate +``` + +**Make migrations if they don't exist:** +```bash +python manage.py makemigrations +python manage.py migrate +``` + +--- + +## Issue: Static Files Not Loading + +**CSS/JS files show 404 in browser** + +### Solutions: + +**Run collectstatic:** +```bash +python manage.py collectstatic +``` + +**Check STATIC_URL in settings.py:** +```python +STATIC_URL = '/static/' +``` + +**For development, ensure DEBUG=True:** +```python +DEBUG = True +``` + +**Check INSTALLED_APPS includes:** +```python +INSTALLED_APPS = [ + ... + 'django.contrib.staticfiles', +] +``` + +--- + +## Issue: Template Not Found + +**Error:** +``` +TemplateDoesNotExist at /path/ +``` + +### Solutions: + +**Check template path:** +```python +# settings.py +TEMPLATES = [ + { + 'DIRS': [BASE_DIR / 'templates'], # Add this + ... + }, +] +``` + +**Create templates directory:** +```bash +mkdir -p templates +``` + +**Check template name matches:** +```python +# views.py +return render(request, 'index.html') # Must match file name exactly +``` + +--- + +## Issue: ALLOWED_HOSTS Error in Production + +**Error:** +``` +Invalid HTTP_HOST header: 'yourdomain.com'. You may need to add 'yourdomain.com' to ALLOWED_HOSTS. +``` + +### Solution: + +**Add domain to ALLOWED_HOSTS:** +```python +# settings.py +DEBUG = False # In production +ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com'] +``` + +**For development:** +```python +DEBUG = True +ALLOWED_HOSTS = [] # Empty list allows localhost automatically +``` + +--- + +## Issue: CSRF Verification Failed + +**Error:** +``` +Forbidden (403) +CSRF verification failed. Request aborted. +``` + +### Solutions: + +**Ensure CSRF middleware is enabled:** +```python +# settings.py +MIDDLEWARE = [ + ... + 'django.middleware.csrf.CsrfViewMiddleware', + ... +] +``` + +**Include CSRF token in forms:** +```html +
+ {% csrf_token %} + ... +
+``` + +**For AJAX, include CSRF token in headers:** +```javascript +// Get CSRF token from cookie +const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value; + +fetch('/api/endpoint/', { + method: 'POST', + headers: { + 'X-CSRFToken': csrftoken + } +}) +``` + +--- + +## Issue: Admin Interface Not Working + +**Can't access /admin/** + +### Solutions: + +**Run migrations:** +```bash +python manage.py migrate +``` + +**Create superuser:** +```bash +python manage.py createsuperuser +``` + +**Check URL configuration:** +```python +# urls.py +from django.contrib import admin + +urlpatterns = [ + path('admin/', admin.site.urls), +] +``` + +**Ensure admin app is installed:** +```python +# settings.py +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', +] +``` + +--- + +## Issue: Project Won't Start After Renaming + +**Renamed project but errors occur** + +### Solution: + +**Update references in these files:** + +**manage.py:** +```python +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'new_name.settings') +``` + +**asgi.py:** +```python +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'new_name.settings') +``` + +**wsgi.py:** +```python +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'new_name.settings') +``` + +**Rename project directory itself** + +--- + +## Issue: Circular Import Errors + +**Error:** +``` +ImportError: cannot import name 'X' from partially initialized module 'Y' +``` + +### Solutions: + +**Avoid circular imports:** +```python +# ❌ Bad - circular import +# models.py +from .views import something + +# views.py +from .models import Model +``` + +**Use lazy imports:** +```python +# ✅ Good - import inside function +def my_view(request): + from .models import Model # Import here + ... +``` + +**Or use Django's get_model:** +```python +from django.apps import apps +Model = apps.get_model('app_name', 'ModelName') +``` + +--- + +## Issue: `SyntaxError` in settings.py + +**Error:** +``` +SyntaxError: invalid syntax +``` + +### Solutions: + +**Check Python version:** +```bash +python --version # Should be 3.8+ +``` + +**Look for common syntax errors:** +```python +# ❌ Missing comma +INSTALLED_APPS = [ + 'django.contrib.admin' # Missing comma! + 'django.contrib.auth', +] + +# ✅ Correct +INSTALLED_APPS = [ + 'django.contrib.admin', # Comma here + 'django.contrib.auth', +] +``` + +**Validate Python syntax:** +```bash +python -m py_compile backend/settings.py +``` + +--- + +## Quick Diagnostic Commands + +**Check Django installation:** +```bash +python -m django --version +``` + +**Check Python version:** +```bash +python --version +``` + +**Run system checks:** +```bash +python manage.py check +python manage.py check --deploy +``` + +**List installed packages:** +```bash +pip list +``` + +**Check migrations status:** +```bash +python manage.py showmigrations +``` + +**Test database connection:** +```bash +python manage.py dbshell +``` + +--- + +[← Back to django-project-setup](../SKILL.md) diff --git a/skills/django-setup/troubleshooting/pip-problems.md b/skills/django-setup/troubleshooting/pip-problems.md new file mode 100644 index 0000000..2b2d5eb --- /dev/null +++ b/skills/django-setup/troubleshooting/pip-problems.md @@ -0,0 +1,386 @@ +# Troubleshooting: pip Problems + +[← Back to main skill](../SKILL.md) + +--- + +## Issue: `pip: command not found` {#pip-not-found} + +**After activating venv, pip doesn't work** + +### Solutions: + +**Use `python -m pip` instead:** +```bash +python -m pip --version +python -m pip install Django +python -m pip install --upgrade pip +``` + +**Ensure venv is activated:** +```bash +source venv/bin/activate # macOS/Linux +venv\Scripts\activate # Windows +``` + +**Reinstall pip in venv:** +```bash +python -m ensurepip --upgrade +``` + +--- + +## Issue: `pip install` Requires sudo + +**Error:** +``` +ERROR: Could not install packages due to an OSError: [Errno 13] Permission denied +``` + +### Solution: + +**❌ NEVER use sudo with pip!** + +**✅ Use virtual environment instead:** +```bash +# Create and activate venv first +python3 -m venv venv +source venv/bin/activate + +# Now install without sudo +pip install Django +``` + +**Why no sudo:** +- Pollutes system Python +- Breaks system tools +- Security risk +- Conflicts with package manager + +--- + +## Issue: SSL Certificate Errors + +**Error:** +``` +SSL: CERTIFICATE_VERIFY_FAILED +``` + +### Solutions: + +**macOS - Install certificates:** +```bash +/Applications/Python\ 3.13/Install\ Certificates.command +``` + +**Or install certifi:** +```bash +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org certifi +``` + +**Ubuntu/Debian:** +```bash +sudo apt install ca-certificates +sudo update-ca-certificates +``` + +**Temporary workaround (not recommended):** +```bash +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org Django +``` + +--- + +## Issue: pip is Outdated + +**Warning:** +``` +WARNING: You are using pip version X.X.X; however, version Y.Y.Y is available. +``` + +### Solution: + +**Upgrade pip:** +```bash +python -m pip install --upgrade pip +``` + +**If that fails:** +```bash +curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py +python get-pip.py +rm get-pip.py +``` + +--- + +## Issue: Package Installation Fails + +**Error:** +``` +ERROR: Could not find a version that satisfies the requirement +``` + +### Solutions: + +**Check package name spelling:** +```bash +# Wrong +pip install django-rest-framework + +# Correct +pip install djangorestframework +``` + +**Check Python version compatibility:** +```bash +python --version # Some packages require Python 3.11+ +``` + +**Try older package version:** +```bash +pip install "Django<5.0" # Install Django 4.x +``` + +**Update pip and setuptools:** +```bash +pip install --upgrade pip setuptools wheel +``` + +--- + +## Issue: Conflicting Dependencies + +**Error:** +``` +ERROR: package-a 1.0 has requirement package-b>=2.0, but you'll have package-b 1.5 +``` + +### Solutions: + +**Let pip resolve (pip 20.3+):** +```bash +pip install --upgrade +``` + +**Install specific compatible versions:** +```bash +pip install package-a package-b==2.0 +``` + +**Use pip-tools for dependency management:** +```bash +pip install pip-tools +# Create requirements.in with your deps +pip-compile requirements.in +pip-sync requirements.txt +``` + +**Nuclear option - recreate venv:** +```bash +deactivate +rm -rf venv +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +--- + +## Issue: Building Wheels Failed + +**Error:** +``` +Failed building wheel for +``` + +### Solutions: + +**Install build dependencies:** + +**macOS:** +```bash +xcode-select --install +brew install openssl +``` + +**Ubuntu/Debian:** +```bash +sudo apt install python3-dev build-essential libssl-dev libffi-dev -y +``` + +**Fedora/RHEL:** +```bash +sudo dnf install python3-devel gcc openssl-devel libffi-devel -y +``` + +**Windows:** +- Install Microsoft C++ Build Tools +- Download from: https://visualstudio.microsoft.com/visual-cpp-build-tools/ + +**Use pre-built wheels:** +```bash +pip install --only-binary :all: +``` + +--- + +## Issue: `externally-managed-environment` Error + +**Error (Python 3.11+ on some Linux distros):** +``` +error: externally-managed-environment +This environment is externally managed +``` + +### Solution: + +**✅ Use virtual environment (recommended):** +```bash +python3 -m venv venv +source venv/bin/activate +pip install Django +``` + +**Or use `--break-system-packages` (not recommended):** +```bash +pip install --break-system-packages Django # ⚠️ Use at own risk +``` + +--- + +## Issue: Slow Download Speed + +**Pip takes forever to download packages** + +### Solutions: + +**Use faster mirror (if in China):** +```bash +pip install -i https://pypi.tuna.tsinghua.edu.cn/simple Django +``` + +**Increase timeout:** +```bash +pip install --timeout=1000 Django +``` + +**Use pip cache:** +```bash +pip install Django # First time (slow) +pip install Django # Second time (fast, uses cache) +``` + +--- + +## Issue: Hash Mismatch Error + +**Error:** +``` +THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE +``` + +### Solutions: + +**Regenerate requirements.txt:** +```bash +pip freeze > requirements.txt +``` + +**Install without hash checking (temporary):** +```bash +pip install --no-deps -r requirements.txt +``` + +**Clear pip cache:** +```bash +pip cache purge +``` + +--- + +## Issue: ImportError After Installing Package + +**Package installs but can't import** + +### Solutions: + +**Verify venv is activated:** +```bash +which python # Should point to venv +pip list # Should show installed package +``` + +**Check package name vs import name:** +```bash +# Install name: django-cors-headers +pip install django-cors-headers + +# Import name: corsheaders +import corsheaders # ← Different! +``` + +**Restart Python shell:** +```bash +exit() # Exit Python shell +python # Start fresh +import django # Try again +``` + +--- + +## Issue: Multiple pip Versions + +**`pip` points to wrong Python version** + +### Solution: + +**Always use `python -m pip`:** +```bash +# Instead of: +pip install Django + +# Use: +python -m pip install Django +``` + +**Or specify Python version:** +```bash +python3.13 -m pip install Django +``` + +--- + +## Best Practices + +✅ **Always use virtual environments** +```bash +python3 -m venv venv +source venv/bin/activate +``` + +✅ **Keep pip updated** +```bash +pip install --upgrade pip +``` + +✅ **Pin versions in production** +```bash +# requirements.txt +Django==5.0.1 +djangorestframework==3.14.0 +``` + +✅ **Use requirements.txt** +```bash +pip freeze > requirements.txt +pip install -r requirements.txt +``` + +✅ **Don't mix conda and pip** (if using Anaconda) +- Use conda for conda packages +- Use pip only for packages not in conda + +--- + +[← Back to django-project-setup](../SKILL.md) diff --git a/skills/django-setup/troubleshooting/venv-issues.md b/skills/django-setup/troubleshooting/venv-issues.md new file mode 100644 index 0000000..e483eaf --- /dev/null +++ b/skills/django-setup/troubleshooting/venv-issues.md @@ -0,0 +1,376 @@ +# Troubleshooting: Virtual Environment Issues + +[← Back to main skill](../SKILL.md) + +--- + +## Issue: `ensurepip is not available` {#ensurepip} + +**Full error:** +``` +Error: Command '[...]/python3', '-Im', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit status 1 +``` + +### Solutions: + +**Ubuntu/Debian:** +```bash +sudo apt install python3.13-venv python3.13-distutils -y +``` + +**macOS (Homebrew):** +```bash +brew reinstall python@3.13 +``` + +**Windows:** +- Reinstall Python with "pip" option checked +- Or download get-pip.py: https://bootstrap.pypa.io/get-pip.py + +--- + +## Issue: PowerShell Execution Policy {#powershell-execution-policy} + +**Error:** +``` +venv\Scripts\Activate.ps1 cannot be loaded because running scripts is disabled on this system +``` + +### Solution: + +**Allow scripts for current user:** +```powershell +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` + +**Then retry activation:** +```powershell +venv\Scripts\Activate.ps1 +``` + +**Verify policy:** +```powershell +Get-ExecutionPolicy -List +``` + +--- + +## Issue: Virtual Environment Not Activating + +**Symptoms:** +- No `(venv)` prefix in prompt +- `which python` still points to system Python + +### Solutions: + +**macOS/Linux - Check activation command:** +```bash +source venv/bin/activate # Correct +. venv/bin/activate # Also works +bash venv/bin/activate # Wrong - don't use bash +``` + +**Windows - Use correct script:** +```powershell +# PowerShell +venv\Scripts\Activate.ps1 + +# Command Prompt +venv\Scripts\activate.bat + +# Git Bash +source venv/Scripts/activate +``` + +**Verify activation:** +```bash +which python # Should point to venv/bin/python +echo $VIRTUAL_ENV # Should show venv path +``` + +--- + +## Issue: Permission Denied on Activation {#permission-denied} + +**Error (macOS/Linux):** +``` +bash: venv/bin/activate: Permission denied +``` + +### Solution: + +**Make script executable:** +```bash +chmod +x venv/bin/activate +source venv/bin/activate +``` + +**Or recreate venv:** +```bash +rm -rf venv +python3 -m venv venv +``` + +--- + +## Issue: `venv` Module Not Found + +**Error:** +``` +No module named venv +``` + +### Solutions: + +**Ubuntu/Debian:** +```bash +sudo apt install python3.13-venv -y +``` + +**Fedora/RHEL:** +```bash +sudo dnf install python3.13-venv -y +``` + +**macOS:** +```bash +# Reinstall Python via Homebrew +brew reinstall python@3.13 + +# Or install from python.org +``` + +**Windows:** +- Usually included by default +- Reinstall Python if missing + +--- + +## Issue: Wrong Python Version in venv + +**Symptom:** +```bash +(venv) $ python --version +Python 3.8.10 # But you wanted 3.13! +``` + +### Solution: + +**Delete and recreate with specific version:** +```bash +deactivate # If currently activated +rm -rf venv + +# Specify exact Python version +python3.13 -m venv venv # macOS/Linux +py -3.13 -m venv venv # Windows +``` + +**Verify before activating:** +```bash +venv/bin/python --version # Check version first +source venv/bin/activate # Then activate +``` + +--- + +## Issue: Can't Deactivate venv + +**Symptom:** +- `deactivate` command doesn't work +- Still see `(venv)` in prompt + +### Solutions: + +**Try deactivate function:** +```bash +deactivate +``` + +**If that fails, just close terminal:** +- Exit terminal/shell +- Open new terminal +- venv won't be active in new session + +**Or manually unset variables:** +```bash +unset VIRTUAL_ENV +export PATH="$OLD_PATH" +``` + +--- + +## Issue: venv Created in Wrong Location + +**Symptom:** +- venv created in home directory instead of project +- venv in system location + +### Solution: + +**Delete misplaced venv:** +```bash +rm -rf ~/venv # Or wherever it was created +``` + +**Navigate to project first:** +```bash +cd /path/to/your/project +pwd # Verify you're in correct directory +python3 -m venv venv # Now create here +``` + +--- + +## Issue: `pip` Not Found After Activation + +**Error:** +``` +(venv) $ pip --version +pip: command not found +``` + +### Solutions: + +**Use `python -m pip` instead:** +```bash +python -m pip --version +python -m pip install +``` + +**Reinstall pip in venv:** +```bash +curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py +python get-pip.py +rm get-pip.py +``` + +**Or recreate venv:** +```bash +deactivate +rm -rf venv +python3 -m venv venv --clear +``` + +--- + +## Issue: venv Taking Too Much Space + +**Symptom:** +- venv folder is several GB +- Too many cached packages + +### Solutions: + +**Clear pip cache:** +```bash +pip cache purge +``` + +**Remove unnecessary packages:** +```bash +pip list # See what's installed +pip uninstall +``` + +**Use `--no-cache-dir` when installing:** +```bash +pip install --no-cache-dir Django +``` + +**Typical venv sizes:** +- Minimal Django: ~100-200 MB +- Full-featured project: ~500 MB - 1 GB +- If >2 GB, something is wrong + +--- + +## Issue: Can't Delete venv Folder + +**Windows error:** +``` +Access denied +File in use +``` + +### Solutions: + +**Deactivate first:** +```bash +deactivate +``` + +**Close all terminals/IDEs using that folder** + +**Windows - use PowerShell as Admin:** +```powershell +Remove-Item -Recurse -Force venv +``` + +**Or use File Explorer:** +- Right-click venv folder +- Properties → Security → Advanced +- Take ownership +- Delete + +--- + +## Issue: IDE Not Recognizing venv + +**VS Code, PyCharm not using venv Python** + +### Solutions: + +**VS Code:** +1. Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac) +2. Type "Python: Select Interpreter" +3. Choose `./venv/bin/python` + +**PyCharm:** +1. File → Settings (or Preferences on Mac) +2. Project → Python Interpreter +3. Click gear icon → Add +4. Select "Existing environment" +5. Browse to `venv/bin/python` + +--- + +## Best Practices to Avoid Issues + +✅ **Always activate before installing packages** +```bash +source venv/bin/activate # Activate first +pip install Django # Then install +``` + +✅ **One venv per project** +``` +project1/ + └── venv/ +project2/ + └── venv/ +``` + +✅ **Don't commit venv to git** +```gitignore +venv/ +.venv/ +env/ +ENV/ +``` + +✅ **Document Python version in README** +```markdown +## Requirements +- Python 3.13+ +``` + +✅ **Use requirements.txt** +```bash +pip freeze > requirements.txt +# Others can: pip install -r requirements.txt +``` + +--- + +[← Back to django-project-setup](../SKILL.md) diff --git a/skills/react-allauth/README.md b/skills/react-allauth/README.md new file mode 100644 index 0000000..ff155a5 --- /dev/null +++ b/skills/react-allauth/README.md @@ -0,0 +1,45 @@ +# React Allauth Skill (Frontend) + +Plug google/django-allauth headless authentication flows into a Vite React app: copies the React SPA example modules, wires auth context/routes, fixes provider URLs, adds proxying, and provides Playwright tests and styling references. + +## What This Skill Provides +- Copies allauth React SPA modules into `frontend/src/user_management/`, renames to `.jsx`, and integrates auth routes with your existing router. +- Configures API base URLs, CSRF handling, and social callback URLs to point at the Django backend (`/_allauth/...`). +- Adds Vite proxy rules for `/_allauth` (and expects existing `/api` proxy) to `https://localhost:8000` with self-signed certs. +- Wraps the app in `AuthContextProvider`, adds auth-aware nav, and removes demo routes (e.g., `/calculator`). +- Supports multi-step flows (email verification, passkeys) with pending-flow redirects. +- Bundles Playwright end-to-end tests for signup/login/logout/code-login/password-reset. +- Includes a styling reference listing all 35 auth components needing UI polish. + +## Prerequisites +- React + Vite frontend already set up (see `react-setup` skill). +- Django backend with django-allauth headless enabled and HTTPS at `https://localhost:8000`. +- mkcert-based HTTPS for frontend (`https://localhost:5173`). +- React Router in use; API config exporting `API_BASE_URL`. + +## Setup Summary (see SKILL.md for line-by-line edits) +1) **Clone & copy example modules** from `django-allauth/examples/react-spa/frontend/src` into `frontend/src/user_management/` (files renamed `.jsx`). +2) **Install dependency**: `npm --prefix ./frontend install @github/webauthn-json`. +3) **App wrapper**: wrap your app in `` in `frontend/src/App.jsx`. +4) **API base URL**: in `user_management/lib/allauth.jsx`, import `API_BASE_URL` and prefix `/_allauth/${Client.BROWSER}/v1` with it. +5) **Routes**: create `frontend/src/router/AuthRouter.jsx` exposing `createAuthRoutes(config)`; merge into `createAppRouter` alongside your existing routes. Remove demo `/calculator` route. +6) **Redirects**: set `LOGIN_REDIRECT_URL = '/'`; update password-change/other redirects from `/calculator` to your desired path (default dashboard/home). +7) **Social callback fix**: set `callback_url: callbackURL` in `redirectToProvider` so backend URL is respected. +8) **Vite proxy**: add `/_allauth` proxy to `vite.config.js` targeting backend with `secure:false`. +9) **Auth-aware nav**: surface Login/Logout links conditioned on `useAuthStatus` (navbar or Home fallback). +10) **Flow handling**: add pending-flow redirects in signup/passkey/email verification and `AuthChangeRedirector`. +11) **Styling reference**: copy `references/styling-guide.md` to project root as `react-allauth-styling-reference.md` for later UI work. +12) **Tests**: run Playwright suite in `scripts/test_auth_flows.py` (requires backend + frontend running, pytest<9, playwright+chromium). + +## Outputs/Artifacts +- Auth modules under `frontend/src/user_management/` wired to your router. +- Vite proxy updated for `/_allauth`. +- Auth context wrapping `App`. +- Optional styling reference file in project root. +- Playwright E2E tests ready to validate flows. + +## Notes & Gotchas +- Backend must expose headless endpoints at `/_allauth/...` and serve email links pointing to the frontend (e.g., `https://localhost:5173`). +- Keep AUTH flows on HTTPS; proxy uses `secure:false` to accept mkcert certs. +- Tests expect email backend to write files to `sent_emails/`; adjust if using SMTP. +- If renaming frontend host/port, update `API_BASE_URL`, email links, and test selectors accordingly. diff --git a/skills/react-allauth/SKILL.md b/skills/react-allauth/SKILL.md new file mode 100644 index 0000000..6db02ca --- /dev/null +++ b/skills/react-allauth/SKILL.md @@ -0,0 +1,587 @@ +--- +name: react-allauth +description: Configure React frontend with django-allauth headless API integration, including authentication UI, auth state management, protected routes, and social authentication flows +--- + +## Purpose + +Configure a React frontend application to integrate with django-allauth's headless API, enabling complete authentication workflows including signup, login, email verification, password reset, and social authentication. This skill handles the entire setup process from copying authentication modules to validating flows with automated testing. + +## Prerequisites + +Before starting this configuration, ensure the following requirements are met: + +- **React project with Vite** - A React frontend application initialized with Vite +- **Django backend with django-allauth** - Django backend configured with django-allauth headless API (in settings.py - Look for allauth in INSTALLED_APPS) +- **HTTPS development environment** - Both frontend and backend running over HTTPS (mkcert recommended for local SSL certificates) +- **React Router** - Project uses `react-router-dom` for routing +- **API configuration** - An `API_BASE_URL` constant exported from a config file (typically `src/config/api.js` or `src/config/api.jsx`) +- **Project structure** - Frontend source code located in `frontend/src/` with standard Vite directory structure + +## Steps Overview + +1. Clone Repository and Copy Authentication Modules +2. Install Required Dependencies +3. Update App.jsx to Include Authentication Context +4. Configure API Base URL in allauth.jsx +5. Update Redirect URLs +6. Copy Authentication Router and Integrate Routes +7. Fix Social Authentication Callback URL +8. Configure Vite Proxy for Authentication Endpoints +9. Add Auth-Aware Navigation Link +10. Enable Flow-Based Signup Navigation +11. Validate Authentication Flows with Automated Testing +12. Copy Styling Reference Guide +13. Stop Background Tasks + +--- + +### Step 1: Clone Repository and Copy Authentication Modules + +Clone the django-allauth repository at the project root: + +```bash +git clone https://github.com/pennersr/django-allauth +``` + +Refactor authentication components by renaming `.js` files to `.jsx`: + +```bash +find django-allauth/examples/react-spa/frontend/src/ -name "*.js" -exec bash -c 'mv "$0" "${0%.js}.jsx"' {} \; +``` + +Copy the authentication modules into the React project: + +```bash +mkdir -p frontend/src/user_management +find django-allauth/examples/react-spa/frontend/src/ -mindepth 1 -maxdepth 1 -type d -exec cp -r {} frontend/src/user_management/ \; +``` + +This creates a `user_management` directory in the React project and copies all authentication-related folders from the cloned repository. The `django-allauth/` directory remains available for later steps in this skill. + +--- + +### Step 2: Install Required Dependencies + +Install the WebAuthn dependency required by the authentication modules: + +```bash +npm --prefix ./frontend install @github/webauthn-json +``` + +--- + +### Step 3: Update App.jsx to Include Authentication Context + +**File:** `frontend/src/App.jsx` + +Import the `AuthContextProvider` and wrap the app's content with it: + +```jsx +import { AuthContextProvider } from './user_management/auth' +``` + +Wrap the existing app content (typically the router) with ``: + +```jsx + + {/* Existing app content */} + +``` + +--- + +### Step 4: Configure API Base URL in allauth.jsx + +**File:** `frontend/src/user_management/lib/allauth.jsx` + +After the `getCSRFToken` import, add: +```jsx +import { API_BASE_URL } from '../../config/api' +``` + +Then update the API endpoint path from: +```jsx +`/_allauth/${Client.BROWSER}/v1` +``` + +To: +```jsx +`${API_BASE_URL}/_allauth/${Client.BROWSER}/v1` +``` + +--- + +### Step 5: Update Redirect URLs + +Update redirect paths from `/calculator` to `/dashboard`: + +**File:** `frontend/src/user_management/auth/routing.jsx` + +Change the `LOGIN_REDIRECT_URL` path to: +```jsx +LOGIN_REDIRECT_URL: '/' +``` + +**File:** `frontend/src/user_management/account/ChangePassword.jsx` + +Replace any occurrence of `'/calculator'` with `'/dashboard'` + +--- + +### Step 6: Copy Authentication Router and Integrate Routes + +Copy the authentication router file and clean up the cloned repository: + +```bash +cp django-allauth/examples/react-spa/frontend/src/Router.jsx frontend/src/router/AuthRouter.jsx && rm -rf django-allauth +``` + +**File:** `frontend/src/router/AuthRouter.jsx` + +Update all import paths to use the `user_management` directory: + +Change: +```jsx +import { AuthChangeRedirector, AnonymousRoute, AuthenticatedRoute } from './auth' +``` + +To: +```jsx +import { AuthChangeRedirector, AnonymousRoute, AuthenticatedRoute } from '../user_management/auth' +``` + +Update all component imports (like `Login`, `Signup`, `ChangeEmail`, etc.) from relative paths to use `user_management`: + +Change: +```jsx +import Login from './account/Login' +import Signup from './account/Signup' +// ... etc +``` + +To: +```jsx +import Login from '../user_management/account/Login' +import Signup from '../user_management/account/Signup' +// ... etc +``` + +Update the `Root` import: +```jsx +import Root from '../layouts/Root' +``` + +Update the `useConfig` import: +```jsx +import { useConfig } from '../user_management/auth/hooks' +``` + +**File:** `frontend/src/router/AppRoutes.jsx` + +Import and integrate authentication routes into `createAppRouter`: + +```jsx +import Root from "../layouts/Root"; +import Home from "../pages/Home"; +import { createAuthRoutes } from './AuthRouter'; + +export function createAppRouter(config) { + const authRoutes = createAuthRoutes(config); + + return [ + { + path: "/", + element: , + children: [ + { + path: "/", + element: , + }, + ...authRoutes + ], + }, + ]; +} +``` + +**File:** `frontend/src/router/AuthRouter.jsx` + +Rename the exported function and export the routes array: + +Change: +```jsx +function createRouter (config) { + return createBrowserRouter([ + { + path: '/', + element: , + children: [ + // ... routes + ] + } + ]) +} + +export default function Router () { + const [router, setRouter] = useState(null) + const config = useConfig() + useEffect(() => { + setRouter(createRouter(config)) + }, [config]) + return router ? : null +} +``` + +To: +```jsx +export function createAuthRoutes (config) { + return [ + // ... all the route objects from the children array + ] +} +``` + +Remove the `/calculator` route as it's not needed. + +--- + +### Step 7: Fix Social Authentication Callback URL + +**File:** `frontend/src/user_management/lib/allauth.jsx` + +Find the `redirectToProvider` function and update the `callback_url` parameter. + +Change: +```jsx +callback_url: window.location.protocol + '//' + window.location.host + callbackURL, +``` + +To: +```jsx +callback_url: callbackURL, +``` + +This configuration ensures social authentication callbacks use the correct backend URL instead of the frontend host. + +--- + +### Step 8: Configure Vite Proxy for Authentication Endpoints + +**File:** `frontend/vite.config.js` + +Add the `/_allauth` proxy configuration to forward authentication requests to the Django backend. + +Add to the `proxy` object: +```js +'/_allauth': { + target: 'https://localhost:8000', + changeOrigin: true, + secure: false, // Allow self-signed certificates +}, +``` + +**Expected result:** +```js +proxy: { + '/api': { + target: 'https://localhost:8000', + changeOrigin: true, + secure: false, + }, + '/_allauth': { + target: 'https://localhost:8000', + changeOrigin: true, + secure: false, + }, +} +``` + +--- + +### Step 9: Add Auth-Aware Navigation Link + +First, search the project to determine if a navbar or header component exists. + +#### If a navbar component exists: + +Import the auth status helper and toggle the navigation link based on whether the user is logged in: + +```jsx +import { useAuthStatus } from "@/user_management/auth"; +import { Link } from "react-router-dom"; + +const [, authInfo] = useAuthStatus(); + +{authInfo.isAuthenticated ? ( + + + Logout + + +) : ( + + + Login + + +)} +``` + +#### If NO navbar component exists: + +Add auth-aware navigation links to the landing page. + +**File:** `frontend/src/pages/Home.jsx` + +Import the required dependencies at the top: + +```jsx +import { useAuthStatus } from "@/user_management/auth"; +import { Link } from "react-router-dom"; +``` + +Add the navigation links in the component JSX: + +```jsx +const [, authInfo] = useAuthStatus(); + +return ( +
+ + {/* Rest of Home page content */} +
+); +``` + +**Note:** Linking to `/account/logout` ensures authenticated users reach the confirmation screen before finalizing the logout. Anonymous visitors continue to see the Login link. + +--- + +### Step 10: Enable Flow-Based Signup Navigation + +This step configures multi-step signup flows (like email verification + passkey creation) to navigate correctly between steps without intermediate redirects. + +#### Step 10.1: Configure Signup Screens to Handle Pending Flows + +**Files:** +- `frontend/src/user_management/account/Signup.jsx` +- `frontend/src/user_management/mfa/SignupByPasskey.jsx` + +Add the following imports at the top of each signup component: + +```jsx +import { useEffect } from 'react' +import { useNavigate } from 'react-router-dom' +import { pathForFlow } from '../auth' +``` + +Inside each component, instantiate the navigate function: + +```jsx +const navigate = useNavigate() +``` + +Add a `useEffect` hook that watches for pending flows in the response: + +```jsx +useEffect(() => { + if (response.content) { + const pending = response.content?.data?.flows?.find(flow => flow.is_pending) + if (pending) { + navigate(pathForFlow(pending), { replace: true }) + } + } +}, [response.content, navigate]) +``` + +This configuration ensures users are redirected to the next step in the signup flow (e.g., email verification) without leaving the signup page in the history stack. + +#### Step 10.2: Update Authentication Redirector for Pending Flows + +**File:** `frontend/src/user_management/auth/routing.jsx` + +Update the `AuthChangeRedirector` component to check for pending flows before redirecting to the default login redirect URL. + +In the `LOGGED_IN` branch, add logic to call `pathForPendingFlow(auth)` before defaulting to `LOGIN_REDIRECT_URL`: + +```jsx +if (auth.status === AuthStatus.LOGGED_IN) { + const pendingPath = pathForPendingFlow(auth) + if (pendingPath) { + navigate(pendingPath, { replace: true }) + } else { + navigate(LOGIN_REDIRECT_URL, { replace: true }) + } +} +``` + +This logic prevents users from being redirected to the dashboard when pending authentication steps remain. + +#### Step 10.3: Configure Email Verification to Handle Flows + +**File:** `frontend/src/user_management/account/VerifyEmailByCode.jsx` + +Replace the static `` component return with a `useEffect` hook that handles flow-based redirects. + +Add the required imports: + +```jsx +import { useEffect } from 'react' +import { useNavigate } from 'react-router-dom' +import { pathForFlow } from '../auth' +``` + +Instantiate navigate: + +```jsx +const navigate = useNavigate() +``` + +Replace the `` return with a `useEffect` that redirects based on the response: + +```jsx +useEffect(() => { + const content = response.content + if (!content) { + return + } + + const flows = content.data?.flows + if (flows?.length) { + const pending = flows.find(flow => flow.is_pending) + if (pending) { + navigate(pathForFlow(pending), { replace: true }) + return + } + } + + if (content.status === 200) { + navigate('/account/email', { replace: true }) + } else if (content.status === 401) { + navigate('/account/login', { replace: true }) + } +}, [response.content, navigate]) +``` + +This configuration ensures email verification redirects to the next pending flow step (e.g., passkey creation) or falls back to the appropriate default page. + +--- + +### Step 11: Validate Authentication Flows with Automated Testing + +Run the comprehensive authentication flow test suite to validate the entire integration. This step confirms all authentication flows work correctly end-to-end. + +#### Step 11.1: Install Testing Dependencies + +Activate the Django virtual environment and install Playwright for browser automation testing: + +```bash +source venv/bin/activate + +# Install pytest only if missing +python -c "import pytest" 2>/dev/null || pip install 'pytest>=7.4,<9.0' + +# Install playwright only if missing +python -c "import playwright" 2>/dev/null || pip install playwright + +# Install chromium only if Playwright hasn't installed it yet +playwright show-browsers | grep -q "chromium" \ + || playwright install chromium +``` + +#### Step 11.2: Check and Start Servers, Then Run the Test Suite + +Before running tests, verify both servers are running and start them if needed. + +**Check if backend is running on port 8000:** + +```bash +lsof -i :8000 +``` + +**If backend is not running, start it:** + +Activate the virtual environment, and run the startup script in the background: + +```bash +source venv/bin/activate && \ +uvicorn backend.asgi:application \ + --host 127.0.0.1 \ + --port 8000 \ + --ssl-keyfile ./certs/localhost+2-key.pem \ + --ssl-certfile ./certs/localhost+2.pem +``` + +**Check if frontend is running on port 5173:** + +```bash +lsof -i :5173 +``` + +**If frontend is not running, start it:** + +Start the development server in the background: + +```bash +npm --prefix ./frontend run dev +``` + +**Wait for servers to be ready:** + +Allow a few seconds for both servers to fully initialize before proceeding with tests. + +**Execute the test suite:** + +From the Django project root with the virtual environment activated: +Activate the virtual environment `source venv/bin/activate` and run the test script at: `scripts/test_auth_flows.py` + + +The test suite includes: + +1. **test_01_signup** - User signup flow validation +2. **test_02_email_verification** - Email verification workflow +3. **test_03_password_login** - Password-based authentication +4. **test_04_logout** - Logout functionality +5. **test_05_code_login** - Passwordless code-based login +6. **test_06_password_reset** - Password reset workflow + +**Prerequisites for testing:** +- Django backend running on `https://localhost:8000` +- React frontend running on `https://localhost:5173` +- Email backend configured to save emails to `sent_emails/` directory +- Clean database state (tests create unique users with timestamps) + +**Test execution:** +- Tests run with visible browser (`headless=False`) for debugging +- Each test is independent and creates its own test user +- Tests clean up email files between runs +- Verbose output (`verbosity=2`) shows detailed test progress + +**Note:** Tests run sequentially and may take several minutes to complete due to browser automation and wait times for email delivery. + +--- + +### Step 12: Copy Styling Reference Guide + +Read the styling reference guide from the skill's bundled resources and write it to the project root for later use. + +The styling guide is located at `references/styling-guide.md` within this skill's directory. Read this file and write its contents to `react-allauth-styling-reference.md` in the project root. + +This file lists all 35 authentication components that require styling and can be referenced by styling skills. Delete this file after styling is complete. + +--- + +### Step 13: Stop Background Tasks + +Terminate any background commands or servers started during the configuration process. diff --git a/skills/react-allauth/references/styling-guide.md b/skills/react-allauth/references/styling-guide.md new file mode 100644 index 0000000..5f0d8d9 --- /dev/null +++ b/skills/react-allauth/references/styling-guide.md @@ -0,0 +1,75 @@ +# React-Allauth Components Requiring Styling + +## Overview + +This file lists all authentication components that require styling. These components are functional but use minimal styling from the django-allauth example repository. Apply consistent design using a styling skill (e.g., `apply-shadcn`). + +## Components Requiring Styling + +### Account Files (15 files) + +1. `frontend/src/user_management/account/Login.jsx` +2. `frontend/src/user_management/account/RequestLoginCode.jsx` +3. `frontend/src/user_management/account/ConfirmLoginCode.jsx` +4. `frontend/src/user_management/account/Logout.jsx` +5. `frontend/src/user_management/account/Signup.jsx` +6. `frontend/src/user_management/account/ChangeEmail.jsx` +7. `frontend/src/user_management/account/VerifyEmail.jsx` +8. `frontend/src/user_management/account/VerifyEmailByCode.jsx` +9. `frontend/src/user_management/account/VerificationEmailSent.jsx` +10. `frontend/src/user_management/account/RequestPasswordReset.jsx` +11. `frontend/src/user_management/account/ConfirmPasswordResetCode.jsx` +12. `frontend/src/user_management/account/ChangePassword.jsx` +13. `frontend/src/user_management/account/ResetPassword.jsx` +14. `frontend/src/user_management/account/Reauthenticate.jsx` + +### Social Account Files (3 files) + +1. `frontend/src/user_management/socialaccount/ProviderSignup.jsx` +2. `frontend/src/user_management/socialaccount/ProviderCallback.jsx` +3. `frontend/src/user_management/socialaccount/ManageProviders.jsx` + +### MFA Files (15 files) + +1. `frontend/src/user_management/mfa/MFAOverview.jsx` +2. `frontend/src/user_management/mfa/ActivateTOTP.jsx` +3. `frontend/src/user_management/mfa/DeactivateTOTP.jsx` +4. `frontend/src/user_management/mfa/AuthenticateTOTP.jsx` +5. `frontend/src/user_management/mfa/ReauthenticateTOTP.jsx` +6. `frontend/src/user_management/mfa/RecoveryCodes.jsx` +7. `frontend/src/user_management/mfa/GenerateRecoveryCodes.jsx` +8. `frontend/src/user_management/mfa/AuthenticateRecoveryCodes.jsx` +9. `frontend/src/user_management/mfa/ReauthenticateRecoveryCodes.jsx` +10. `frontend/src/user_management/mfa/AddWebAuthn.jsx` +11. `frontend/src/user_management/mfa/ListWebAuthn.jsx` +12. `frontend/src/user_management/mfa/AuthenticateWebAuthn.jsx` +13. `frontend/src/user_management/mfa/ReauthenticateWebAuthn.jsx` +14. `frontend/src/user_management/mfa/SignupByPasskey.jsx` +15. `frontend/src/user_management/mfa/CreateSignupPasskey.jsx` +16. `frontend/src/user_management/mfa/Trust.jsx` + +### Component Files (2 files) + +1. `frontend/src/user_management/components/Button.jsx` +2. `frontend/src/user_management/mfa/WebAuthnLoginButton.jsx` + +**Total:** 35 files requiring styling + +## Button Component Duplication Issue + +Two Button.jsx files exist in the project: + +1. **`frontend/src/components/ui/button.jsx`** - shadcn/ui Button component with full styling variants +2. **`frontend/src/user_management/components/Button.jsx`** - Wrapper component that uses shadcn/ui Button + +**Recommended approach:** Keep both files as they serve different purposes: + +- **`frontend/src/components/ui/button.jsx`** - Core shadcn/ui component with styling system +- **`frontend/src/user_management/components/Button.jsx`** - Wrapper that ensures authentication components use consistent button styling + +## Notes + +- All components use React Router for navigation +- Components expect shadcn/ui components to be available via `@/components/ui/` imports +- The authentication context (`AuthContextProvider`) must wrap all components +- Components use the allauth API client from `user_management/lib/allauth.jsx` diff --git a/skills/react-allauth/scripts/test_auth_flows.py b/skills/react-allauth/scripts/test_auth_flows.py new file mode 100755 index 0000000..6903bfa --- /dev/null +++ b/skills/react-allauth/scripts/test_auth_flows.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python3 +""" +Professional test suite for Django-Allauth authentication flows. +Tests: Signup, Email Verification, Password Login, Logout, Code Login, Password Reset +""" + +import os +import re +import time +import unittest +from pathlib import Path +from playwright.sync_api import sync_playwright + +# Find project root by looking for Django project markers +# Works regardless of whether skill is installed at project or user level +def find_project_root(): + """Find Django project root by looking for manage.py or venv.""" + cwd = Path.cwd() + + # Search upwards for Django project markers + current = cwd + for _ in range(10): # Prevent infinite loop + # Look for Django project markers + if (current / 'manage.py').exists() and (current / 'venv').exists(): + return current + if current.parent == current: # Reached filesystem root + break + current = current.parent + + # Fallback: use current working directory + return cwd + +PROJECT_ROOT = find_project_root() +EMAIL_DIR = PROJECT_ROOT / 'sent_emails' + + +class AuthenticationFlowTests(unittest.TestCase): + """Test suite for complete authentication flows.""" + + @classmethod + def setUpClass(cls): + """Set up test fixtures before running any tests.""" + cls.password = "SecureTestPassword123!" + cls.new_password = "NewSecurePassword456!" + cls.playwright = None + cls.browser = None + cls.context = None + cls.page = None + + def setUp(self): + """Set up before each test.""" + # Each test gets its own email to avoid conflicts + self.email = f"testuser_{int(time.time() * 1000)}@example.com" + time.sleep(0.1) # Ensure unique timestamps + self.clear_email_folder() + + @staticmethod + def clear_email_folder(): + """Clear all emails from the email folder.""" + if EMAIL_DIR.exists(): + for f in EMAIL_DIR.iterdir(): + f.unlink() + + @staticmethod + def get_latest_email_content(wait_time=3): + """Read the most recently created email file.""" + # Wait for email file to be written + for _ in range(wait_time * 10): # Check every 100ms + if EMAIL_DIR.exists(): + files = list(EMAIL_DIR.iterdir()) + if files: + latest_file = max(files, key=lambda f: f.stat().st_ctime) + return latest_file.read_text() + time.sleep(0.1) + return None + + @staticmethod + def extract_verification_code(email_content): + """Extract the verification code from email.""" + # Match 6-character alphanumeric code + pattern = r'^\s*([A-Z0-9]{6})\s*$' + match = re.search(pattern, email_content, re.MULTILINE) + return match.group(1) if match else None + + @staticmethod + def extract_login_code(email_content): + """Extract the login code from email.""" + pattern = r'^\s*([A-Z0-9]{6})\s*$' + match = re.search(pattern, email_content, re.MULTILINE) + if match: + return match.group(1) + pattern = r'code:\s*([A-Z0-9]{6})' + match = re.search(pattern, email_content, re.IGNORECASE) + return match.group(1) if match else None + + @staticmethod + def extract_password_reset_url(email_content): + """Extract the password reset URL from email.""" + pattern = r'https://localhost:5173/account/password/reset/key/([^\s\n]+)' + match = re.search(pattern, email_content) + return match.group(0) if match else None + + def start_browser(self): + """Start browser session.""" + if not self.playwright: + self.playwright = sync_playwright().start() + self.browser = self.playwright.chromium.launch(headless=False) + self.context = self.browser.new_context(ignore_https_errors=True) + self.page = self.context.new_page() + + def stop_browser(self): + """Stop browser session.""" + if self.page: + self.page.close() + if self.context: + self.context.close() + if self.browser: + self.browser.close() + if self.playwright: + self.playwright.stop() + self.page = None + self.context = None + self.browser = None + self.playwright = None + + def test_01_signup(self): + """Test user signup flow.""" + self.start_browser() + try: + self.page.goto('https://localhost:5173/account/signup', wait_until='networkidle') + self.page.fill('[data-testid="signup-email"]', self.email) + self.page.fill('[data-testid="signup-password1"]', self.password) + self.page.fill('[data-testid="signup-password2"]', self.password) + self.page.click('[data-testid="signup-submit"]') + self.page.wait_for_url('**/account/verify-email', timeout=5000) + + self.assertIn('verify-email', self.page.url, "Should redirect to verify-email page") + finally: + self.stop_browser() + + def test_02_email_verification(self): + """Test email verification flow.""" + self.start_browser() + try: + # First signup + self.page.goto('https://localhost:5173/account/signup', wait_until='networkidle') + self.page.fill('[data-testid="signup-email"]', self.email) + self.page.fill('[data-testid="signup-password1"]', self.password) + self.page.fill('[data-testid="signup-password2"]', self.password) + self.page.click('[data-testid="signup-submit"]') + self.page.wait_for_url('**/account/verify-email', timeout=5000) + + # Get verification email + email_content = self.get_latest_email_content() + self.assertIsNotNone(email_content, "Verification email should exist") + + verification_code = self.extract_verification_code(email_content) + self.assertIsNotNone(verification_code, "Should extract verification code") + self.assertEqual(len(verification_code), 6, "Verification code should be 6 characters") + + # Verify email with code + self.page.fill('[data-testid="verify-email-code-input"]', verification_code) + self.page.click('[data-testid="verify-email-code-submit"]') + self.page.wait_for_url('**/account/email', timeout=5000) + + self.assertIn('email', self.page.url, "Should redirect to email management after verification") + finally: + self.stop_browser() + + def test_03_password_login(self): + """Test password-based login flow.""" + self.start_browser() + try: + # Setup: signup and verify (user will be logged in after verification) + self._signup_and_verify() + + # Logout first + self.page.goto('https://localhost:5173/account/logout', wait_until='networkidle') + time.sleep(1) + self.page.click('[data-testid="logout-submit"]') + time.sleep(2) + + # Now login with password + self.page.goto('https://localhost:5173/account/login', wait_until='networkidle') + self.page.fill('[data-testid="login-email"]', self.email) + self.page.fill('[data-testid="login-password"]', self.password) + self.page.click('[data-testid="login-submit"]') + self.page.wait_for_url('https://localhost:5173/', timeout=5000) + + # Verify logged in + logout_link = self.page.locator('a:has-text("Logout")') + self.assertGreater(logout_link.count(), 0, "Should see Logout link when logged in") + finally: + self.stop_browser() + + def test_04_logout(self): + """Test logout flow.""" + self.start_browser() + try: + # Setup: login + self._login_user() + + # Logout + self.page.goto('https://localhost:5173/account/logout', wait_until='networkidle') + time.sleep(1) + self.page.click('[data-testid="logout-submit"]') + self.page.wait_for_load_state('networkidle') + time.sleep(2) + + # Verify logged out + self.page.goto('https://localhost:5173/', wait_until='networkidle') + time.sleep(1) + login_link = self.page.locator('a:has-text("Login")') + self.assertGreater(login_link.count(), 0, "Should see Login link when logged out") + finally: + self.stop_browser() + + def test_05_code_login(self): + """Test passwordless login with code flow.""" + self.start_browser() + try: + # Setup: signup and verify (user will be logged in after verification) + self._signup_and_verify() + + # Logout + self.page.goto('https://localhost:5173/account/logout', wait_until='networkidle') + time.sleep(1) + self.page.click('[data-testid="logout-submit"]') + time.sleep(2) + + self.clear_email_folder() + + # Request login code + self.page.goto('https://localhost:5173/account/login', wait_until='networkidle') + time.sleep(2) + self.page.click('a:has-text("Send me a sign-in code")') + self.page.wait_for_load_state('networkidle') + time.sleep(1) + + self.page.fill('[data-testid="request-code-email"]', self.email) + self.page.click('[data-testid="request-code-submit"]') + self.page.wait_for_load_state('networkidle') + time.sleep(2) + + # Get code from email + code_email = self.get_latest_email_content() + self.assertIsNotNone(code_email, "Login code email should exist") + + login_code = self.extract_login_code(code_email) + self.assertIsNotNone(login_code, "Should extract login code") + self.assertEqual(len(login_code), 6, "Login code should be 6 characters") + + # Enter code and login + self.page.fill('[data-testid="confirm-code-input"]', login_code) + self.page.click('[data-testid="confirm-code-submit"]') + self.page.wait_for_url('https://localhost:5173/', timeout=5000) + + # Verify logged in + logout_link = self.page.locator('a:has-text("Logout")') + self.assertGreater(logout_link.count(), 0, "Should be logged in after code login") + finally: + self.stop_browser() + + def test_06_password_reset(self): + """Test password reset flow.""" + self.start_browser() + try: + # Setup: signup and verify + self._signup_and_verify() + + # Logout (should be at login page) + self.page.goto('https://localhost:5173/account/logout', wait_until='networkidle') + time.sleep(1) + if self.page.locator('[data-testid="logout-submit"]').count() > 0: + self.page.click('[data-testid="logout-submit"]') + time.sleep(2) + + self.clear_email_folder() + + # Request password reset + self.page.goto('https://localhost:5173/account/login', wait_until='networkidle') + time.sleep(1) + self.page.click('a:has-text("Forgot your password?")') + self.page.wait_for_load_state('networkidle') + time.sleep(1) + + self.page.fill('[data-testid="password-reset-email"]', self.email) + self.page.click('[data-testid="password-reset-submit"]') + self.page.wait_for_load_state('networkidle') + time.sleep(2) + + # Get reset email + reset_email = self.get_latest_email_content() + self.assertIsNotNone(reset_email, "Password reset email should exist") + + reset_url = self.extract_password_reset_url(reset_email) + self.assertIsNotNone(reset_url, "Should extract password reset URL") + + # Reset password + self.page.goto(reset_url) + self.page.wait_for_load_state('networkidle') + time.sleep(1) + + self.page.fill('[data-testid="reset-password1"]', self.new_password) + self.page.fill('[data-testid="reset-password2"]', self.new_password) + self.page.click('[data-testid="reset-password-confirm"]') + self.page.wait_for_load_state('networkidle') + time.sleep(2) + + # Password reset complete - now login with new password to verify it worked + # Navigate to login page + self.page.goto('https://localhost:5173/account/login', wait_until='networkidle') + time.sleep(1) + + # Login with new password + self.page.fill('[data-testid="login-email"]', self.email) + self.page.fill('[data-testid="login-password"]', self.new_password) + self.page.click('[data-testid="login-submit"]') + self.page.wait_for_url('https://localhost:5173/', timeout=5000) + + # Verify logged in with new password + logout_link = self.page.locator('a:has-text("Logout")') + self.assertGreater(logout_link.count(), 0, "Should be logged in with new password") + finally: + self.stop_browser() + + # Helper methods + def _signup_and_verify(self): + """Helper: signup and verify email.""" + self.clear_email_folder() + self.page.goto('https://localhost:5173/account/signup', wait_until='networkidle') + self.page.fill('[data-testid="signup-email"]', self.email) + self.page.fill('[data-testid="signup-password1"]', self.password) + self.page.fill('[data-testid="signup-password2"]', self.password) + self.page.click('[data-testid="signup-submit"]') + self.page.wait_for_url('**/account/verify-email', timeout=5000) + + email_content = self.get_latest_email_content() + verification_code = self.extract_verification_code(email_content) + self.page.fill('[data-testid="verify-email-code-input"]', verification_code) + self.page.click('[data-testid="verify-email-code-submit"]') + self.page.wait_for_url('**/account/email', timeout=5000) + # User is now logged in and on the email management page + + def _login_user(self): + """Helper: complete signup, verify, and login.""" + self._signup_and_verify() + # User is already logged in after email verification + + +if __name__ == '__main__': + # Run tests with verbose output + unittest.main(verbosity=2) diff --git a/skills/react-setup/README.md b/skills/react-setup/README.md new file mode 100644 index 0000000..912982c --- /dev/null +++ b/skills/react-setup/README.md @@ -0,0 +1,37 @@ +# React Setup Skill (Vite + Django Backend) + +Spin up a fresh React app (Vite) that talks to a Django backend over HTTPS with correct CORS/CSRF handling, Axios defaults, and a basic router/layout scaffold. + +## What This Skill Does +- Initializes a new Vite React project in `frontend/`. +- Configures `vite.config.js` for HTTPS dev using mkcert certs from `certs/`, port 5173, and proxying `/api` to `https://localhost:8000`. +- Adds CSRF service and Axios interceptors that inject `X-CSRFToken` for mutating requests. +- Sets up React Router layout (`Root`), navbar/footer, home page, and backend health check component that verifies CSRF + `/api/health/`. +- Provides a minimal API config file to centralize endpoints. +- Validates the build with `npm --prefix ./frontend run build`. + +## Prerequisites +- Django backend running over HTTPS (see `django-setup` skill) with endpoints `/api/csrf/` and `/api/health/`. +- mkcert certificates stored in project `certs/` (key/cert filenames discovered in skill steps). +- Node.js 18+ and npm available. + +## Setup Summary (see SKILL.md for exact commands) +1) **Create project**: `npm create vite@latest frontend -- --template react --yes`; then `npm --prefix ./frontend install react-router-dom axios`. +2) **Configure `vite.config.js`**: import `fs`/`path`; load mkcert key/cert from `certs/`; set `server.https`, `server.port=5173`, and proxy `/api` → `https://localhost:8000` with `secure:false`. +3) **API config** `src/config/api.js`: export `API_BASE_URL` (empty to use proxy) and `ENDPOINTS` with `CSRF: '/api/csrf/'`. +4) **CSRF service** `src/services/csrf.js`: fetch `/api/csrf/` once, read `csrftoken` cookie, cache/token helpers. +5) **Axios instance** `src/services/api.js`: `withCredentials:true`, JSON headers; request interceptor adds `X-CSRFToken` via `getCsrfToken()` for POST/PUT/PATCH/DELETE. +6) **UI scaffold**: navbar/footer components; `Root` layout with `Outlet`; `Home` page using `BackendStatus` component that pings CSRF then `/api/health/`. +7) **Routing**: `src/router/AppRoutes.jsx` exports `createAppRouter()` config; `src/router/index.jsx` wraps `RouterProvider`; `App.jsx` renders ``. +8) **Verify**: `npm --prefix ./frontend run build` to ensure config/imports succeed. + +## Outputs/Artifacts +- Vite React app under `frontend/` with HTTPS dev server ready to hit Django. +- `vite.config.js` with mkcert HTTPS + API proxy. +- Axios + CSRF utilities wired for Django session auth. +- Minimal router/layout/pages demonstrating backend connectivity. + +## Notes & Gotchas +- Make sure `certs/` exists and filenames match what mkcert generated; update `vite.config.js` accordingly. +- Backend must expose `/api/csrf/` to set the `csrftoken` cookie; otherwise Axios interceptor will fail. +- If you change backend host/port, update the proxy target in `vite.config.js` and any hardcoded `FRONTEND_URL` on the Django side. diff --git a/skills/react-setup/SKILL.md b/skills/react-setup/SKILL.md new file mode 100644 index 0000000..5519432 --- /dev/null +++ b/skills/react-setup/SKILL.md @@ -0,0 +1,268 @@ +--- +name: react-setup +description: Use when initializing a new React frontend with Vite to connect to a Django backend over HTTPS. Sets up routing, CSRF protection, Axios config, and validates the build. Not for existing React projects. +allowed-tools: Bash, Write, Edit, Read, Glob, Grep, TodoWrite, Task +--- + +## Overview +Sets up a production-ready React + Vite frontend configured for Django backend integration with: +- HTTPS local development (using mkcert certificates) +- CSRF token handling for Django session auth +- Axios interceptors for automatic CSRF injection +- React Router with layout structure +- Production build validation + +**When to use:** New React projects that need Django backend connectivity +**When NOT to use:** Existing React projects, non-Django backends, or projects not using session auth + +## Prerequisites +- Django backend configured with HTTPS +- mkcert certificates in `/certs/` directory (use `mkcert-https-setup` skill) +- Node.js 18+ installed + +--- + +Copy this checklist and track your progress: + +- [ ] 1. Initialize React + Vite Project +- [ ] 2. Configure vite.config.js: +- [ ] 3. Create API Configuration +- [ ] 4. Create CSRF Service +- [ ] 5. Create Axios Configuration +- [ ] 6. Create Navbar and Footer Components +- [ ] 7. Create `src/layouts/Root.jsx`: +- [ ] 8. Create Page Components +- [ ] 9. Create `src/router/AppRoutes.jsx` +- [ ] 10. Create Router Wrapper +- [ ] 11. Update App.jsx +- [ ] 12. Verify Setup + +--- + +1. Initialize React + Vite Project + +- `npm create vite@latest frontend -- --template react --yes` +- `npm --prefix ./frontend install react-router-dom axios` + +--- + +2. Configure vite.config.js: + +1. List cert files in `certs/` to find actual filenames +2. Read existing `frontend/vite.config.js` (created by Vite) +3. Add imports: `fs`, `path` +4. Add server config: + - server.https: Load key/cert from `certs/` using found filenames + - server.port: 5173 + - server.proxy: `/api` → `https://localhost:8000` (changeOrigin: true, secure: false) + +--- + +3. Create API Configuration + +src/config/api.js: +```javascript +export const API_BASE_URL = ''; // Empty string uses Vite proxy + +export const ENDPOINTS = { + CSRF: '/api/csrf/', + // Add your API endpoints here as you build features + // Example: PRODUCTS: '/api/products/', +}; +``` +--- + +4. Create CSRF Service + +src/services/csrf.js: +```javascript +import { ENDPOINTS } from '../config/api'; + +let csrfToken = null; + +/** + * Fetches and caches the CSRF token from Django backend + * The token is stored in a cookie by Django after the first request + */ +export async function getCsrfToken() { + // Return cached token if available + if (csrfToken) { + return csrfToken; + } + + try { + const response = await fetch(ENDPOINTS.CSRF, { + credentials: 'include', + headers: { + 'Accept': 'application/json', + } + }); + + if (!response.ok) { + throw new Error(`Failed to fetch CSRF token: ${response.status}`); + } + + // Django sets the token in a cookie after this request + const cookies = document.cookie.split(';'); + const csrfCookie = cookies.find(cookie => cookie.trim().startsWith('csrftoken=')); + + if (!csrfCookie) { + throw new Error('CSRF token not found in cookies'); + } + + csrfToken = csrfCookie.split('=')[1].trim(); + return csrfToken; + } catch (error) { + console.error('CSRF token fetch error:', error); + throw error; + } +} + +/** + * Clears the cached CSRF token (useful for logout or token refresh) + */ +export function clearCsrfToken() { + csrfToken = null; +} +``` +--- + +5. Create Axios Configuration + +src/services/api.js: +```javascript +import axios from 'axios'; +import { API_BASE_URL } from '../config/api'; +import { getCsrfToken } from './csrf'; // ADD THIS + +const axiosInstance = axios.create({ + baseURL: API_BASE_URL, + withCredentials: true, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } +}); + +// REQUEST INTERCEPTOR +axiosInstance.interceptors.request.use( + async (config) => { + if (['post', 'put', 'patch', 'delete'].includes(config.method.toLowerCase())) { + const csrfToken = await getCsrfToken(); + config.headers['X-CSRFToken'] = csrfToken; + } + return config; + } +); + +// Response interceptor +axiosInstance.interceptors.response.use( + response => response, + error => { + console.error('API request failed:', error); + return Promise.reject(error); + } +); + +export default axiosInstance; +``` +--- + +6. Create Navbar and Footer Components + +Create `src/components/Navbar.jsx`: +```javascript +export default function Navbar() { + return ( + + ); +} +``` + +Create `src/components/Footer.jsx`: +```javascript +export default function Footer() { + return ( +
+

© {new Date().getFullYear()} My App. All rights reserved.

+
+ ); +} +``` + +--- + +7. Create `src/layouts/Root.jsx`: + +```javascript +import { Outlet } from 'react-router-dom'; +import Navbar from '../components/Navbar'; +import Footer from '../components/Footer'; + +export default function Root() { + return ( +
+ +
+ +
+
+
+ ); +} +``` + +--- + +8. Create Page Components + +Create `src/components/BackendStatus.jsx`: +1. Import axios from `../services/api`, useState, useEffect +2. Initialize state: `status = "checking..."`, `error = null` +3. useEffect hook (runs on mount): + - First: `axios.get('/api/csrf/')` to initialize cookies + - Second: `axios.get('/api/health/')` using same axios instance + - Success path: update status with backend message + - Error path: set error state, display "Backend unreachable" +4. Component is presentational only (no routing/layout logic) + +Create `src/pages/Home.jsx`: +1. Render `` +2. Add heading + text explaining the backend connectivity check + +--- + +9. Create `src/router/AppRoutes.jsx`: + +1. Export `createAppRouter()` function +2. Returns route config array: Root layout at "/" with Home page as nested "/" child route +3. Imports: `Root` from `../layouts/Root`, `Home` from `../pages/Home` + +--- + +10. Create `src/router/index.jsx`: + +1. Export default `AppRouter` component +2. Create browser router from `createAppRouter()` config +3. Return `` +4. Imports: `createBrowserRouter`, `RouterProvider` from react-router-dom, `createAppRouter` from `./AppRoutes` + +--- + +11. Update `src/App.jsx`: +- Remove: All useState, demo imports (reactLogo, viteLogo, App.css), counter UI +- Add imports: `Router` from `./router` +- Render: `` + +--- + +12. Verify Setup + +Run `npm --prefix ./frontend run build` to validate imports, dependencies, and syntax + +**Common issues:** +- Missing certs: Check `/certs/` directory exists with .pem files +- Module not found: Verify all imports use correct paths +- CSRF errors: Ensure Django backend is running at `https://localhost:8000` diff --git a/skills/tailwind-setup/README.md b/skills/tailwind-setup/README.md new file mode 100644 index 0000000..8ee7429 --- /dev/null +++ b/skills/tailwind-setup/README.md @@ -0,0 +1,41 @@ +# Tailwind Setup Skill (Vite + shadcn/ui) + +Add Tailwind CSS, shadcn/ui, dark-mode theme tokens, and aliases to a React/Vite frontend that talks to a Django backend. + +## What This Skill Does +- Installs Tailwind (+ Vite plugin) and core shadcn/ui deps (`class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`). +- Updates `vite.config.js` to run `tailwindcss()` alongside React and adds `@` alias → `./src`. +- Replaces `src/index.css` with Tailwind entry, then appends theme tokens via `scripts/apply-theme.sh` (uses `assets/tailwind-theme.css`) for dark/light palettes. +- Sets up shadcn/ui scaffolding: `components.json`, `jsconfig.json` aliases, and utility `src/lib/utils.js`. +- Enables class-based dark mode in `tailwind.config.js`. +- Adds `ThemeContext` & `ThemeToggle` components and wraps `App` with `ThemeProvider`. +- Installs shadcn/ui components (card, field, input, button, table, navigation-menu, label, separator, alert) via CLI. +- Verifies build with `npm --prefix ./frontend run build`. + +## Prerequisites +- React + Vite frontend in `frontend/`. +- Django backend already set up (used only for proxy alignment). +- Node.js 18+ and npm; mkcert HTTPS recommended. + +## Setup Summary (see SKILL.md for exact edits) +1) **Deps**: `npm --prefix ./frontend install tailwindcss @tailwindcss/vite class-variance-authority clsx tailwind-merge lucide-react`. +2) **Vite config**: import `path`, `tailwindcss`; add plugin; add `resolve.alias` `{ '@': path.resolve(__dirname, './src') }`. +3) **CSS entry**: set `src/index.css` to `@import "tailwindcss";`. +4) **Theme tokens**: run `bash .claude/skills/tailwind-setup/scripts/apply-theme.sh frontend` (appends tokens to `src/index.css`). +5) **shadcn scaffolding**: create `components.json`, `jsconfig.json`, `src/lib/utils.js`. +6) **Tailwind config**: `darkMode: 'class'`, content globs for `./index.html` and `./src/**/*.{js,jsx}`. +7) **ThemeProvider + toggle**: add `src/contexts/ThemeContext.jsx`, `src/components/ThemeToggle.jsx`; wrap `` in `App.jsx`; drop toggle into navbar. +8) **shadcn components**: run `npx --prefix ./frontend shadcn@latest add ...` for card/field/input/button/table/navigation-menu/label/separator/alert. +9) **Build check**: `npm --prefix ./frontend run build`. + +## Outputs/Artifacts +- Tailwind + shadcn tokens in `src/index.css`. +- `tailwind.config.js`, `components.json`, `jsconfig.json`, `src/lib/utils.js`. +- Theme context/toggle components added and App wrapped. +- Installed shadcn/ui primitives ready for use. + +## Notes & Gotchas +- Run theme script from project root and pass `frontend` path arg. +- Ensure Vite aliases in `vite.config.js` and `jsconfig.json` match. +- If you already have CSS content, back it up before replacing with Tailwind import (tokens are appended by the script). +- Add additional content globs to `tailwind.config.js` if you place components elsewhere. diff --git a/skills/tailwind-setup/SKILL.md b/skills/tailwind-setup/SKILL.md new file mode 100644 index 0000000..013338f --- /dev/null +++ b/skills/tailwind-setup/SKILL.md @@ -0,0 +1,352 @@ +--- +name: tailwind-setup +description: Configure Tailwind CSS and shadcn/ui for React frontends with Django backends, including dark mode support and theme tokens. This skill should be used when setting up a new React project or adding Tailwind to an existing one. +allowed-tools: Bash, Write, Edit, Read, Glob, Grep, TodoWrite, mcp__shadcn__get_project_registries, mcp__shadcn__search_items_in_registries, mcp__shadcn__view_items_in_registries, mcp__shadcn__get_item_examples_from_registries, mcp__shadcn__get_add_command_for_items, mcp__shadcn__get_audit_checklist +--- + +## Purpose + +Configure Tailwind CSS with shadcn/ui for React frontends connecting to Django backends. This setup includes dark mode support, theme tokens, and shadcn/ui component integration. The configuration enables automatic dark mode switching and provides a foundation for building consistent, themeable user interfaces. + +## Setup Process Overview + +Follow these steps in order: +1. Install Dependencies +2. Configure Vite for Django Backend +3. Setup Tailwind in CSS +4. Apply Base Theme (Required for dark mode support) +5. Setup shadcn/ui +6. Implement Dark Mode with Toggle +7. Verify Setup and Check for Errors + +--- + +## Step 1: Install Dependencies + +Install Tailwind CSS and the Vite plugin: + +```bash +npm --prefix ./frontend tailwindcss @tailwindcss/vite +``` + +## Step 2: Configure Vite for Django Backend + +Configure `vite.config.js` to enable Tailwind and path aliases: + +1. Read existing `vite.config.js` +2. Add the required imports at the top (keep the existing React import): + ```javascript + import path from 'node:path' + import tailwindcss from '@tailwindcss/vite' + ``` +3. Update the plugins array so Tailwind runs alongside React: + ```javascript + plugins: [ + react(), + tailwindcss(), + ] + ``` +4. Define the alias block in the same file so `@/` resolves to `src` (this relies on the `path` import above): + ```javascript + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + } + ``` + +## Step 3: Setup Tailwind in CSS + +Configure the main CSS file to import Tailwind: + +1. Read existing `src/index.css` +2. Replace the entire contents with the Tailwind import: + ```css + @import "tailwindcss"; + ``` + +## Step 4: Apply Base Theme + +Apply the base theme by running the bundled script from the project root directory at: `scripts/apply-theme.sh` + +Apply the base theme by running the script from the skill's bundled resources at:`scripts/apply-theme.sh` within this skill's directory. + +The script requires requires the frontend directory path (relative to project + root) as an argument. + +3. **Run the theme script from project root**: + ```bash + bash /scripts/apply-theme.sh frontend + ``` + +The script appends shadcn/ui's design tokens (from `assets/tailwind-theme.css`, in the skill's bundled resources) to `src/index.css`, adding support for typography, colors, and automatic dark mode. + + +## Step 5: Setup shadcn/ui + +### 5a. Install shadcn/ui Dependencies + +Install the required packages for shadcn/ui component functionality: + +```bash +npm --prefix ./frontend install class-variance-authority clsx tailwind-merge lucide-react +``` + +### 5b. Create jsconfig.json for Path Aliases + +Create `jsconfig.json` in the frontend root directory: + +```json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} +``` + +### 5c. Create Utility Helper Function + +Create `src/lib/utils.js`: + +```javascript +import { clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs) { + return twMerge(clsx(inputs)) +} +``` + +### 5d. Create components.json Configuration + +Create `components.json` in the frontend root directory: + +```json +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": false, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} +``` + +### 5e. Install shadcn/ui Components + +**PREREQUISITE:** Before installing components, verify their availability and review usage patterns using the shadcn MCP server. This ensures compatibility with the current registry and helps understand proper implementation patterns. + +**MCP Verification:** + +Use the shadcn MCP tools to search for each component and confirm it exists in the registry: + +- `mcp__shadcn__search_items_in_registries` with `registries=["@shadcn"]` and `query="card"` +- `mcp__shadcn__search_items_in_registries` with `registries=["@shadcn"]` and `query="button"` +- `mcp__shadcn__search_items_in_registries` with `registries=["@shadcn"]` and `query="navigation-menu"` +- `mcp__shadcn__search_items_in_registries` with `registries=["@shadcn"]` and `query="alert"` + +**Install Required Components:** + +Once verified, install the components using the shadcn CLI: + +```bash +npx --prefix ./frontend shadcn@latest add card +npx --prefix ./frontend shadcn@latest add field +npx --prefix ./frontend shadcn@latest add input +npx --prefix ./frontend shadcn@latest add button +npx --prefix ./frontend shadcn@latest add table +npx --prefix ./frontend shadcn@latest add navigation-menu +npx --prefix ./frontend shadcn@latest add label +npx --prefix ./frontend shadcn@latest add separator +npx --prefix ./frontend shadcn@latest add alert +``` + +## Step 6: Implement Dark Mode with Toggle + +### 6a. Configure Tailwind for Class-based Dark Mode + +Create or update `tailwind.config.js` in the frontend root directory, to enable class-based dark mode: + +```js +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: 'class', + content: [ + "./index.html", + "./src/**/*.{js,jsx}", + ], +} +``` + +### 6b. Create Theme Context + +Create `src/contexts/ThemeContext.jsx`: + +```js +import { createContext, useContext, useEffect, useState } from "react"; + +const ThemeContext = createContext({ + theme: "system", + setTheme: () => null, +}); + +export function ThemeProvider({ children, defaultTheme = "system", storageKey = "theme" }) { + const [theme, setTheme] = useState(() => { + return localStorage.getItem(storageKey) || defaultTheme; + }); + + useEffect(() => { + const root = document.documentElement; + + root.classList.remove("dark"); + + if (theme === "system") { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches; + if (systemTheme) { + root.classList.add("dark"); + } + return; + } + + if (theme === "dark") { + root.classList.add("dark"); + } + }, [theme]); + + const value = { + theme, + setTheme: (newTheme) => { + localStorage.setItem(storageKey, newTheme); + setTheme(newTheme); + }, + }; + + return {children}; +} + +export function useTheme() { + const context = useContext(ThemeContext); + + if (context === undefined) { + throw new Error("useTheme must be used within a ThemeProvider"); + } + + return context; +} +``` + +### 6c. Create Theme Toggle Button Component + +Create `src/components/ThemeToggle.jsx`: + +```js +import { Moon, Sun } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { useTheme } from "@/contexts/ThemeContext"; + +export default function ThemeToggle() { + const { theme, setTheme } = useTheme(); + + const toggleTheme = () => { + // If currently system or light, switch to dark + // If currently dark, switch to light + if (theme === "dark") { + setTheme("light"); + } else { + setTheme("dark"); + } + }; + + return ( + + ); +} +``` + +### 6d. Wrap App with ThemeProvider + +Update `src/App.jsx` to include the ThemeProvider: + +```js +import Router from "./router"; +import { ThemeProvider } from "@/contexts/ThemeContext"; + +function App() { + return ( + + + + ); +} + +export default App; +``` + +### 6e. Add Theme Toggle to Navbar + +Integrate the ThemeToggle component into the navigation bar: + +1. **Locate the navbar component**: Find the main navigation component (commonly in `src/components/Navbar.jsx` or similar) +2. **Import the ThemeToggle component**: + ```js + import ThemeToggle from "@/components/ThemeToggle"; + ``` +3. **Add the toggle to the navbar**: Place the `` component in an appropriate location within the navbar, typically in the right section alongside other navigation items: + ```jsx + + ``` + + +## Step 7: Run Build to Check for Compilation Errors + +Build the project to verify there are no compilation errors: + +```bash +npm --prefix ./frontend run build +``` + +If the build succeeds, the setup is complete. If there are errors: +- Check for missing imports or incorrect file paths +- Verify all components were installed correctly +- Ensure `jsconfig.json` path aliases match `vite.config.js` aliases +- Confirm `tailwind.config.js` content paths include all component files + +## Setup Complete + +The Tailwind CSS and shadcn/ui setup is now complete. The project includes: +- Tailwind CSS with Vite integration +- shadcn/ui design tokens and theme variables +- Dark mode support with class-based switching +- Path aliases configured for clean imports (`@/components`, `@/lib`, etc.) +- Core shadcn/ui components installed and ready to use + diff --git a/skills/tailwind-setup/assets/tailwind-theme.css b/skills/tailwind-setup/assets/tailwind-theme.css new file mode 100644 index 0000000..d26ff01 --- /dev/null +++ b/skills/tailwind-setup/assets/tailwind-theme.css @@ -0,0 +1,97 @@ +/* Base Theme Configuration */ +@layer base { + :root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.2686 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.2686 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.2686 0 0); + --primary: oklch(0.7686 0.1647 70.0804); + --primary-foreground: oklch(0 0 0); + --secondary: oklch(0.9670 0.0029 264.5419); + --secondary-foreground: oklch(0.4461 0.0263 256.8018); + --muted: oklch(0.9846 0.0017 247.8389); + --muted-foreground: oklch(0.5510 0.0234 264.3637); + --accent: oklch(0.9869 0.0214 95.2774); + --accent-foreground: oklch(0.4732 0.1247 46.2007); + --destructive: oklch(0.6368 0.2078 25.3313); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0.9276 0.0058 264.5313); + --input: oklch(0.9276 0.0058 264.5313); + --ring: oklch(0.7686 0.1647 70.0804); + --chart-1: oklch(0.7686 0.1647 70.0804); + --chart-2: oklch(0.6658 0.1574 58.3183); + --chart-3: oklch(0.5553 0.1455 48.9975); + --chart-4: oklch(0.4732 0.1247 46.2007); + --chart-5: oklch(0.4137 0.1054 45.9038); + --radius: 0.375rem; + } + + .dark { + --background: oklch(0.2046 0 0); + --foreground: oklch(0.9219 0 0); + --card: oklch(0.2686 0 0); + --card-foreground: oklch(0.9219 0 0); + --popover: oklch(0.2686 0 0); + --popover-foreground: oklch(0.9219 0 0); + --primary: oklch(0.7686 0.1647 70.0804); + --primary-foreground: oklch(0 0 0); + --secondary: oklch(0.2686 0 0); + --secondary-foreground: oklch(0.9219 0 0); + --muted: oklch(0.2393 0 0); + --muted-foreground: oklch(0.7155 0 0); + --accent: oklch(0.4732 0.1247 46.2007); + --accent-foreground: oklch(0.9243 0.1151 95.7459); + --destructive: oklch(0.6368 0.2078 25.3313); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0.3715 0 0); + --input: oklch(0.3715 0 0); + --ring: oklch(0.7686 0.1647 70.0804); + --chart-1: oklch(0.8369 0.1644 84.4286); + --chart-2: oklch(0.6658 0.1574 58.3183); + --chart-3: oklch(0.4732 0.1247 46.2007); + --chart-4: oklch(0.5553 0.1455 48.9975); + --chart-5: oklch(0.4732 0.1247 46.2007); + } + + * { + @apply border-border; + } + + body { + @apply bg-background text-foreground; + } +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} diff --git a/skills/tailwind-setup/scripts/apply-theme.sh b/skills/tailwind-setup/scripts/apply-theme.sh new file mode 100755 index 0000000..c5604a8 --- /dev/null +++ b/skills/tailwind-setup/scripts/apply-theme.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# Script to apply Tailwind theme to index.css +# Usage: bash apply-theme.sh +# Example: bash apply-theme.sh frontend + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +THEME_FILE="$SCRIPT_DIR/../assets/tailwind-theme.css" + +# Check if frontend directory argument provided +if [ -z "$1" ]; then + echo "Error: Please provide the frontend directory path" + echo "Usage: bash apply-theme.sh " + echo "Example: bash apply-theme.sh frontend" + exit 1 +fi + +TARGET_DIR="$1" +TARGET_FILE="$TARGET_DIR/src/index.css" + +# Check if target file exists +if [ ! -f "$TARGET_FILE" ]; then + echo "Error: $TARGET_FILE not found." + echo "Make sure '$TARGET_DIR' is the correct frontend directory path." + exit 1 +fi + +# Check if theme file exists +if [ ! -f "$THEME_FILE" ]; then + echo "Error: Theme file not found at $THEME_FILE" + exit 1 +fi + +# Check if theme already applied +if grep -q "Base Theme Configuration" "$TARGET_FILE"; then + echo "Theme already appears to be applied to $TARGET_FILE" + echo "Skipping to avoid duplication." + exit 0 +fi + +# Append theme to index.css +echo "" >> "$TARGET_FILE" +cat "$THEME_FILE" >> "$TARGET_FILE" + +echo "✓ Tailwind theme successfully applied to $TARGET_FILE"