From 87d4acc2e29a1155eb923c6ca88b770a35c621fe Mon Sep 17 00:00:00 2001 From: Krrish Ghimire Date: Wed, 8 Jul 2026 23:05:53 +0545 Subject: [PATCH] update readme --- README.md | 477 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 391 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index c082cfd..2ee9a82 100644 --- a/README.md +++ b/README.md @@ -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 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.