register, login, create and manage sites and pages

This commit is contained in:
Krrish Ghimire
2026-06-30 11:13:55 +05:45
commit 65f38d68c5
110 changed files with 28049 additions and 0 deletions

293
PLAN.md Normal file
View File

@@ -0,0 +1,293 @@
# 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. Users self-host or use free subdomain hosting.
---
## Tech Stack
| Layer | Technology |
|-------|-----------|
| Backend | Java 17, Spring Boot 3.x, Gradle (Groovy) |
| Frontend | Angular 17, Angular Material, Angular CDK (drag-drop) |
| Relational DB | PostgreSQL |
| Document DB | MongoDB (LLM sessions) |
| Object Storage | Cloudflare R2 (S3-compatible) |
| LLM | Gemini (primary), OpenAI (fallback), provider-abstraction for future |
| Auth | Spring Security + OAuth2 Client — email/password, Google OAuth, GitHub OAuth, OpenID Connect |
| Git | JGit — local repo per site, optional GitHub/GitLab remote push |
| Infra | Docker Compose (dev), R2 + Cloudflare (hosting), Caddy/nginx (prod reverse proxy) |
---
## Build Phases
### v0.1 — Auth & Foundation (Target: Weeks 1-3)
**Goal:** User can register, log in via email or OAuth, and create/manage sites and pages.
#### Backend
| File | Action | Details |
|------|--------|---------|
| `backend/build.gradle` | Add | Spring Boot 3.x, deps: web, jpa, security, oauth2-client, oauth2-resource-server, validation, lombok, postgresql, testcontainers, aws-java-sdk-s3 (R2), springdoc-openapi, jgit |
| `backend/settings.gradle` | Add | Project name, plugin management |
| `backend/src/main/resources/application.yml` | Add | DB configs, JWT keys, OAuth client IDs/secrets, LLM keys, R2 credentials, git config |
| `com/indie/IndieApplication.java` | Add | Main class |
| `com/indie/config/SecurityConfig.java` | Add | SecurityFilterChain with JWT filter + OAuth2 login + OpenID Connect. Route permissions: `/api/auth/**` public, `/api/**` authenticated |
| `com/indie/config/JwtConfig.java` | Add | RS256 signing, access token 15min, refresh token 7d |
| `com/indie/config/GitConfig.java` | Add | Repos root path (`/var/git/sites/`), author name/email |
| `com/indie/model/User.java` | Add | id(UUID), email, password(bcrypt, nullable), name, avatarUrl, provider(LOCAL/GOOGLE/GITHUB/OPENID), providerId, emailVerified, createdAt, updatedAt |
| `com/indie/model/Site.java` | Add | id, userId, name, description, subdomain(unique), themeConfig(JSONB), status(DRAFT/PUBLISHED), publishedAt, createdAt, updatedAt |
| `com/indie/model/Page.java` | Add | id, siteId(FK), title, slug, seoTitle, seoDescription, orderIndex, createdAt |
| `com/indie/model/Component.java` | Add | id, pageId(FK), type(enum), config(JSONB), styles(JSONB), orderIndex |
| `com/indie/repository/*.java` | Add | JPA repositories for User, Site, Page, Component |
| `com/indie/service/AuthService.java` | Add | register, login, refreshToken, oauthLogin |
| `com/indie/service/OAuth2UserService.java` | Add | Maps Google/GitHub/OpenID user info to User entity |
| `com/indie/controller/AuthController.java` | Add | `POST /api/auth/register`, `/login`, `/refresh`, `/logout` |
| `com/indie/controller/SiteController.java` | Add | Full CRUD: `GET/POST /api/sites`, `GET/PUT/DELETE /api/sites/{id}` |
| `com/indie/controller/PageController.java` | Add | CRUD for pages within a site |
#### Frontend
| File | Action | Details |
|------|--------|---------|
| `frontend/` | Init | Angular 17 standalone, Angular Material, lazy routes, Tailwind CSS |
| `src/app/auth/` | Add | LoginComponent, RegisterComponent, OAuth callback handler, AuthService, AuthGuard, JWT interceptor |
| `src/app/dashboard/` | Add | DashboardComponent (list site cards), CreateSiteDialog, DeleteSiteDialog |
#### Infrastructure
| File | Action | Details |
|------|--------|---------|
| `docker-compose.yml` | Add | PostgreSQL, MongoDB, MinIO (local R2 mock), backend (8080), frontend (4200) |
---
### v0.2 — Drag-and-Drop Builder (Target: Weeks 4-6)
**Goal:** User can visually build a website by dragging components onto a canvas, editing their properties, styling via theme editor, and previewing live.
#### Backend
| File | Action | Details |
|------|--------|---------|
| `com/indie/controller/BuilderController.java` | Add | `GET /api/sites/{id}/pages` (with nested components), `PUT /api/sites/{id}/pages/{pageId}/components` (batch save), `GET/PUT /api/sites/{id}/theme` |
| `com/indie/service/BuilderService.java` | Add | Validate component tree (type enum, config schema, max 50/page), save in transaction |
#### Frontend
| File | Action | Details |
|------|--------|---------|
| `src/app/builder/builder.component.ts` | Add | 3-panel layout (palette 250px, canvas flex, properties 320px) via CSS Grid or mat-sidenav |
| `src/app/builder/services/builder-state.service.ts` | Add | BehaviorSubject store: `components$`, `selectedComponentId$`, `themeConfig$`, `pages$`. Methods: add, remove, update, reorder. Undo/redo stack (50 snapshots) |
| `src/app/builder/services/auto-save.service.ts` | Add | 2s debounce → `PUT` batch save. "Saving..."/"Saved" indicator |
| `src/app/builder/component-palette/` | Add | PaletteComponent — Material list of 10 draggable component types, organized by category |
| `src/app/builder/canvas/` | Add | CanvasComponent — `cdkDropList` accepting drops, renders components in order, highlight on select, delete on hover |
| `src/app/builder/property-panel/` | Add | PropertyPanelComponent — dynamic form per type (config + styles sections). Material expansion panels |
| `src/app/builder/renderer/` | Add | Render components per type (heading, text, image, button, divider, gallery, video, contact_form, map, custom_html) — each takes `config` + `styles` as `@Input()` |
| `src/app/builder/theme-editor/` | Add | ThemeEditorComponent — color pickers, font selectors, spacing slider. Live preview updates |
| `src/app/builder/preview/` | Add | PreviewComponent — sandboxed `<iframe>` with `srcdoc`, responsive device toggles (desktop/tablet/mobile) |
| `src/app/builder/builder-api.service.ts` | Add | HTTP calls for save/load components, theme, pages |
**Component types:** heading, text, image, button, divider, gallery, video, contact_form, map, custom_html
---
### v0.3 — LLM Prompt-to-Site (Target: Weeks 7-8)
**Goal:** User types a description of their desired website → AI generates the full site structure (pages + components + theme) → user tweaks in builder or refines via another prompt.
#### Backend
| File | Action | Details |
|------|--------|---------|
| `com/indie/service/llm/LLMProvider.java` | Add | Interface: `generate(systemPrompt, userPrompt, options) → LLMResult`. `LLMResult`: content, tokenUsage, model, latency |
| `com/indie/service/llm/GeminiProvider.java` | Add | Implements LLMProvider via Gemini API. Config: `indie.llm.providers.gemini.api-key`, model |
| `com/indie/service/llm/OpenAIProvider.java` | Add | Implements LLMProvider via OpenAI API. Config: `indie.llm.providers.openai.api-key`, model |
| `com/indie/service/llm/LLMFactory.java` | Add | Returns configured provider from `indie.llm.active-provider`, fallback chain |
| `com/indie/dto/LLMOptions.java` | Add | temperature, maxTokens, timeout, responseFormat |
| `com/indie/dto/SiteStructure.java` | Add | Top-level DTO: `List<PageStructure> pages`, `ThemeConfig theme`. Nested types for page and component |
| `com/indie/dto/LLMResult.java` | Add | content(String), tokenUsage, model, latencyMs |
| `com/indie/model/LLMSession.java` | Add | MongoDB document: userId, prompt, generatedStructure, acceptedAt, refinedPrompt, createdAt |
| `com/indie/service/LLMService.java` | Add | Orchestrator: prompt → LLM → parse → validate → return SiteStructure |
| `com/indie/service/LLMResponseParser.java` | Add | Parse LLM JSON output, handle markdown fences, malformed fields, partial extraction |
| `com/indie/controller/LLMController.java` | Add | `POST /api/llm/generate` (prompt→structure), `POST /api/llm/refine` (structure+prompt→refined). Rate limit: 10/hour/user |
| `com/indie/config/RateLimitConfig.java` | Add | Rate limiting via bucket4j |
| `src/main/resources/prompts/site-generator.txt` | Add | System prompt: schema definition + 2-3 few-shot examples |
#### Frontend
| File | Action | Details |
|------|--------|---------|
| `src/app/llm-workspace/` | Add | LLMWorkspaceComponent — textarea, "Generate" button, loading spinner with ETA, result preview summary, "Apply to Builder" button |
| `src/app/llm-workspace/llm-session.service.ts` | Add | Prompt history, refinement flow (original prompt + user edits + refinement → new generation) |
---
### v0.4 — Export, Git & Hosting (Target: Weeks 9-11)
**Goal:** User can publish their site as pure static files. Every publish creates a git commit. User can connect GitHub/GitLab for automatic pushes. Sites served at `*.indie.com` for free.
#### Backend
| File | Action | Details |
|------|--------|---------|
| `com/indie/service/RenderService.java` | Add | Core render engine: `renderSite(site)`, `renderPage(site, page, components) → String`, `renderCss(theme, components) → String`, `renderJs() → String` |
| `com/indie/engine/templates/*.html` | Add | Thymeleaf templates per component type + `layout.html` + `head.html` |
| `com/indie/engine/StyleCompiler.java` | Add | Theme → CSS custom properties at `:root`, component styles → CSS classes, responsive breakpoints |
| `com/indie/engine/JsBundle.java` | Add | Generates `script.js`: nav toggle, smooth scroll, lightbox, contact form handler, comment form handler. Minified |
| `com/indie/service/ExportService.java` | Add | `export(siteId) → ZipOutputStream` — full site directory + `.git/` |
| `com/indie/controller/ExportController.java` | Add | `GET /api/sites/{id}/export``application/zip` download |
| `com/indie/service/GitService.java` | Add | `initRepo(siteId)`, `commit(siteId, message)`, `commitAndPush(siteId, message)`, `getLog(siteId)`, `rollback(siteId, commitHash)`, `exportRepo(siteId)` |
| `com/indie/model/GitRemote.java` | Add | JPA: id, siteId, provider(GITHUB/GITLAB), remoteUrl, encryptedToken, defaultBranch |
| `com/indie/service/KeyManager.java` | Add | SSH keypair generation per site, encrypted storage |
| `com/indie/controller/GitController.java` | Add | `POST /api/sites/{id}/git/connect`, `/disconnect`, `GET /api/sites/{id}/git/status` |
| `com/indie/service/DeploymentService.java` | Add | On publish: render → write to git working dir → `GitService.commit` → upload to R2 → return public URL |
| `com/indie/controller/DeploymentController.java` | Add | `POST /api/sites/{id}/publish`, `/unpublish` |
| `com/indie/service/DomainService.java` | Add | Skeleton: store custom domain mapping in Site entity, prepared for future TLS |
| `com/indie/service/MediaService.java` | Add | Upload to R2, validate type/size, resize |
| `com/indie/controller/MediaController.java` | Add | `POST /api/media/upload`, `GET /api/media/{id}` |
#### Frontend
| File | Action | Details |
|------|--------|---------|
| `src/app/builder/git-settings/` | Add | GitSettingsComponent — connect/disconnect GitHub/GitLab OAuth, select remote repo, connection status |
| `src/app/builder/settings/` | Add | SettingsComponent — site name, subdomain, export zip button, publish/unpublish buttons |
#### Infrastructure
| File | Action | Details |
|------|--------|---------|
| `infra/cloudflare-worker.js` | Add | Routes `*.indie.com` → R2 bucket `indie-sites/{subdomain}/` |
| `docker-compose.prod.yml` | Add | Production services: PostgreSQL, MongoDB, backend (×2), nginx, certbot |
**Git directory structure on server:**
```
/var/git/sites/
└── {siteId}/
├── .git/
├── index.html
├── about/index.html
├── style.css
├── script.js
└── assets/
```
---
### v0.5 — Social Features & Polish (Target: Weeks 12-13)
**Goal:** Minimal social features (comments, RSS) and final polish before public release.
#### Backend
| File | Action | Details |
|------|--------|---------|
| `com/indie/model/Comment.java` | Add | JPA: id(UUID), siteId, pageId, authorName, authorEmail, content, isApproved, parentCommentId, createdAt |
| `com/indie/controller/CommentController.java` | Add | Public: `POST /api/sites/{subdomain}/comments`. Authenticated: `GET/PUT/DELETE` moderation |
| `com/indie/service/RssService.java` | Add | `generateRss(siteId) → String` — RSS 2.0 feed from site pages |
| `com/indie/controller/RssController.java` | Add | `GET /api/feed/{subdomain}``application/rss+xml`, 1h cache |
#### Frontend
| File | Action | Details |
|------|--------|---------|
| `src/app/builder/settings/` | Modify | Add git history panel (commit list, rollback button, download repo) |
| `src/app/builder/components/comments-section/` | Add | In-builder config (enable/disable, moderate) + renderer for generated sites |
---
## Infrastructure Architecture
```
┌──────────────────────┐
│ Cloudflare CDN │
│ *.indie.com → R2 │
└────────┬─────────────┘
┌────────▼─────────────┐
│ Cloudflare R2 │
│ indie-sites bucket │
│ /{subdomain}/* │
└──────────────────────┘
┌────────┴─────────────┐
│ Spring Boot API │
│ (VPS / Docker) │
└────────┬─────────────┘
┌──────────────┼──────────────┐
│ │ │
┌────────▼───┐ ┌──────▼──────┐ ┌───▼────────┐
│ PostgreSQL │ │ MongoDB │ │ Git Repos │
│ (users, │ │ (LLM sess.) │ │ /var/git/ │
│ sites, │ └─────────────┘ └────────────┘
│ pages, │
│ comps) │
└─────────────┘
```
---
## LLM Provider Abstraction
```java
// Adding a new provider = implement this interface + add config
public interface LLMProvider {
LLMResult generate(String systemPrompt, String userPrompt, LLMOptions options);
String getName();
}
// application.yml
indie:
llm:
active-provider: gemini
fallback-provider: openai
providers:
gemini:
api-key: ${GEMINI_API_KEY}
model: gemini-2.0-flash
openai:
api-key: ${OPENAI_API_KEY}
model: gpt-4o-mini
```
---
## Key Design Decisions
1. **Static output only** — Generated sites are pure HTML/CSS/JS. No Angular/React dependency for end users. They own the output forever, no lock-in.
2. **Git as source of truth** — Every site is a git repository. Publish = commit. Rollback is a `git checkout`. Export includes full `.git/` history.
3. **Shared rendering** — The same component renderers drive the builder preview AND the static export. What you see is what you get.
4. **No billing in MVP** — Subdomain hosting is free. No Stripe/PayPal code. Monetization (paid hosting, custom domains) is a post-MVP concern.
5. **Incremental releases** — Each v0.x is independently shippable. If timeline slips, ship what's done.
---
## Validation Strategy
| Release | What to Test |
|---------|-------------|
| v0.1 | Register, login (email + OAuth), refresh token, site CRUD, page CRUD, 401 on unauthenticated |
| v0.2 | Drag → reorder → save → reload matches, theme change reflects in preview, undo/redo, all 10 component types render |
| v0.3 | LLM generates valid SiteStructure JSON, generation loads into builder, refinement updates correctly, error handling (timeout, bad JSON) |
| v0.4 | Exported zip opens in browser identically to preview, git init + commit works, git push to remote works, R2 upload succeeds, subdomain serves content |
| v0.5 | Comment submit → approve → display, RSS validates, image upload + resize, rollback restores previous version |
---
## Risk Register
| Risk | Likelihood | Mitigation |
|------|-----------|------------|
| Scope creep (adding features mid-phase) | High | Strict feature freeze per v0.x; ship as-is |
| Drag-drop performance with many components | Medium | Cap at 50 components/page; virtual scroll if needed |
| LLM output quality inconsistent | Medium | Iterate on system prompt; template gallery fallback |
| Solo developer burnout | Medium | Realistic timeline; celebrate each release |
| LLM API costs | Low | Gemini-2.0-flash is free-tier friendly; rate limit 10/hour/user |