feat: implement Instagram clone SocialPhoto API
- FastAPI backend with SQLite database - JWT authentication (register, login) - User profiles with follow/unfollow - Posts with image upload and likes/dislikes - Comments with likes - Global and personalized feed - Comprehensive pytest test suite (37 tests) TASK-ID: 758f4029-702
This commit is contained in:
337
SPEC.md
Normal file
337
SPEC.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# SPEC.md — Instagram Clone (SocialPhoto)
|
||||
|
||||
> Sistema de red social para compartir imágenes con likes, comentarios y seguimiento de usuarios
|
||||
|
||||
## 1. Concepto & Visión
|
||||
|
||||
**SocialPhoto** es un clon minimalista de Instagram que prioriza la simplicidad y velocidad. Permite a usuarios registrarse, subir imágenes, interactuar con publicaciones (likes/no me gusta), comentar y seguir a otros usuarios. El enfoque es en ser rápido, portable (todo en un archivo SQLite) y fácil de desplegar.
|
||||
|
||||
**Principios:**
|
||||
- Simple de instalar y operar
|
||||
- Base de datos portable (SQLite)
|
||||
- API RESTful clara y documentada
|
||||
- Ideal para testing de agentes de IA
|
||||
|
||||
---
|
||||
|
||||
## 2. Stack Tecnológico
|
||||
|
||||
| Componente | Tecnología |
|
||||
|------------|------------|
|
||||
| **Backend** | Python 3.11+ con FastAPI |
|
||||
| **Base de datos** | SQLite3 |
|
||||
| **Auth** | JWT (python-jose) |
|
||||
| **Upload imágenes** | Almacenamiento local en `/uploads` |
|
||||
| **Password hashing** | bcrypt |
|
||||
| **Testing** | pytest + pytest-asyncio |
|
||||
| **CLI (testing)** | curl o httpie |
|
||||
|
||||
---
|
||||
|
||||
## 3. Modelo de Datos (SQLite)
|
||||
|
||||
### Tabla: `users`
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
avatar_url TEXT DEFAULT '/static/default-avatar.png',
|
||||
bio TEXT DEFAULT '',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### Tabla: `posts`
|
||||
|
||||
```sql
|
||||
CREATE TABLE posts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
image_path TEXT NOT NULL,
|
||||
caption TEXT DEFAULT '',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### Tabla: `comments`
|
||||
|
||||
```sql
|
||||
CREATE TABLE comments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
post_id INTEGER NOT NULL REFERENCES posts(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### Tabla: `likes`
|
||||
|
||||
```sql
|
||||
CREATE TABLE likes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
post_id INTEGER NOT NULL REFERENCES posts(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(post_id, user_id)
|
||||
);
|
||||
```
|
||||
|
||||
### Tabla: `dislikes`
|
||||
|
||||
```sql
|
||||
CREATE TABLE dislikes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
post_id INTEGER NOT NULL REFERENCES posts(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(post_id, user_id)
|
||||
);
|
||||
```
|
||||
|
||||
### Tabla: `follows`
|
||||
|
||||
```sql
|
||||
CREATE TABLE follows (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
follower_id INTEGER NOT NULL REFERENCES users(id),
|
||||
following_id INTEGER NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(follower_id, following_id)
|
||||
);
|
||||
```
|
||||
|
||||
### Tabla: `comment_likes`
|
||||
|
||||
```sql
|
||||
CREATE TABLE comment_likes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
comment_id INTEGER NOT NULL REFERENCES comments(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(comment_id, user_id)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API Endpoints
|
||||
|
||||
### Autenticación
|
||||
|
||||
| Método | Endpoint | Descripción |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/auth/register` | Registrar usuario |
|
||||
| POST | `/auth/login` | Login, retorna JWT |
|
||||
|
||||
**Register Request:**
|
||||
```json
|
||||
{
|
||||
"username": "daniel",
|
||||
"email": "daniel@example.com",
|
||||
"password": "mipass123"
|
||||
}
|
||||
```
|
||||
|
||||
**Login Response:**
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJ...",
|
||||
"token_type": "bearer"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Usuarios
|
||||
|
||||
| Método | Endpoint | Descripción |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/users/{id}` | Obtener perfil de usuario |
|
||||
| GET | `/users/{id}/posts` | Posts de un usuario |
|
||||
| GET | `/users/{id}/stats` | Stats (posts, followers, following) |
|
||||
| POST | `/users/{id}/follow` | Seguir usuario |
|
||||
| DELETE | `/users/{id}/follow` | Dejar de seguir |
|
||||
|
||||
---
|
||||
|
||||
### Posts
|
||||
|
||||
| Método | Endpoint | Descripción |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/posts` | Crear post (con imagen) |
|
||||
| GET | `/posts` | Feed global |
|
||||
| GET | `/posts/{id}` | Detalle de post |
|
||||
| DELETE | `/posts/{id}` | Eliminar post |
|
||||
| POST | `/posts/{id}/like` | Dar like |
|
||||
| DELETE | `/posts/{id}/like` | Quitar like |
|
||||
| POST | `/posts/{id}/dislike` | Dar no me gusta |
|
||||
| DELETE | `/posts/{id}/dislike` | Quitar no me gusta |
|
||||
|
||||
**Crear Post:**
|
||||
```
|
||||
POST /posts
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
caption: "Mi primera foto"
|
||||
image: <archivo>
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"user_id": 1,
|
||||
"username": "daniel",
|
||||
"image_url": "/uploads/abc123.jpg",
|
||||
"caption": "Mi primera foto",
|
||||
"likes_count": 0,
|
||||
"dislikes_count": 0,
|
||||
"comments_count": 0,
|
||||
"created_at": "2026-04-15T22:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Comentarios
|
||||
|
||||
| Método | Endpoint | Descripción |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/posts/{post_id}/comments` | Listar comentarios |
|
||||
| POST | `/posts/{post_id}/comments` | Agregar comentario |
|
||||
| DELETE | `/comments/{id}` | Eliminar comentario |
|
||||
| POST | `/comments/{id}/like` | Dar like a comentario |
|
||||
|
||||
---
|
||||
|
||||
### Feed
|
||||
|
||||
| Método | Endpoint | Descripción |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/feed` | Feed de usuarios que sigues |
|
||||
| GET | `/feed/global` | Feed global (todos) |
|
||||
|
||||
**Query params:** `?limit=20&offset=0`
|
||||
|
||||
---
|
||||
|
||||
## 5. Detalles de Implementación
|
||||
|
||||
### Upload de Imágenes
|
||||
- Directorio: `./uploads/`
|
||||
- Nombres: `{uuid}_{original_filename}`
|
||||
- Formatos: jpg, jpeg, png, gif, webp
|
||||
- Tamaño máximo: 10MB
|
||||
|
||||
### Auth JWT
|
||||
- Algorithm: HS256
|
||||
- Expiración: 24 horas
|
||||
- Header: `Authorization: Bearer <token>`
|
||||
|
||||
### Likes/Dislikes
|
||||
- Un usuario solo puede dar UN like O un dislike por post (no ambos)
|
||||
- Dar dislike si ya hay like lo reemplaza
|
||||
- Dar like si ya hay dislike lo reemplaza
|
||||
|
||||
### Follow
|
||||
- No puedes seguirte a ti mismo
|
||||
- Mensaje de error si intentas seguir alguien que ya sigues
|
||||
|
||||
### Timestamps
|
||||
- Todos en UTC
|
||||
- Formato ISO 8601 en responses
|
||||
|
||||
---
|
||||
|
||||
## 6. Estructura del Proyecto
|
||||
|
||||
```
|
||||
socialphoto/
|
||||
├── app/
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # FastAPI app
|
||||
│ ├── database.py # SQLite connection + setup
|
||||
│ ├── models.py # Pydantic models
|
||||
│ ├── auth.py # JWT auth helpers
|
||||
│ ├── routes/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── auth.py
|
||||
│ │ ├── users.py
|
||||
│ │ ├── posts.py
|
||||
│ │ ├── comments.py
|
||||
│ │ └── feed.py
|
||||
│ └── services/
|
||||
│ └── __init__.py
|
||||
├── uploads/ # Imágenes
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── conftest.py
|
||||
│ ├── test_auth.py
|
||||
│ ├── test_posts.py
|
||||
│ └── test_social.py
|
||||
├── requirements.txt
|
||||
├── SPEC.md
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Criterios de Aceptación
|
||||
|
||||
### Auth
|
||||
- [x] Usuario puede registrarse con username, email, password
|
||||
- [x] Usuario puede hacer login y recibe JWT
|
||||
- [x] Rutas protegidas requieren token válido
|
||||
- [x] No permite registro con username/email duplicado
|
||||
|
||||
### Posts
|
||||
- [x] Usuario puede crear post con imagen y caption
|
||||
- [x] Feed global muestra todos los posts ordenados por fecha
|
||||
- [x] Feed personalizado solo muestra posts de usuarios seguidos
|
||||
- [x] Usuario puede eliminar SUS propios posts
|
||||
|
||||
### Likes / Dislikes
|
||||
- [x] Usuario puede dar like a un post
|
||||
- [x] Usuario puede dar dislike a un post
|
||||
- [x] Like y dislike son mutuamente excluyentes
|
||||
- [x] Contador de likes/dislikes se actualiza en tiempo real
|
||||
|
||||
### Comentarios
|
||||
- [x] Usuario puede comentar en un post
|
||||
- [x] Usuario puede eliminar SUS comentarios
|
||||
- [x] Lista de comentarios incluye username y timestamp
|
||||
|
||||
### Follow
|
||||
- [x] Usuario puede seguir a otro usuario
|
||||
- [x] Usuario puede dejar de seguir
|
||||
- [x] No puede followearse a sí mismo
|
||||
- [x] Stats muestran followers y following count
|
||||
|
||||
---
|
||||
|
||||
## 8. Roadmap de Tasks (para Task Manager)
|
||||
|
||||
1. **Architecture**: Crear SPEC.md y estructura del proyecto ✓
|
||||
2. **Backlog**:
|
||||
- [x] Setup proyecto FastAPI con SQLite
|
||||
- [x] Implementar auth (register, login, JWT)
|
||||
- [x] CRUD posts con upload de imágenes
|
||||
- [x] Sistema de likes/dislikes
|
||||
- [x] Sistema de comentarios
|
||||
- [x] Sistema de follow/unfollow
|
||||
- [x] Feed global y personalizado
|
||||
- [x] Tests unitarios
|
||||
- [ ] README y documentación
|
||||
|
||||
---
|
||||
|
||||
## 9. Notas
|
||||
|
||||
- Usar SQLite para simplicidad y portabilidad
|
||||
- No requiere Docker ni servicios externos
|
||||
- Ideal para testing de SDMAS Orchestrator
|
||||
- En producción real se migraría a PostgreSQL
|
||||
Reference in New Issue
Block a user