update readme

This commit is contained in:
Krrish Ghimire
2026-07-08 23:05:53 +05:45
parent 77c4cfc777
commit 87d4acc2e2

477
README.md
View File

@@ -1,6 +1,6 @@
# Indie — Independent Website Platform
A platform that lets anyone create, own, and publish their own static website without social media gatekeeping. Built with a drag-and-drop builder and LLM prompt-to-site generation. Every site is backed by a git repository.
A platform that lets anyone create, own, and publish their own static website through AI prompts. Describe what you want, and the AI generates it. Every site is backed by a git repository.
---
@@ -11,8 +11,7 @@ A platform that lets anyone create, own, and publish their own static website wi
- Java 17+
- Node.js 18+ and npm
- Docker & Docker Compose (for PostgreSQL, MongoDB, MinIO)
- Angular CLI 17 (`npm install -g @angular/cli@17`)
- Gradle wrapper (included)
- npm packages: `@angular/cli@17` (`npm install -g @angular/cli@17`)
### Setup
@@ -22,6 +21,8 @@ A platform that lets anyone create, own, and publish their own static website wi
docker compose up -d
```
This starts PostgreSQL (port 5432), MongoDB (port 27017), and MinIO (ports 9000/9001).
2. **Start the backend:**
```bash
@@ -43,22 +44,64 @@ A platform that lets anyone create, own, and publish their own static website wi
The app will be available at `http://localhost:4200`.
API calls are proxied to `http://localhost:8080`.
### Environment Variables
---
## Infrastructure (Docker Compose)
| Service | Image | Ports | Credentials | Volume |
|------------|--------------------|--------------|--------------------------------|---------------------|
| PostgreSQL | `postgres:16-alpine` | host:5432 | `indie` / `indie_dev` | `pgdata` |
| MongoDB | `mongo:7` | host:27017 | (no auth) | `mongodata` |
| MinIO | `minio/minio:latest` | host:9000 (S3 API), host:9001 (Console) | `indieadmin` / `indiesecret` | `miniodata` |
MinIO mimics Cloudflare R2/S3 for local development. Console: `http://localhost:9001`.
---
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| **Database** | | |
| `SPRING_DATASOURCE_URL` | PostgreSQL JDBC URL | `jdbc:postgresql://localhost:5432/indie` |
| `SPRING_DATASOURCE_USERNAME` | DB username | `indie` |
| `SPRING_DATASOURCE_PASSWORD` | DB password | `indie_dev` |
| `SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_ID` | Google OAuth 2.0 client ID (omit to disable Google login) | — |
| `SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_SECRET` | Google OAuth 2.0 client secret | — |
| `SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GITHUB_CLIENT_ID` | GitHub OAuth App client ID (omit to disable GitHub login) | — |
| `SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GITHUB_CLIENT_SECRET` | GitHub OAuth App client secret | — |
| `MONGODB_URI` | MongoDB connection URI | `mongodb://localhost:27017/indie` |
| **JWT** | | |
| `INDIE_JWT_ACCESS_EXPIRATION` | Access token TTL (ms) | `900000` (15 min) |
| `INDIE_JWT_REFRESH_EXPIRATION` | Refresh token TTL (ms) | `604800000` (7 days) |
| `INDIE_JWT_PRIVATE_KEY_PATH` | RS256 private key path | `config/jwt-private.key` |
| `INDIE_JWT_PUBLIC_KEY_PATH` | RS256 public key path | `config/jwt-public.key` |
| **Git** | | |
| `INDIE_GIT_REPOS_PATH` | Git repositories root | `/var/git/sites/` |
| `INDIE_GIT_AUTHOR_NAME` | Default git author name | `Indie Platform` |
| `INDIE_GIT_AUTHOR_EMAIL` | Default git author email | `git@indie.local` |
| **Storage (S3-compatible)** | | |
| `INDIE_STORAGE_ENDPOINT` | S3 endpoint URL | `http://localhost:9000` |
| `INDIE_STORAGE_REGION` | S3 region | `auto` |
| `INDIE_STORAGE_ACCESS_KEY` | S3 access key | `indie` |
| `INDIE_STORAGE_SECRET_KEY` | S3 secret key | `indie_dev` |
| `INDIE_STORAGE_BUCKET` | S3 bucket name | `indie-sites` |
| `INDIE_STORAGE_PUBLIC_URL` | Public URL base for stored files | `http://localhost:9000/indie-sites` |
| **Encryption** | | |
| `INDIE_ENCRYPTION_KEY_PATH` | AES-256-GCM master key path | `config/encryption.key` |
| **App** | | |
| `INDIE_APP_BASE_URL` | Frontend base URL (for verification emails) | `http://localhost:4200` |
| `INDIE_VERIFICATION_TOKEN_EXPIRATION` | Email verification token TTL (minutes) | `1440` (24h) |
| `INDIE_CORS_ORIGINS` | Allowed CORS origins (comma-separated) | `http://localhost:4200,http://localhost:3000` |
| **LLM** | | |
| `GEMINI_API_KEY` | Google Gemini API key | — |
| `GEMINI_MODEL` | Gemini model name | `gemini-2.0-flash` |
| `OPENAI_API_KEY` | OpenAI API key | — |
| `OPENAI_MODEL` | OpenAI model name | `gpt-4o-mini` |
| `INDIE_LLM_RATE_LIMIT` | Max LLM requests per hour per user | `10` |
| **Email (MailHog compatible)** | | |
| `EMAIL_HOST` | SMTP host | `localhost` |
| `EMAIL_PORT` | SMTP port | `1025` |
| `EMAIL_USERNAME` | SMTP username | _(empty)_ |
| `EMAIL_PASSWORD` | SMTP password | _(empty)_ |
| `EMAIL_SMTP_AUTH` | Enable SMTP auth | `true` |
| `EMAIL_SMTP_STARTTLS` | Enable STARTTLS | `true` |
---
@@ -70,81 +113,176 @@ backend/
├── settings.gradle # Project name
├── gradlew / gradlew.bat # Gradle wrapper
├── gradle/wrapper/ # Wrapper JAR + properties
└── src/main/
├── java/com/indie/
│ ├── IndieApplication.java # Spring Boot main class
└── src/
├── main/java/com/krrishg/
│ ├── IndieApplication.java # Spring Boot main class (@EnableScheduling)
│ ├── config/
│ │ ├── SecurityConfig.java # HTTP security, JWT filter, OAuth2 login, CORS
│ │ ├── SecurityConfig.java # HTTP security, JWT filter, CORS
│ │ ├── JwtConfig.java # RS256 token generation and validation
│ │ ── GitConfig.java # Git repository settings
│ │ ── TokenService.java # Interface for token generation/validation
│ │ ├── JwtAuthenticationToken.java # Custom AbstractAuthenticationToken
│ │ ├── UserPrincipal.java # UserDetails record
│ │ ├── GitConfig.java # Git repository settings
│ │ ├── StorageConfig.java # S3Client bean (MinIO / S3-compatible)
│ │ ├── EncryptionService.java # AES-256/GCM for LLM API key encryption
│ │ ├── RateLimitConfig.java # Bucket4j token-bucket rate limiter
│ │ ├── SchemaMigration.java # Startup DDL migrations
│ │ ├── MapToJsonConverter.java # JPA converter for JSON text columns
│ │ └── GlobalExceptionHandler.java # @ControllerAdvice error handling
│ ├── model/
│ │ ├── User.java # User entity with local + OAuth support
│ │ ├── User.java # User entity (email, password, emailVerified)
│ │ ├── VerificationToken.java # Email verification / set-password tokens
│ │ ├── Site.java # Site entity per user
│ │ ├── Page.java # Page within a site
│ │ ├── Component.java # Component on a page (heading, text, image, etc.)
│ │ ├── JsonbConverter.java # JPA converter for JSONB columns
│ │ ── *.java # Enums (AuthProvider, SiteStatus, ComponentType)
│ │ ├── SiteStatus.java # Enum: DRAFT, PUBLISHED
│ │ ├── AuthProvider.java # Enum: LOCAL only (OAuth planned)
│ │ ├── GitRemote.java # Connected git remote (GitHub/GitLab)
│ │ ── SelfHostedConfig.java # SSH/deploy configuration per site
│ │ └── GenerationVersion.java # MongoDB: LLM generation history per site
│ ├── repository/
│ │ ├── UserRepository.java
│ │ ├── SiteRepository.java
│ │ ├── PageRepository.java
│ │ ── ComponentRepository.java
│ │ ├── GitRemoteRepository.java
│ │ ── SelfHostedConfigRepository.java
│ │ └── VerificationTokenRepository.java
│ ├── dto/
│ │ ├── RegisterRequest.java
│ │ ├── RegistrationResponse.java
│ │ ├── LoginRequest.java
│ │ ├── RefreshTokenRequest.java
│ │ ├── AuthResponse.java # JWT tokens + user info
│ │ ├── SiteRequest.java
│ │ ├── VerifyEmailRequest.java
│ │ ├── VerifyEmailResponse.java
│ │ ├── ResendVerificationRequest.java
│ │ ├── SetPasswordRequest.java
│ │ ├── SiteRequest.java # Includes deploymentTarget field
│ │ ├── SiteResponse.java
│ │ ├── PageRequest.java
│ │ ── PageResponse.java
│ │ ├── SiteStructure.java # Top-level DTO: pages + theme
│ │ ── Reference.java # URL/IMAGE reference for LLM context
│ │ ├── LLMOptions.java # LLM generation options
│ │ ├── LLMResult.java # LLM generation result
│ │ ├── LLMConfigRequest.java # User LLM provider config
│ │ ├── SelfHostedConfigRequest.java
│ │ └── SelfHostedConfigResponse.java
│ ├── service/
│ │ ├── AuthService.java # Register, login, token refresh
│ │ ├── OAuth2UserService.java # Maps Google/GitHub user info to User entity
│ │ ── OAuth2SuccessHandler.java # Redirects with JWT after OAuth login
│ │ ├── AuthService.java # Register, login, token refresh, verify email
│ │ ├── EmailService.java # Verification email sender
│ │ ── CleanupService.java # Scheduled cleanup of stale unverified accounts
│ │ ├── LLMService.java # Orchestrates LLM calls, stores/restores versions
│ │ ├── LLMResponseParser.java # Parses LLM JSON into SiteStructure
│ │ ├── ReferenceService.java # Fetches web page content for LLM context
│ │ ├── RenderService.java # Renders SiteStructure into HTML + CSS + JS
│ │ ├── DeploymentService.java # Orchestrates publish/unpublish
│ │ ├── Deployer.java # Interface for self-hosted deployment
│ │ ├── SshRsyncDeployer.java # SSH + rsync + nginx + Let's Encrypt
│ │ ├── PagesProvider.java # Interface for git provider Pages
│ │ ├── GitHubPagesProvider.java # GitHub Pages implementation
│ │ ├── GitLabPagesProvider.java # GitLab Pages implementation
│ │ ├── GitService.java # JGit: init, commit, push, log, rollback
│ │ ├── KeyManager.java # SSH key-pair generation per site
│ │ ├── MediaService.java # S3/MinIO file upload and public URLs
│ │ ├── ExportService.java # Exports site structure to ZIP
│ │ └── DomainService.java # Custom domain resolution skeleton
│ ├── service/llm/
│ │ ├── LLMProvider.java # Interface for LLM providers
│ │ ├── GeminiProvider.java # Google Gemini implementation
│ │ ├── OpenAIProvider.java # OpenAI implementation
│ │ └── ProviderFactory.java # Factory creating providers by name + key
│ ├── engine/
│ │ ├── JsBundle.java # Generates static JS (nav, smooth scroll, lightbox, contact form)
│ │ └── StyleCompiler.java # Generates base CSS with custom property fallbacks
│ └── controller/
│ ├── AuthController.java # POST /api/auth/register, /login, /refresh, /logout
│ ├── AuthController.java # POST /api/auth/*
│ ├── SiteController.java # CRUD /api/sites
── PageController.java # CRUD /api/sites/{siteId}/pages
└── resources/
└── application.yml # All configuration
── LLMController.java # POST /api/llm/generate, /refine, /versions/*
│ ├── GitController.java # POST /api/sites/{id}/git/*
├── DeploymentController.java # POST /api/sites/{id}/publish, /unpublish
│ ├── MediaController.java # POST /api/media/upload, GET /api/media/{key}
│ ├── ExportController.java # GET /api/sites/{id}/export
│ ├── UserSettingsController.java # GET/PUT/DELETE /api/user/llm-config
│ └── SelfHostedController.java # CRUD /api/sites/{id}/self-hosted
├── main/resources/
│ ├── application.yml # All configuration
│ └── prompts/
│ ├── site-generator.txt # LLM system prompt for site generation
│ └── site-refiner.txt # LLM system prompt for refinement
└── test/java/com/krrishg/
├── config/ # 7 config tests
├── controller/ # 9 controller tests
├── dto/ # 7 DTO tests
├── engine/ # 2 engine tests
├── model/ # 7 model tests
├── repository/ # 4 repository tests
├── service/ # 16 service tests + fakes
├── service/llm/ # 3 LLM provider tests
└── support/ # 13 test fakes, stubs, and configs
frontend/
├── proxy.conf.json # Dev proxy to backend
├── angular.json # Angular CLI configuration
├── tailwind.config.js # Tailwind CSS configuration
├── Dockerfile # Multi-stage production build
├── nginx.conf # Nginx config (SPA fallback + /api/ proxy)
├── proxy.conf.json # Dev proxy to backend
├── angular.json # Angular CLI configuration
├── tailwind.config.js # Tailwind CSS configuration
├── postcss.config.js # PostCSS (tailwindcss + autoprefixer)
├── package.json
── src/
├── index.html
├── styles.css # Global styles + Tailwind imports
── app/
├── app.component.ts # Root component
├── app.config.ts # App providers
├── app.routes.ts # Lazy-loaded routes
── core/
├── models/
│ │ ├── auth-response.ts # AuthResponse & UserInfo interfaces
├── site.ts # SiteResponse interface
── page.ts # PageResponse interface
│ ├── services/
│ │ ├── auth.service.ts # Auth HTTP + token storage
│ │ ├── site.service.ts # Site CRUD HTTP
│ │ ── page.service.ts # Page CRUD HTTP
│ ├── guards/
│ │ └── auth.guard.ts # Route guard (redirects to /login)
── interceptors/
── jwt.interceptor.ts # Attaches Bearer token
├── auth/
│ ├── login/
│ │ ── login.component.ts # Email/password login form
└── register/
└── register.component.ts # Name/email/password register form
└── dashboard/
├── dashboard.component.ts # Site cards grid + toolbar
── create-site-dialog/
│ └── create-site-dialog.ts # Create site dialog
└── delete-site-dialog/
└── delete-site-dialog.ts # Confirm delete dialog
── tsconfig.json / tsconfig.app.json / tsconfig.spec.json
├── src/
├── index.html
── main.ts # Angular bootstrap
├── styles.css # Global styles + Tailwind imports
├── favicon.ico
├── assets/
── app/
├── app.component.ts / .html / .css
├── app.config.ts # App providers
├── app.routes.ts # Lazy-loaded routes
── core/
│ ├── models/
│ │ ├── auth-response.ts # AuthResponse & UserInfo
│ │ ├── site.ts # SiteResponse
│ │ ── page.ts # PageResponse
│ ├── llm.ts # LLM request/response types, GenerationVersion
│ │ └── deployment.ts # Git/self-hosted config types
── services/
── auth.service.ts # Auth HTTP + token storage
│ │ ├── site.service.ts # Site CRUD HTTP
│ ├── page.service.ts # Page CRUD HTTP
│ │ ── deployment.service.ts # Export, git, publish HTTP
│ ├── llm-api.service.ts # LLM generate/refine/version HTTP
└── llm-config.service.ts # User LLM key storage HTTP
│ ├── guards/
├── auth.guard.ts # Redirects unauthenticated to /login
── login.guard.ts # Redirects authenticated to /dashboard
│ └── interceptors/
└── jwt.interceptor.ts # Attaches Bearer token
├── auth/
│ │ ├── login/
│ │ │ └── login.component.ts
│ │ ├── register/
│ │ │ └── register.component.ts
│ │ └── verify-email/
│ │ └── verify-email.component.ts # Email verification + set-password
│ ├── dashboard/
│ │ ├── dashboard.component.ts # Site cards grid + toolbar
│ │ ├── create-site-dialog/
│ │ │ └── create-site-dialog.ts
│ │ └── delete-site-dialog/
│ │ └── delete-site-dialog.ts
│ └── llm-workspace/
│ ├── llm-workspace.component.ts # Main workspace (prompt, preview, timeline)
│ ├── llm-session.service.ts # LLM generation session state
│ ├── settings/
│ │ └── settings.component.ts # LLM provider key/dialog settings
│ ├── git-settings/
│ │ └── git-settings.component.ts # Git remote connection settings
│ ├── preview-dialog/
│ │ └── preview-dialog.component.ts # Preview generated site
│ └── delete-version-dialog/
│ └── delete-version-dialog.ts # Confirm delete version
config/ # Auto-generated on first boot
├── jwt-private.key # RS256 private key
├── jwt-public.key # RS256 public key
└── encryption.key # AES-256-GCM master key (base64)
```
---
@@ -155,30 +293,86 @@ frontend/
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/auth/register` | Register with email + password |
| POST | `/api/auth/register` | Register with name + email + password (returns verification pending) |
| POST | `/api/auth/login` | Login with email + password |
| POST | `/api/auth/refresh` | Refresh access token |
| POST | `/api/auth/logout` | Logout |
| POST | `/api/auth/verify-email` | Verify email via token, returns setup token |
| POST | `/api/auth/resend-verification` | Resend verification email |
| POST | `/api/auth/set-password` | Set password after email verification |
### Sites (`/api/sites`)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/sites` | List user's sites |
| POST | `/api/sites` | Create a new site |
| POST | `/api/sites` | Create a new site (with `deploymentTarget`: `GIT_PAGES`, `SELF_HOSTED`, or `NONE`) |
| GET | `/api/sites/{id}` | Get site details |
| PUT | `/api/sites/{id}` | Update site |
| DELETE | `/api/sites/{id}` | Delete site |
### Pages (`/api/sites/{siteId}/pages`)
### LLM Generation (`/api/llm`)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/sites/{siteId}/pages` | List pages for a site |
| POST | `/api/sites/{siteId}/pages` | Create a new page |
| GET | `/api/sites/{siteId}/pages/{pageId}` | Get page details |
| PUT | `/api/sites/{siteId}/pages/{pageId}` | Update page |
| DELETE | `/api/sites/{siteId}/pages/{pageId}` | Delete page |
| POST | `/api/llm/generate` | Generate site from prompt (rate-limited to 10/hour/user) |
| POST | `/api/llm/refine` | Refine existing site structure with follow-up prompt |
| GET | `/api/llm/versions/{siteId}` | List generation versions for a site |
| POST | `/api/llm/versions/{id}/restore` | Restore a specific generation version |
| DELETE | `/api/llm/versions/{id}` | Delete a generation version |
### Git Remotes (`/api/sites/{id}/git`)
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/sites/{id}/git/connect` | Connect git remote (GitHub/GitLab) |
| POST | `/api/sites/{id}/git/disconnect` | Disconnect git remote |
| GET | `/api/sites/{id}/git/status` | Get connection status, SSH keys, commit log |
### Deployment (`/api/sites/{id}`)
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/sites/{id}/publish` | Publish site (git push + S3 upload) |
| POST | `/api/sites/{id}/unpublish` | Unpublish site |
| GET | `/api/sites/{id}/export` | Download site as `.zip` archive |
### Self-Hosted Deployment (`/api/sites/{id}/self-hosted`)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/sites/{id}/self-hosted` | Get SSH/deploy configuration |
| PUT | `/api/sites/{id}/self-hosted` | Save SSH/deploy configuration |
| DELETE | `/api/sites/{id}/self-hosted` | Remove configuration |
### Media (`/api/media`)
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/media/upload` | Upload a file (returns key, url, metadata) |
| GET | `/api/media/{key}` | Get public URL for a stored file |
### User Settings (`/api/user`)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/user/llm-config` | Get saved LLM provider configs (keys redacted) |
| PUT | `/api/user/llm-config` | Save API key / base URL / model for a provider |
| DELETE | `/api/user/llm-config/{provider}` | Delete saved config for a provider |
---
## Frontend Routes
| Path | Component | Auth |
|------|-----------|------|
| `/login` | LoginComponent | Redirects to /dashboard if already logged in |
| `/register` | RegisterComponent | Redirects to /dashboard if already logged in |
| `/verify-email` | VerifyEmailComponent | Public (email verification flow) |
| `/dashboard` | DashboardComponent | Requires auth |
| `/llm/:siteId` | LlmWorkspaceComponent | Requires auth |
| `/llm/:siteId/settings` | SettingsComponent | Requires auth |
| `/llm/:siteId/git` | GitSettingsComponent | Requires auth |
---
@@ -186,44 +380,155 @@ frontend/
| Layer | Technology |
|-------|-----------|
| Backend | Java 17, Spring Boot 3.2, Gradle |
| Frontend | Angular 17, Angular Material, Tailwind CSS |
| Backend | Java 17, Spring Boot 3.2, Gradle (Groovy) |
| Frontend | Angular 17, Angular Material, Tailwind CSS, Leaflet |
| Relational DB | PostgreSQL 16 |
| Document DB | MongoDB 7 |
| Document DB | MongoDB 7 (generation versions, LLM sessions) |
| Object Storage | MinIO (dev) / Cloudflare R2 (prod) |
| Auth | Spring Security + OAuth2 Client + JWT (RS256) |
| Git | JGit |
| Auth | Spring Security + JWT (RS256), email/password |
| LLM | Gemini (primary), OpenAI (fallback), provider-abstraction |
| Git | JGit — local repo per site, GitHub/GitLab remote push |
| Email | Spring Boot Mail (MailHog compatible) |
| Rate Limiting | Bucket4j |
| API Docs | Springdoc OpenAPI (Swagger UI) |
---
## Architecture
```
┌──────────────────────┐
│ Cloudflare CDN │
│ custom domains → R2 │
└────────┬─────────────┘
┌────────▼─────────────┐
│ Cloudflare R2 │
│ indie-sites bucket │
│ /{siteId}/* │
└──────────────────────┘
┌────────┴─────────────┐
│ Spring Boot API │
│ (VPS / Docker) │
└────────┬─────────────┘
┌──────────────┼──────────────┐
│ │ │
┌────────▼───┐ ┌──────▼──────┐ ┌───▼────────┐
│ PostgreSQL │ │ MongoDB │ │ Git Repos │
│ (users, │ │ (versions, │ │ /var/git/ │
│ sites) │ │ sessions) │ └────────────┘
└─────────────┘ └─────────────┘
```
---
## Development
```bash
# Backend
cd backend
./gradlew build # Build
./gradlew test # Run tests
./gradlew test # Run tests (unit + slice + integration)
./gradlew bootRun # Run locally (requires docker compose up -d first)
# Frontend
cd frontend
npm install # Install dependencies
ng serve # Dev server at localhost:4200
ng build # Production build
ng build # Production build (outputs to dist/)
ng test # Run unit tests (Jasmine + Karma)
```
### Production Build (Docker)
```bash
docker build -t indie-frontend frontend/
docker run -p 80:80 indie-frontend
```
The frontend Docker image serves the app via nginx (port 80) and proxies `/api/` requests to `http://backend:8080`.
---
## Build Status
## Roadmap
### v0.1 — Auth & Foundation ✅
- [x] User registration (email/password)
- [x] User registration (email/password + email verification)
- [x] User login (email/password + JWT)
- [x] OAuth2 login (Google, GitHub) with JWT redirect
- [x] Token refresh with 7-day refresh token
- [x] Site CRUD (create, list, get, update, delete)
- [x] Page CRUD within a site
- [x] Frontend: Login, Register, OAuth callback pages
- [x] Frontend: Dashboard with site cards, create/delete dialogs
- [x] Frontend: JWT interceptor, auth guard, lazy routes
- [x] Frontend: Login, Register, Verify Email, Dashboard
- [x] Frontend: JWT interceptor, auth guard, login guard, lazy routes
- [x] Scheduled cleanup of unverified accounts
### v0.2 — LLM Prompt Workspace (in progress)
- [ ] Prompt-to-site generation via Gemini/OpenAI
- [ ] Version timeline with MongoDB storage
- [ ] Live preview (sandboxed iframe)
- [ ] Content editor (grouped by page)
- [ ] Refine via follow-up prompts
- [ ] Rate limiting (10 generations/hour/user)
- [ ] Reference URL support for context
### v0.3 — Export, Git & Hosting (planned)
- [ ] Render engine (HTML + CSS + JS generation)
- [ ] Git init, commit, push via JGit
- [ ] SSH key-pair management per site
- [ ] GitHub Pages / GitLab Pages providers
- [ ] S3 upload (R2 in production)
- [ ] Export as `.zip` archive
- [ ] Publish/unpublish flow
- [ ] Custom domain skeleton
- [ ] Media file upload service
### v0.4 — Social Features & Polish (planned)
- [ ] Comments with moderation
- [ ] RSS feed generation
- [ ] Git history panel in workspace
- [ ] Self-hosted deployment (SSH + rsync + nginx)
---
## LLM Provider Abstraction
```java
public interface LLMProvider {
LLMResult generate(String systemPrompt, String userPrompt,
List<Reference> references, LLMOptions options);
String getName();
}
```
Adding a new provider = implement `LLMProvider` interface + add config block in `application.yml`.
Provider API keys can be configured globally via env vars (`GEMINI_API_KEY`, `OPENAI_API_KEY`) or per-user through the `/api/user/llm-config` endpoint (encrypted at rest via AES-256-GCM).
---
## Key Design Decisions
1. **Pure prompt-based** — No drag-and-drop builder. Every site starts from a prompt. Iteration happens through follow-up prompts and inline text edits.
2. **Static output only** — Generated sites are pure HTML/CSS/JS. No runtime framework dependency. Users own the output forever.
3. **Git as source of truth** — Every site is a git repository. Publish = commit. Rollback = checkout. Export includes full `.git/`.
4. **Version timeline** — Every generation creates a version in MongoDB. Users can browse, preview, and restore any previous version.
5. **No billing in MVP** — Monetization is post-MVP.
---
## config/ Directory
On first boot, the application auto-generates the following files in `config/`:
| File | Algorithm | Purpose |
|------|-----------|---------|
| `jwt-private.key` | RS256 | JWT token signing |
| `jwt-public.key` | RS256 | JWT token verification |
| `encryption.key` | AES-256-GCM (Base64) | LLM API key encryption |
In production, generate and mount these manually rather than relying on auto-generation.