# ImageCreator Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build a full-stack image generation website with user authentication, prompt optimization, and T2I/I2I generation capabilities. **Architecture:** FastAPI backend with PostgreSQL, Vue 3 frontend with Naive UI, Docker Compose deployment. Backend handles all image generation logic via adapter pattern (referencing prompt-optimizer architecture). **Tech Stack:** Python FastAPI, SQLAlchemy, PostgreSQL, JWT, Vue 3, TypeScript, Naive UI, TailwindCSS, Vite, Docker --- ## File Structure ``` /opt/imagecreator/imagecreator/ ├── backend/ │ ├── app/ │ │ ├── __init__.py │ │ ├── main.py # FastAPI app entry │ │ ├── config.py # Settings from env │ │ ├── database.py # SQLAlchemy engine/session │ │ ├── api/ │ │ │ ├── __init__.py │ │ │ ├── deps.py # Dependency injection (get_db, get_current_user) │ │ │ ├── auth.py # /api/auth routes │ │ │ ├── users.py # /api/users routes │ │ │ ├── images.py # /api/images routes │ │ │ └── history.py # /api/history routes │ │ ├── core/ │ │ │ ├── __init__.py │ │ │ └── security.py # JWT, password hashing │ │ ├── models/ │ │ │ ├── __init__.py │ │ │ ├── user.py # User SQLAlchemy model │ │ │ └── history.py # GenerationHistory model │ │ ├── schemas/ │ │ │ ├── __init__.py │ │ │ ├── auth.py # Login/Register schemas │ │ │ ├── user.py # User schemas │ │ │ ├── image.py # Image generate/optimize schemas │ │ │ └── history.py # History schemas │ │ └── services/ │ │ ├── __init__.py │ │ ├── auth.py # AuthService (register, login) │ │ ├── image.py # ImageService (generate, optimize) │ │ └── llm.py # LLM client for prompt optimization │ ├── tests/ │ │ ├── __init__.py │ │ ├── test_auth.py │ │ ├── test_images.py │ │ └── test_history.py │ ├── requirements.txt │ └── Dockerfile ├── frontend/ │ ├── src/ │ │ ├── api/ │ │ │ ├── index.ts # Axios instance │ │ │ ├── auth.ts # Auth API calls │ │ │ ├── images.ts # Image generation API calls │ │ │ └── history.ts # History API calls │ │ ├── components/ │ │ │ ├── ImageGenerator.vue │ │ │ ├── PromptOptimizer.vue │ │ │ ├── ImageHistory.vue │ │ │ ├── ImagePreview.vue │ │ │ └── ... │ │ ├── views/ │ │ │ ├── Login.vue │ │ │ ├── Register.vue │ │ │ ├── Home.vue │ │ │ └── History.vue │ │ ├── stores/ │ │ │ └── auth.ts # Pinia auth store │ │ ├── router/ │ │ │ └── index.ts │ │ ├── App.vue │ │ └── main.ts │ ├── package.json │ ├── vite.config.ts │ ├── tailwind.config.js │ ├── index.html │ └── Dockerfile ├── docker-compose.yml └── README.md ``` --- ## Phase 1: Project Structure & Backend Foundation ### Task 1: Create project directory structure **Files:** - Create: `/opt/imagecreator/imagecreator/backend/app/__init__.py` - Create: `/opt/imagecreator/imagecreator/backend/app/main.py` - Create: `/opt/imagecreator/imagecreator/backend/app/config.py` - Create: `/opt/imagecreator/imagecreator/backend/app/database.py` - Create: `/opt/imagecreator/imagecreator/backend/app/api/__init__.py` - Create: `/opt/imagecreator/imagecreator/backend/app/api/deps.py` - Create: `/opt/imagecreator/imagecreator/backend/app/core/__init__.py` - Create: `/opt/imagecreator/imagecreator/backend/app/core/security.py` - Create: `/opt/imagecreator/imagecreator/backend/app/models/__init__.py` - Create: `/opt/imagecreator/imagecreator/backend/app/models/user.py` - Create: `/opt/imagecreator/imagecreator/backend/app/models/history.py` - Create: `/opt/imagecreator/imagecreator/backend/app/schemas/__init__.py` - Create: `/opt/imagecreator/imagecreator/backend/app/schemas/auth.py` - Create: `/opt/imagecreator/imagecreator/backend/app/schemas/user.py` - Create: `/opt/imagecreator/imagecreator/backend/app/schemas/image.py` - Create: `/opt/imagecreator/imagecreator/backend/app/schemas/history.py` - Create: `/opt/imagecreator/imagecreator/backend/app/services/__init__.py` - Create: `/opt/imagecreator/imagecreator/backend/app/services/auth.py` - Create: `/opt/imagecreator/imagecreator/backend/app/services/image.py` - Create: `/opt/imagecreator/imagecreator/backend/app/services/llm.py` - Create: `/opt/imagecreator/imagecreator/backend/requirements.txt` - Create: `/opt/imagecreator/imagecreator/backend/Dockerfile` - Create: `/opt/imagecreator/imagecreator/frontend/` (Vue project structure) - [ ] **Step 1: Create backend directory structure** Run: `mkdir -p /opt/imagecreator/imagecreator/backend/app/{api,core,models,schemas,services} /opt/imagecreator/imagecreator/backend/tests` - [ ] **Step 2: Create backend requirements.txt** ```txt fastapi==0.109.0 uvicorn[standard]==0.27.0 sqlalchemy==2.0.25 psycopg2-binary==2.9.9 python-jose[cryptography]==3.3.0 passlib[bcrypt]==1.7.4 python-multipart==0.0.6 pydantic==2.5.3 pydantic-settings==2.1.0 google-genai==0.8.0 openai==1.12.0 aiofiles==23.2.1 httpx==0.26.0 pytest==8.0.0 pytest-asyncio==0.23.4 ``` - [ ] **Step 3: Create backend/app/__init__.py and main.py** ```python # app/__init__.py ``` ```python # app/main.py from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.api import auth, users, images, history from app.config import settings from app.database import engine from app.models import user, history as history_model app = FastAPI(title="ImageCreator API", version="1.0.0") app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) app.include_router(users.router, prefix="/api/users", tags=["users"]) app.include_router(images.router, prefix="/api/images", tags=["images"]) app.include_router(history.router, prefix="/api/history", tags=["history"]) @app.get("/health") def health_check(): return {"status": "ok"} ``` - [ ] **Step 4: Create app/config.py** ```python # app/config.py from pydantic_settings import BaseSettings from functools import lru_cache class Settings(BaseSettings): # Database DATABASE_URL: str = "postgresql://user:pass@localhost:5432/imagecreator" # JWT SECRET_KEY: str = "your-secret-key-change-in-production" ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 REFRESH_TOKEN_EXPIRE_DAYS: int = 7 # CORS CORS_ORIGINS: list[str] = ["http://localhost:3000"] # LLM API Keys GEMINI_API_KEY: str = "" OPENAI_API_KEY: str = "" # Image storage IMAGE_STORAGE_PATH: str = "/app/uploads" class Config: env_file = ".env" @lru_cache() def get_settings(): return Settings() settings = get_settings() ``` - [ ] **Step 5: Create app/database.py** ```python # app/database.py from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base from app.config import settings engine = create_engine(settings.DATABASE_URL, pool_pre_ping=True) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() def get_db(): db = SessionLocal() try: yield db finally: db.close() ``` - [ ] **Step 6: Commit** ```bash git add -A && git commit -m "feat: create backend project structure" ``` --- ### Task 2: Implement database models **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/models/user.py` - Modify: `/opt/imagecreator/imagecreator/backend/app/models/history.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_models.py` - [ ] **Step 1: Create User model** ```python # app/models/user.py import uuid from datetime import datetime from sqlalchemy import Column, String, DateTime from sqlalchemy.dialects.postgresql import UUID from app.database import Base class User(Base): __tablename__ = "users" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) username = Column(String(32), unique=True, nullable=False, index=True) email = Column(String(255), unique=True, nullable=False, index=True) hashed_password = Column(String(255), nullable=False) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) ``` ```python # app/models/history.py import uuid from datetime import datetime from sqlalchemy import Column, String, Text, DateTime, ForeignKey, Enum as SQLEnum from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship import enum from app.database import Base class GenerationMode(str, enum.Enum): TEXT2IMAGE = "text2image" IMAGE2IMAGE = "image2image" class GenerationHistory(Base): __tablename__ = "generation_history" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) original_prompt = Column(Text, nullable=False) optimized_prompt = Column(Text, nullable=False) mode = Column(SQLEnum(GenerationMode), nullable=False) model = Column(String(32), nullable=False) image_url = Column(String(512), nullable=False) input_image_url = Column(String(512), nullable=True) # For I2I created_at = Column(DateTime, default=datetime.utcnow) user = relationship("User", backref="generation_history") ``` - [ ] **Step 2: Create test for models** ```python # tests/test_models.py import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.database import Base from app.models.user import User from app.models.history import GenerationHistory, GenerationMode engine = create_engine("sqlite:///:memory:") TestSessionLocal = sessionmaker(bind=engine) def test_user_model(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="testuser", email="test@example.com", hashed_password="hashed") db.add(user) db.commit() assert user.id is not None assert user.username == "testuser" assert user.email == "test@example.com" db.close() def test_generation_history_model(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="testuser2", email="test2@example.com", hashed_password="hashed") db.add(user) db.commit() history = GenerationHistory( user_id=user.id, original_prompt="a cat", optimized_prompt="a fluffy orange cat sitting on a windowsill", mode=GenerationMode.TEXT2IMAGE, model="gemini-2.5-flash-image", image_url="/images/cat.png" ) db.add(history) db.commit() assert history.id is not None assert history.user_id == user.id assert history.mode == GenerationMode.TEXT2IMAGE db.close() ``` - [ ] **Step 3: Run tests** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_models.py -v` Expected: PASS - [ ] **Step 4: Commit** ```bash git add -A && git commit -m "feat: add database models (User, GenerationHistory)" ``` --- ### Task 3: Implement security module (JWT & password) **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/core/security.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_security.py` - [ ] **Step 1: Write failing test** ```python # tests/test_security.py import pytest from app.core.security import verify_password, hash_password, create_access_token, decode_access_token def test_password_hash_and_verify(): password = "testpassword123" hashed = hash_password(password) assert hashed != password assert verify_password(password, hashed) is True assert verify_password("wrongpassword", hashed) is False def test_access_token_creation_and_decode(): token = create_access_token({"sub": "testuser"}) assert token is not None payload = decode_access_token(token) assert payload is not None assert payload.sub == "testuser" ``` - [ ] **Step 2: Run test to verify it fails** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_security.py -v` Expected: FAIL - verify_password not defined - [ ] **Step 3: Write minimal implementation** ```python # app/core/security.py from datetime import datetime, timedelta from typing import Optional from jose import JWTError, jwt from passlib.context import CryptContext from app.config import settings pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) return encoded_jwt def decode_access_token(token: str) -> Optional[dict]: try: payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) return payload except JWTError: return None ``` - [ ] **Step 4: Run tests** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_security.py -v` Expected: PASS - [ ] **Step 5: Commit** ```bash git add -A && git commit -m "feat: add JWT and password security module" ``` --- ### Task 4: Implement auth API & service **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/schemas/auth.py` - Modify: `/opt/imagecreator/imagecreator/backend/app/services/auth.py` - Modify: `/opt/imagecreator/imagecreator/backend/app/api/auth.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_auth_api.py` - [ ] **Step 1: Write failing test** ```python # tests/test_auth_api.py import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.database import Base, get_db from app.main import app from app.models.user import User from app.core.security import hash_password engine = create_engine("sqlite:///:memory:") TestSessionLocal = sessionmaker(bind=engine) def override_get_db(): db = TestSessionLocal() try: yield db finally: db.close() app.dependency_overrides[get_db] = override_get_db client = TestClient(app) def test_register_success(): Base.metadata.create_all(bind=engine) response = client.post("/api/auth/register", json={ "username": "newuser", "email": "new@example.com", "password": "password123" }) assert response.status_code == 201 data = response.json() assert data["username"] == "newuser" assert "id" in data def test_register_duplicate_username(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="existing", email="existing@example.com", hashed_password=hash_password("pass")) db.add(user) db.commit() db.close() response = client.post("/api/auth/register", json={ "username": "existing", "email": "new@example.com", "password": "password123" }) assert response.status_code == 400 def test_login_success(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="loginuser", email="login@example.com", hashed_password=hash_password("correctpassword")) db.add(user) db.commit() db.close() response = client.post("/api/auth/login", json={ "username": "loginuser", "password": "correctpassword" }) assert response.status_code == 200 data = response.json() assert "access_token" in data assert "token_type" in data def test_login_wrong_password(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="wrongpass", email="wrongpass@example.com", hashed_password=hash_password("correctpassword")) db.add(user) db.commit() db.close() response = client.post("/api/auth/login", json={ "username": "wrongpass", "password": "wrongpassword" }) assert response.status_code == 401 ``` - [ ] **Step 2: Run test to verify it fails** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_auth_api.py -v` Expected: FAIL - modules not found - [ ] **Step 3: Write schemas** ```python # app/schemas/auth.py from pydantic import BaseModel, EmailStr class UserRegister(BaseModel): username: str email: EmailStr password: str class UserLogin(BaseModel): username: str password: str class Token(BaseModel): access_token: str refresh_token: str token_type: str = "bearer" class TokenRefresh(BaseModel): refresh_token: str ``` ```python # app/schemas/user.py from pydantic import BaseModel, EmailStr from typing import Optional from datetime import datetime from uuid import UUID class UserBase(BaseModel): username: str email: EmailStr class UserCreate(UserBase): password: str class UserResponse(UserBase): id: UUID created_at: datetime class Config: from_attributes = True class UserUpdate(BaseModel): email: Optional[EmailStr] = None ``` - [ ] **Step 4: Write auth service** ```python # app/services/auth.py from typing import Optional from sqlalchemy.orm import Session from uuid import UUID from app.models.user import User from app.core.security import hash_password, verify_password class AuthService: def __init__(self, db: Session): self.db = db def register(self, username: str, email: str, password: str) -> User: # Check if user exists existing = self.db.query(User).filter( (User.username == username) | (User.email == email) ).first() if existing: raise ValueError("Username or email already registered") hashed_password = hash_password(password) user = User(username=username, email=email, hashed_password=hashed_password) self.db.add(user) self.db.commit() self.db.refresh(user) return user def authenticate(self, username: str, password: str) -> Optional[User]: user = self.db.query(User).filter(User.username == username).first() if not user: return None if not verify_password(password, user.hashed_password): return None return user def get_user_by_id(self, user_id: UUID) -> Optional[User]: return self.db.query(User).filter(User.id == user_id).first() ``` - [ ] **Step 5: Write auth API routes** ```python # app/api/auth.py from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.database import get_db from app.schemas.auth import UserRegister, UserLogin, Token, TokenRefresh from app.schemas.user import UserResponse from app.services.auth import AuthService from app.core.security import create_access_token router = APIRouter() @router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED) def register(data: UserRegister, db: Session = Depends(get_db)): service = AuthService(db) try: user = service.register(data.username, data.email, data.password) return user except ValueError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) @router.post("/login", response_model=Token) def login(data: UserLogin, db: Session = Depends(get_db)): service = AuthService(db) user = service.authenticate(data.username, data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password" ) access_token = create_access_token(data={"sub": str(user.id)}) # For simplicity, using same token as refresh - in production, use separate refresh token return Token(access_token=access_token, refresh_token=access_token) @router.post("/refresh", response_model=Token) def refresh(data: TokenRefresh, db: Session = Depends(get_db)): # Simplified: in production, validate refresh token properly from app.core.security import decode_access_token payload = decode_access_token(data.refresh_token) if not payload: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") access_token = create_access_token(data={"sub": payload.get("sub")}) return Token(access_token=access_token, refresh_token=data.refresh_token) ``` - [ ] **Step 6: Write api/deps.py** ```python # app/api/deps.py from typing import Generator from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.orm import Session from app.database import get_db from app.core.security import decode_access_token from app.services.auth import AuthService security = HTTPBearer() def get_current_user( credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db) ): token = credentials.credentials payload = decode_access_token(token) if not payload: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token" ) user_id_str = payload.get("sub") if not user_id_str: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") from uuid import UUID try: user_id = UUID(user_id_str) except ValueError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid user ID") service = AuthService(db) user = service.get_user_by_id(user_id) if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found") return user ``` - [ ] **Step 7: Run tests** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_auth_api.py -v` Expected: PASS - [ ] **Step 8: Commit** ```bash git add -A && git commit -m "feat: implement auth API (register, login, JWT)" ``` --- ### Task 5: Implement users API **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/api/users.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_users_api.py` - [ ] **Step 1: Write failing test** ```python # tests/test_users_api.py import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.database import Base, get_db from app.main import app from app.models.user import User from app.core.security import hash_password, create_access_token engine = create_engine("sqlite:///:memory:") TestSessionLocal = sessionmaker(bind=engine) def override_get_db(): db = TestSessionLocal() try: yield db finally: db.close() app.dependency_overrides[get_db] = override_get_db client = TestClient(app) def get_auth_header(user_id): token = create_access_token({"sub": str(user_id)}) return {"Authorization": f"Bearer {token}"} def test_get_current_user(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="testuser", email="test@example.com", hashed_password=hash_password("pass")) db.add(user) db.commit() db.refresh(user) db.close() response = client.get("/api/users/me", headers=get_auth_header(user.id)) assert response.status_code == 200 data = response.json() assert data["username"] == "testuser" assert data["email"] == "test@example.com" assert "password" not in data ``` - [ ] **Step 2: Run test to verify it fails** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_users_api.py -v` Expected: FAIL - [ ] **Step 3: Write users API** ```python # app/api/users.py from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.database import get_db from app.api.deps import get_current_user from app.models.user import User from app.schemas.user import UserResponse, UserUpdate from app.services.auth import AuthService router = APIRouter() @router.get("/me", response_model=UserResponse) def get_current_user_info(current_user: User = Depends(get_current_user)): return current_user @router.put("/me", response_model=UserResponse) def update_current_user( data: UserUpdate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): service = AuthService(db) if data.email and data.email != current_user.email: # Check if email is taken existing = db.query(User).filter(User.email == data.email, User.id != current_user.id).first() if existing: raise HTTPException(status_code=400, detail="Email already registered") current_user.email = data.email db.commit() db.refresh(current_user) return current_user ``` - [ ] **Step 4: Run tests** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_users_api.py -v` Expected: PASS - [ ] **Step 5: Commit** ```bash git add -A && git commit -m "feat: implement users API" ``` --- ### Task 6: Implement LLM and prompt optimization service **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/services/llm.py` - Create: `/opt/imagecreator/imagecreator/backend/app/services/prompt.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_prompt.py` - [ ] **Step 1: Write failing test** ```python # tests/test_prompt.py import pytest from app.services.prompt import PromptOptimizationService def test_optimize_text2image_prompt(): service = PromptOptimizationService() result = service.optimize_text2image("a cat") assert result is not None assert len(result) > len("a cat") assert "cat" in result.lower() def test_optimize_image2image_prompt(): service = PromptOptimizationService() result = service.optimize_image2image("make it colorful", "a cat image") assert result is not None assert len(result) > 0 ``` - [ ] **Step 2: Run test to verify it fails** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_prompt.py -v` Expected: FAIL - module not found - [ ] **Step 3: Write LLM service** ```python # app/services/llm.py from typing import Optional import google.genai as genai from app.config import settings class LLMService: def __init__(self): genai.configure(api_key=settings.GEMINI_API_KEY) def generate_text(self, prompt: str, system_instruction: Optional[str] = None) -> str: client = genai.GenerativeModel("gemini-2.5-flash") response = client.generate_content(prompt) return response.text ``` ```python # app/services/prompt.py from app.services.llm import LLMService TEXT2IMAGE_SYSTEM_PROMPT = """You are an expert at optimizing text-to-image prompts. Your task is to take user's simple or vague prompt and enhance it with detailed, vivid descriptions. Include details about: subject, setting, lighting, mood, style, composition, and quality modifiers. Output ONLY the optimized prompt in English, no explanations.""" IMAGE2IMAGE_SYSTEM_PROMPT = """You are an expert at optimizing image-to-image prompts. The user wants to modify an existing image. Your task is to clearly describe what should be changed. Important rules: 1. Identify if user wants to ADD, REMOVE, REPLACE, or ENHANCE something 2. Be specific about what to keep vs what to change 3. Describe the desired style, mood, and quality 4. Output ONLY the optimized prompt in English, no explanations.""" class PromptOptimizationService: def __init__(self): self.llm = LLMService() def optimize_text2image(self, user_prompt: str) -> str: full_prompt = f"{TEXT2IMAGE_SYSTEM_PROMPT}\n\nUser's original prompt: {user_prompt}" return self.llm.generate_text(full_prompt) def optimize_image2image(self, user_prompt: str, original_description: str = "") -> str: context = f"Original image description: {original_description}\n\n" if original_description else "" full_prompt = f"{IMAGE2IMAGE_SYSTEM_PROMPT}\n\n{context}User's modification request: {user_prompt}" return self.llm.generate_text(full_prompt) ``` - [ ] **Step 4: Run tests (skipped if no API key)** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_prompt.py -v` Expected: SKIP or FAIL (if no API key) - [ ] **Step 5: Commit** ```bash git add -A && git commit -m "feat: implement LLM and prompt optimization service" ``` --- ### Task 7: Implement image generation service **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/services/image.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_image_service.py` - [ ] **Step 1: Write failing test** ```python # tests/test_image_service.py import pytest from app.services.image import ImageGenerationService def test_generate_text2image_request_structure(): # Test that the service creates proper request structure # This is a unit test - actual generation would require API key service = ImageGenerationService() # Just verify service initializes assert service is not None ``` - [ ] **Step 2: Write image service** ```python # app/services/image.py import base64 import os import uuid from typing import Optional import google.genai as genai from app.config import settings class ImageGenerationService: def __init__(self): genai.configure(api_key=settings.GEMINI_API_KEY) async def generate_text2image( self, prompt: str, model: str = "gemini-2.5-flash-image" ) -> dict: client = genai.GenerativeModel(model) response = client.generate_content( prompt, generation_config={"response_modalities": ["TEXT", "IMAGE"]} ) image_data = None for part in response.candidates[0].content.parts: if part.inline_data: image_data = base64.b64encode(part.inline_data.data).decode('utf-8') break return { "image_b64": image_data, "model": model, "finish_reason": response.candidates[0].finish_reason } async def generate_image2image( self, prompt: str, input_image_b64: str, input_mime_type: str = "image/png", model: str = "gemini-2.5-flash-image" ) -> dict: client = genai.GenerativeModel(model) response = client.generate_content( [ {"text": prompt}, {"inline_data": {"mime_type": input_mime_type, "data": input_image_b64}} ], generation_config={"response_modalities": ["TEXT", "IMAGE"]} ) image_data = None for part in response.candidates[0].content.parts: if part.inline_data: image_data = base64.b64encode(part.inline_data.data).decode('utf-8') break return { "image_b64": image_data, "model": model, "finish_reason": response.candidates[0].finish_reason } def save_image(self, image_b64: str, user_id: str) -> str: os.makedirs(settings.IMAGE_STORAGE_PATH, exist_ok=True) filename = f"{user_id}/{uuid.uuid4()}.png" filepath = os.path.join(settings.IMAGE_STORAGE_PATH, filename) os.makedirs(os.path.dirname(filepath), exist_ok=True) image_data = base64.b64decode(image_b64) with open(filepath, "wb") as f: f.write(image_data) return f"/uploads/{filename}" ``` - [ ] **Step 3: Commit** ```bash git add -A && git commit -m "feat: implement image generation service" ``` --- ### Task 8: Implement images API **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/schemas/image.py` - Modify: `/opt/imagecreator/imagecreator/backend/app/api/images.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_images_api.py` - [ ] **Step 1: Write failing test** ```python # tests/test_images_api.py import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.database import Base, get_db from app.main import app from app.models.user import User from app.core.security import hash_password, create_access_token engine = create_engine("sqlite:///:memory:") TestSessionLocal = sessionmaker(bind=engine) def override_get_db(): db = TestSessionLocal() try: yield db finally: db.close() app.dependency_overrides[get_db] = override_get_db client = TestClient(app) def get_auth_header(user_id): token = create_access_token({"sub": str(user_id)}) return {"Authorization": f"Bearer {token}"} def test_optimize_prompt_requires_auth(): response = client.post("/api/images/optimize", json={"prompt": "a cat"}) assert response.status_code == 403 # No auth header def test_generate_requires_auth(): response = client.post("/api/images/generate", json={ "prompt": "a cat", "mode": "text2image", "model": "gemini-2.5-flash-image" }) assert response.status_code == 403 ``` - [ ] **Step 2: Write schemas** ```python # app/schemas/image.py from pydantic import BaseModel from typing import Optional from enum import Enum class GenerationMode(str, Enum): TEXT2IMAGE = "text2image" IMAGE2IMAGE = "image2image" class PromptOptimizeRequest(BaseModel): prompt: str mode: GenerationMode = GenerationMode.TEXT2IMAGE original_description: Optional[str] = None class PromptOptimizeResponse(BaseModel): optimized_prompt: str class ImageGenerateRequest(BaseModel): prompt: str optimized_prompt: str mode: GenerationMode model: str = "gemini-2.5-flash-image" input_image_b64: Optional[str] = None # For I2I class ImageGenerateResponse(BaseModel): image_url: str model: str optimized_prompt: str ``` - [ ] **Step 3: Write images API** ```python # app/api/images.py from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.database import get_db from app.api.deps import get_current_user from app.models.user import User from app.schemas.image import ( PromptOptimizeRequest, PromptOptimizeResponse, ImageGenerateRequest, ImageGenerateResponse, ) from app.services.prompt import PromptOptimizationService from app.services.image import ImageGenerationService from app.services.auth import AuthService router = APIRouter() @router.post("/optimize", response_model=PromptOptimizeResponse) async def optimize_prompt( data: PromptOptimizeRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): service = PromptOptimizationService() try: if data.mode == "text2image": optimized = service.optimize_text2image(data.prompt) else: optimized = service.optimize_image2image(data.prompt, data.original_description or "") return PromptOptimizeResponse(optimized_prompt=optimized) except Exception as e: raise HTTPException(status_code=500, detail=f"Optimization failed: {str(e)}") @router.post("/generate", response_model=ImageGenerateResponse) async def generate_image( data: ImageGenerateRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): service = ImageGenerationService() try: if data.mode == "text2image": result = await service.generate_text2image(data.optimized_prompt, data.model) else: if not data.input_image_b64: raise HTTPException(status_code=400, detail="input_image_b64 required for image2image") result = await service.generate_image2image( data.optimized_prompt, data.input_image_b64, "image/png", data.model ) # Save image image_url = service.save_image(result["image_b64"], str(current_user.id)) return ImageGenerateResponse( image_url=image_url, model=result["model"], optimized_prompt=data.optimized_prompt ) except Exception as e: raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}") ``` - [ ] **Step 4: Run tests** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_images_api.py -v` Expected: PASS (auth tests) - [ ] **Step 5: Commit** ```bash git add -A && git commit -m "feat: implement images API (optimize, generate)" ``` --- ### Task 9: Implement history API **Files:** - Modify: `/opt/imagecreator/imagecreator/backend/app/api/history.py` - Create: `/opt/imagecreator/imagecreator/backend/tests/test_history_api.py` - [ ] **Step 1: Write failing test** ```python # tests/test_history_api.py import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.database import Base, get_db from app.main import app from app.models.user import User from app.models.history import GenerationHistory, GenerationMode from app.core.security import hash_password, create_access_token engine = create_engine("sqlite:///:memory:") TestSessionLocal = sessionmaker(bind=engine) def override_get_db(): db = TestSessionLocal() try: yield db finally: db.close() app.dependency_overrides[get_db] = override_get_db client = TestClient(app) def get_auth_header(user_id): token = create_access_token({"sub": str(user_id)}) return {"Authorization": f"Bearer {token}"} def test_get_history_empty(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="histtest", email="hist@example.com", hashed_password=hash_password("pass")) db.add(user) db.commit() db.refresh(user) db.close() response = client.get("/api/history", headers=get_auth_header(user.id)) assert response.status_code == 200 assert response.json() == [] def test_get_history_with_items(): Base.metadata.create_all(bind=engine) db = TestSessionLocal() user = User(username="histtest2", email="hist2@example.com", hashed_password=hash_password("pass")) db.add(user) db.commit() db.refresh(user) history = GenerationHistory( user_id=user.id, original_prompt="a cat", optimized_prompt="a fluffy orange cat", mode=GenerationMode.TEXT2IMAGE, model="gemini", image_url="/images/cat.png" ) db.add(history) db.commit() db.close() response = client.get("/api/history", headers=get_auth_header(user.id)) assert response.status_code == 200 data = response.json() assert len(data) == 1 assert data[0]["original_prompt"] == "a cat" ``` - [ ] **Step 2: Write history API** ```python # app/api/history.py from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List from uuid import UUID from app.database import get_db from app.api.deps import get_current_user from app.models.user import User from app.models.history import GenerationHistory from app.schemas.history import HistoryResponse router = APIRouter() @router.get("", response_model=List[HistoryResponse]) def get_history( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): items = db.query(GenerationHistory).filter( GenerationHistory.user_id == current_user.id ).order_by(GenerationHistory.created_at.desc()).all() return items @router.get("/{history_id}", response_model=HistoryResponse) def get_history_item( history_id: UUID, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): item = db.query(GenerationHistory).filter( GenerationHistory.id == history_id, GenerationHistory.user_id == current_user.id ).first() if not item: raise HTTPException(status_code=404, detail="History item not found") return item @router.delete("/{history_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_history_item( history_id: UUID, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): item = db.query(GenerationHistory).filter( GenerationHistory.id == history_id, GenerationHistory.user_id == current_user.id ).first() if not item: raise HTTPException(status_code=404, detail="History item not found") db.delete(item) db.commit() ``` - [ ] **Step 3: Write history schema** ```python # app/schemas/history.py from pydantic import BaseModel from typing import Optional from datetime import datetime from uuid import UUID class HistoryResponse(BaseModel): id: UUID original_prompt: str optimized_prompt: str mode: str model: str image_url: str input_image_url: Optional[str] = None created_at: datetime class Config: from_attributes = True ``` - [ ] **Step 4: Run tests** Run: `cd /opt/imagecreator/imagecreator/backend && pytest tests/test_history_api.py -v` Expected: PASS - [ ] **Step 5: Commit** ```bash git add -A && git commit -m "feat: implement history API" ``` --- ### Task 10: Backend Docker setup **Files:** - Create: `/opt/imagecreator/imagecreator/backend/Dockerfile` - Create: `/opt/imagecreator/imagecreator/docker-compose.yml` - [ ] **Step 1: Create backend Dockerfile** ```dockerfile # backend/Dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app/ ./app/ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* # Create uploads directory RUN mkdir -p /app/uploads EXPOSE 8000 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] ``` - [ ] **Step 2: Create docker-compose.yml** ```yaml # docker-compose.yml version: '3.8' services: backend: build: ./backend ports: - "8000:8000" environment: - DATABASE_URL=postgresql://user:pass@db:5432/imagecreator - SECRET_KEY=change-this-secret-key-in-production - GEMINI_API_KEY=${GEMINI_API_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY} volumes: - uploads:/app/uploads depends_on: - db restart: unless-stopped frontend: build: ./frontend ports: - "3000:80" depends_on: - backend db: image: postgres:15-alpine environment: - POSTGRES_USER=user - POSTGRES_PASSWORD=pass - POSTGRES_DB=imagecreator volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped volumes: postgres_data: uploads: ``` - [ ] **Step 3: Create .env.example** ```env # .env.example SECRET_KEY=your-secret-key-here GEMINI_API_KEY=your-gemini-api-key OPENAI_API_KEY=your-openai-api-key DATABASE_URL=postgresql://user:pass@localhost:5432/imagecreator ``` - [ ] **Step 4: Commit** ```bash git add -A && git commit -m "feat: add backend Dockerfile and docker-compose" ``` --- ## Phase 2: Frontend Development ### Task 11: Initialize Vue 3 frontend project **Files:** - Create: `/opt/imagecreator/imagecreator/frontend/package.json` - Create: `/opt/imagecreator/imagecreator/frontend/vite.config.ts` - Create: `/opt/imagecreator/imagecreator/frontend/index.html` - Create: `/opt/imagecreator/imagecreator/frontend/tailwind.config.js` - Create: `/opt/imagecreator/imagecreator/frontend/src/main.ts` - Create: `/opt/imagecreator/imagecreator/frontend/src/App.vue` - Create: `/opt/imagecreator/imagecreator/frontend/src/router/index.ts` - Create: `/opt/imagecreator/imagecreator/frontend/src/api/index.ts` - [ ] **Step 1: Create package.json** ```json { "name": "imagecreator-frontend", "version": "1.0.0", "private": true, "type": "module", "scripts": { "dev": "vite", "build": "vue-tsc && vite build", "preview": "vite preview" }, "dependencies": { "vue": "^3.4.0", "vue-router": "^4.2.0", "pinia": "^2.1.0", "axios": "^1.6.0", "naive-ui": "^2.38.0" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.0", "typescript": "^5.3.0", "vite": "^5.0.0", "vue-tsc": "^1.8.0", "tailwindcss": "^3.4.0", "autoprefixer": "^10.4.0", "postcss": "^8.4.0" } } ``` - [ ] **Step 2: Create vite.config.ts** ```typescript // vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true }, '/uploads': { target: 'http://localhost:8000', changeOrigin: true } } } }) ``` - [ ] **Step 3: Create tailwind.config.js** ```javascript // tailwind.config.js export default { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}", ], theme: { extend: {}, }, plugins: [], } ``` - [ ] **Step 4: Create main.ts** ```typescript // src/main.ts import { createApp } from 'vue' import { createPinia } from 'pinia' import naive from 'naive-ui' import App from './App.vue' import router from './router' import './style.css' const app = createApp(App) app.use(createPinia()) app.use(router) app.use(naive) app.mount('#app') ``` - [ ] **Step 5: Create api/index.ts** ```typescript // src/api/index.ts import axios from 'axios' const api = axios.create({ baseURL: '/api', timeout: 30000, }) api.interceptors.request.use((config) => { const token = localStorage.getItem('access_token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) api.interceptors.response.use( (response) => response, async (error) => { if (error.response?.status === 401) { localStorage.removeItem('access_token') window.location.href = '/login' } return Promise.reject(error) } ) export default api ``` - [ ] **Step 6: Create App.vue** ```vue ``` - [ ] **Step 7: Create router** ```typescript // src/router/index.ts import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/login', name: 'Login', component: () => import('../views/Login.vue') }, { path: '/register', name: 'Register', component: () => import('../views/Register.vue') }, { path: '/', name: 'Home', component: () => import('../views/Home.vue'), meta: { requiresAuth: true } }, { path: '/history', name: 'History', component: () => import('../views/History.vue'), meta: { requiresAuth: true } }, ] const router = createRouter({ history: createWebHistory(), routes, }) router.beforeEach((to, from, next) => { const token = localStorage.getItem('access_token') if (to.meta.requiresAuth && !token) { next('/login') } else if ((to.path === '/login' || to.path === '/register') && token) { next('/') } else { next() } }) export default router ``` - [ ] **Step 8: Create style.css** ```css /* src/style.css */ @tailwind base; @tailwind components; @tailwind utilities; body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } ``` - [ ] **Step 9: Commit** ```bash git add -A && git commit -m "feat: initialize Vue 3 frontend project" ``` --- ### Task 12: Implement auth stores and API **Files:** - Create: `/opt/imagecreator/imagecreator/frontend/src/stores/auth.ts` - Create: `/opt/imagecreator/imagecreator/frontend/src/api/auth.ts` - [ ] **Step 1: Write auth API** ```typescript // src/api/auth.ts import api from './index' export interface LoginRequest { username: string password: string } export interface RegisterRequest { username: string email: string password: string } export interface AuthResponse { access_token: string refresh_token: string token_type: string } export const authApi = { login(data: LoginRequest) { return api.post('/auth/login', data) }, register(data: RegisterRequest) { return api.post('/auth/register', data) }, } ``` - [ ] **Step 2: Write auth store** ```typescript // src/stores/auth.ts import { defineStore } from 'pinia' import { ref } from 'vue' import { authApi } from '../api/auth' export const useAuthStore = defineStore('auth', () => { const token = ref(localStorage.getItem('access_token')) const user = ref<{ id: string; username: string; email: string } | null>(null) const login = async (username: string, password: string) => { const response = await authApi.login({ username, password }) token.value = response.data.access_token localStorage.setItem('access_token', response.data.access_token) } const register = async (username: string, email: string, password: string) => { await authApi.register({ username, email, password }) } const logout = () => { token.value = null user.value = null localStorage.removeItem('access_token') } return { token, user, login, register, logout } }) ``` - [ ] **Step 3: Commit** ```bash git add -A && git commit -m "feat: implement frontend auth store and API" ``` --- ### Task 13: Implement Login and Register views **Files:** - Create: `/opt/imagecreator/imagecreator/frontend/src/views/Login.vue` - Create: `/opt/imagecreator/imagecreator/frontend/src/views/Register.vue` - [ ] **Step 1: Write Login.vue** ```vue 登录 还没有账号?注册 ``` - [ ] **Step 2: Write Register.vue** ```vue 注册 已有账号?登录 ``` - [ ] **Step 3: Commit** ```bash git add -A && git commit -m "feat: implement Login and Register views" ``` --- ### Task 14: Implement Home view with image generation **Files:** - Create: `/opt/imagecreator/imagecreator/frontend/src/views/Home.vue` - Create: `/opt/imagecreator/imagecreator/frontend/src/api/images.ts` - Create: `/opt/imagecreator/imagecreator/frontend/src/components/ImageGenerator.vue` - Create: `/opt/imagecreator/imagecreator/frontend/src/components/PromptOptimizer.vue` - [ ] **Step 1: Write images API** ```typescript // src/api/images.ts import api from './index' export interface PromptOptimizeRequest { prompt: string mode: 'text2image' | 'image2image' original_description?: string } export interface ImageGenerateRequest { prompt: string optimized_prompt: string mode: 'text2image' | 'image2image' model: string input_image_b64?: string } export const imagesApi = { optimizePrompt(data: PromptOptimizeRequest) { return api.post<{ optimized_prompt: string }>('/images/optimize', data) }, generateImage(data: ImageGenerateRequest) { return api.post<{ image_url: string; model: string }>('/images/generate', data) }, } ``` - [ ] **Step 2: Write Home.vue** ```vue ImageCreator 生成 历史 退出 优化提示词 优化后 {{ optimizedPrompt }} 选择图片 下载图片 生成的图片将显示在这里 生成图片 ``` - [ ] **Step 3: Commit** ```bash git add -A && git commit -m "feat: implement Home view with image generation" ``` --- ### Task 15: Implement History view **Files:** - Create: `/opt/imagecreator/imagecreator/frontend/src/views/History.vue` - Create: `/opt/imagecreator/imagecreator/frontend/src/api/history.ts` - [ ] **Step 1: Write history API** ```typescript // src/api/history.ts import api from './index' export interface HistoryItem { id: string original_prompt: string optimized_prompt: string mode: string model: string image_url: string input_image_url: string | null created_at: string } export const historyApi = { getHistory() { return api.get('/history') }, deleteHistory(id: string) { return api.delete(`/history/${id}`) }, } ``` - [ ] **Step 2: Write History.vue** ```vue ImageCreator 生成 历史 退出 生成历史 暂无历史记录 {{ item.mode === 'text2image' ? '文生图' : '图生图' }} 删除 原始提示词: {{ item.original_prompt }} 优化后: {{ item.optimized_prompt }} {{ formatDate(item.created_at) }} | {{ item.model }} ``` - [ ] **Step 3: Commit** ```bash git add -A && git commit -m "feat: implement History view" ``` --- ### Task 16: Frontend Dockerfile and nginx config **Files:** - Create: `/opt/imagecreator/imagecreator/frontend/Dockerfile` - Create: `/opt/imagecreator/imagecreator/frontend/nginx.conf` - [ ] **Step 1: Create Dockerfile** ```dockerfile # frontend/Dockerfile FROM node:20-alpine as builder WORKDIR /app COPY package.json ./ RUN npm install COPY . . RUN npm run build FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] ``` - [ ] **Step 2: Create nginx.conf** ```nginx # frontend/nginx.conf server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend:8000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /uploads/ { proxy_pass http://backend:8000/uploads/; proxy_set_header Host $host; } } ``` - [ ] **Step 3: Commit** ```bash git add -A && git commit -m "feat: add frontend Dockerfile and nginx config" ``` --- ## Phase 3: Final Integration ### Task 17: Create README **Files:** - Create: `/opt/imagecreator/imagecreator/README.md` - [ ] **Step 1: Write README** ```markdown # ImageCreator A full-stack image generation website with user authentication, prompt optimization, and T2I/I2I generation. ## Features - User authentication (register/login) - Prompt optimization using LLM - Text-to-Image generation - Image-to-Image generation - Generation history ## Tech Stack - **Backend**: FastAPI, SQLAlchemy, PostgreSQL - **Frontend**: Vue 3, Naive UI, TailwindCSS - **Deployment**: Docker Compose ## Quick Start ### Prerequisites - Docker & Docker Compose - Gemini API key (for image generation) ### Environment Setup 1. Copy `.env.example` to `.env` and configure: ```bash cp .env.example .env ``` 2. Edit `.env` and add your API keys: ``` GEMINI_API_KEY=your_gemini_api_key SECRET_KEY=your-secret-key ``` ### Running with Docker Compose ```bash docker-compose up -d ``` - Frontend: http://localhost:3000 - Backend API: http://localhost:8000 - API Docs: http://localhost:8000/docs ## Development ### Backend ```bash cd backend pip install -r requirements.txt uvicorn app.main:app --reload --port 8000 ``` ### Frontend ```bash cd frontend npm install npm run dev ``` ## Project Structure ``` imagecreator/ ├── backend/ # FastAPI backend │ ├── app/ │ │ ├── api/ # API routes │ │ ├── core/ # Security, config │ │ ├── models/ # Database models │ │ ├── schemas/ # Pydantic schemas │ │ └── services/ # Business logic │ └── tests/ ├── frontend/ # Vue 3 frontend │ └── src/ │ ├── api/ # API clients │ ├── views/ # Page components │ ├── stores/ # Pinia stores │ └── router/ # Vue Router └── docker-compose.yml ``` ## License AGPL-3.0 ``` - [ ] **Step 2: Commit** ```bash git add -A && git commit -m "docs: add README" ``` --- ## Summary **Total Tasks: 17** ### Phase 1: Backend (Tasks 1-10) - Project structure, config, database - User & History models - Security (JWT, password) - Auth API & service - Users API - LLM & Prompt optimization service - Image generation service - Images API - History API - Backend Docker ### Phase 2: Frontend (Tasks 11-16) - Vue 3 project init - Auth stores and API - Login/Register views - Home view with generation - History view - Frontend Docker ### Phase 3: Final (Task 17) - README documentation
{{ optimizedPrompt }}
原始提示词:
{{ item.original_prompt }}
优化后:
{{ item.optimized_prompt }}