update readme
This commit is contained in:
477
README.md
477
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<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.
|
||||
|
||||
Reference in New Issue
Block a user