first commit
This commit is contained in:
307
README.md
Normal file
307
README.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Comment API iskeleti
|
||||
|
||||
Node.js + Express + Socket.io + Supabase + Redis tabanlı login/register API iskeleti. Güvenlik odaklı, host allowlist, rate limit, admin rol kilidi ve Redis oturum yönetimi içerir. Docker-compose ile Redis + API birlikte ayağa kalkar.
|
||||
|
||||
## Dizın yapısı
|
||||
|
||||
```
|
||||
src/
|
||||
app.js // Express app
|
||||
server.js // HTTP + Socket.io bootstrap
|
||||
config/
|
||||
env.js // .env yükleme ve parse
|
||||
supabase.js // Supabase client
|
||||
redis.js // Redis client
|
||||
middleware/
|
||||
authRequired.js
|
||||
adminRequired.js
|
||||
rateLimit.js
|
||||
hostOnly.js
|
||||
errorHandler.js
|
||||
routes/
|
||||
auth.routes.js
|
||||
health.routes.js
|
||||
controllers/
|
||||
auth.controller.js
|
||||
services/
|
||||
auth.service.js
|
||||
session.service.js
|
||||
adminLock.service.js
|
||||
utils/
|
||||
crypto.js // bcrypt + JWT yardımcıları
|
||||
response.js // standart JSON çıktı
|
||||
```
|
||||
|
||||
## Çalıştırma
|
||||
|
||||
1. Bağımlılıklar:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
> Not: `metascraper` bağımlılığı özel git repo’dan gelir (`git+https://gitea.wisecolt-panda.net/wisecolt/metascraper.git`). Erişim için uygun token/SSH anahtarı olan bir ortamda `npm install` çalıştırın.
|
||||
> Docker build için base imaja `git` eklenmiştir; özel repo erişimi için gerekirse `GIT_SSH_COMMAND` veya `GITHUB_TOKEN` benzeri auth ekleyin.
|
||||
2. Ortam değişkenleri: `.env.example` dosyasını `.env` olarak kopyalayıp doldurun.
|
||||
3. Geliştirme:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
4. Üretim:
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
Yerelde (dev, hot-reload) Redis ile birlikte çalıştırmak için:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml up --build
|
||||
# durdurmak için
|
||||
docker compose -f docker-compose.dev.yml down
|
||||
```
|
||||
|
||||
- API: `http://localhost:${PORT}`
|
||||
- Redis: `redis://localhost:6379` (container içinde `redis://redis:6379`)
|
||||
|
||||
### Prod modunda Docker
|
||||
|
||||
1. `.env` içinde `NODE_ENV=production` ve mümkünse `COOKIE_SECURE=true`, `TRUST_PROXY=loopback` (veya proxy sayısı) olarak ayarlayın.
|
||||
2. Çalıştırın:
|
||||
```bash
|
||||
docker compose up --build -d
|
||||
```
|
||||
3. Durdurun:
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
## .env örneği
|
||||
|
||||
```
|
||||
NODE_ENV=development
|
||||
PORT=3000
|
||||
TRUST_PROXY=loopback
|
||||
|
||||
SUPABASE_URL=https://YOUR-SUPABASE-PROJECT.supabase.co
|
||||
SUPABASE_SERVICE_ROLE_KEY=YOUR_SUPABASE_SERVICE_ROLE_KEY
|
||||
|
||||
REDIS_URL=redis://redis:6379
|
||||
|
||||
JWT_SECRET=supersecretjwt
|
||||
JWT_EXPIRES_IN=7d
|
||||
COOKIE_NAME=sid
|
||||
COOKIE_SECURE=false
|
||||
|
||||
ADMIN_EMAIL=admin@example.com
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_NAME=Admin User
|
||||
ADMIN_ROLE_LOCK=true
|
||||
|
||||
HOST_ONLY_ALLOWLIST=127.0.0.1,::1
|
||||
|
||||
RATE_LIMIT_WINDOW_MS=900000
|
||||
RATE_LIMIT_MAX=100
|
||||
|
||||
LOGIN_RATE_LIMIT_WINDOW_MS=60000
|
||||
LOGIN_RATE_LIMIT_MAX=5
|
||||
```
|
||||
|
||||
## Endpointler
|
||||
|
||||
- `GET /health` — `{ ok: true, time }`
|
||||
- `POST /auth/register` — body `{ email, name, username, password }`
|
||||
- `POST /auth/login` — body `{ emailOrUsername, password }`
|
||||
- `POST /auth/logout` — cookie tabanlı JWT siler
|
||||
- `GET /auth/me` — aktif kullanıcıyı döndürür
|
||||
- `POST /auth/refresh` — yeni JWT üretir (iskelet)
|
||||
- `POST /meta/scrape` — body `{ url }`, destekli domainler: `netflix.com`, `primevideo.com` (yalnızca `role: user`)
|
||||
- Başarılı sorgu Supabase `media_data` tablosuna kaydedilir: `media_id, media_name, year, type(movie/tv), thumbnail_url, info, genre, media_provider (netflix/primevideo)`
|
||||
|
||||
### Postman örnekleri
|
||||
|
||||
**Register**
|
||||
```
|
||||
POST {{baseUrl}}/auth/register
|
||||
Content-Type: application/json
|
||||
Body:
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"name": "Test User",
|
||||
"username": "testuser",
|
||||
"password": "password123"
|
||||
}
|
||||
```
|
||||
**200**:
|
||||
```
|
||||
{
|
||||
"user": {
|
||||
"id": "...",
|
||||
"email": "user@example.com",
|
||||
"name": "Test User",
|
||||
"username": "testuser",
|
||||
"roleEffective": "user"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Login**
|
||||
```
|
||||
POST {{baseUrl}}/auth/login
|
||||
Content-Type: application/json
|
||||
Body:
|
||||
{
|
||||
"emailOrUsername": "testuser",
|
||||
"password": "password123"
|
||||
}
|
||||
```
|
||||
**200**:
|
||||
```
|
||||
{
|
||||
"user": {
|
||||
"id": "...",
|
||||
"email": "user@example.com",
|
||||
"name": "Test User",
|
||||
"username": "testuser",
|
||||
"roleEffective": "user"
|
||||
}
|
||||
}
|
||||
```
|
||||
JWT otomatik olarak `Cookie: sid=<token>` olarak set edilir.
|
||||
|
||||
**Me**
|
||||
```
|
||||
GET {{baseUrl}}/auth/me
|
||||
Cookie: sid=<token>
|
||||
```
|
||||
**200**:
|
||||
```
|
||||
{
|
||||
"user": {
|
||||
"id": "...",
|
||||
"email": "...",
|
||||
"username": "...",
|
||||
"roleEffective": "user"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Logout**
|
||||
```
|
||||
POST {{baseUrl}}/auth/logout
|
||||
Cookie: sid=<token>
|
||||
```
|
||||
**200**:
|
||||
```
|
||||
{ "message": "Çıkış yapıldı" }
|
||||
```
|
||||
|
||||
**Meta scrape (Netflix/Prime)**
|
||||
```
|
||||
POST {{baseUrl}}/meta/scrape
|
||||
Content-Type: application/json
|
||||
Cookie: sid=<token> # roleEffective=user
|
||||
Body:
|
||||
{ "url": "https://www.netflix.com/tr/title/81507921" }
|
||||
```
|
||||
Prime örneği:
|
||||
```
|
||||
{ "url": "https://www.primevideo.com/-/tr/detail/0NHIN3TGAI9L7VZ45RS52RHUPL/ref=share_ios_movie" }
|
||||
```
|
||||
**200** (örnek):
|
||||
```
|
||||
{
|
||||
"provider": "netflix",
|
||||
"type": "movie",
|
||||
"media": {
|
||||
"url": "https://www.netflix.com/title/82123114",
|
||||
"id": "82123114",
|
||||
"name": "ONE SHOT with Ed Sheeran",
|
||||
"year": "2025",
|
||||
"seasons": null,
|
||||
"thumbnail": "...",
|
||||
"info": "...",
|
||||
"genre": "..."
|
||||
},
|
||||
"saved": {
|
||||
"media_id": "82123114",
|
||||
"media_name": "ONE SHOT with Ed Sheeran",
|
||||
"year": "2025",
|
||||
"type": "movie",
|
||||
"thumbnail_url": "...",
|
||||
"info": "...",
|
||||
"genre": "...",
|
||||
"media_provider": "netflix",
|
||||
"created_at": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hata çıktıları tek tip:
|
||||
```
|
||||
{
|
||||
"error": { "code": "ERROR_CODE", "message": "Açıklama", "details": [...] }
|
||||
}
|
||||
```
|
||||
|
||||
## Supabase şeması (SQL)
|
||||
|
||||
```sql
|
||||
create extension if not exists "pgcrypto";
|
||||
|
||||
create table if not exists public.users (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
email text unique not null,
|
||||
name text not null,
|
||||
username text unique not null,
|
||||
password_hash text not null,
|
||||
role text not null default 'user' check (role in ('user','admin')),
|
||||
created_at timestamp with time zone default now()
|
||||
);
|
||||
|
||||
create table if not exists public.media_data (
|
||||
media_id varchar,
|
||||
media_name text,
|
||||
year numeric,
|
||||
type text,
|
||||
thumbnail_url varchar,
|
||||
info text,
|
||||
genre text,
|
||||
media_provider text,
|
||||
created_at timestamp with time zone default now()
|
||||
);
|
||||
```
|
||||
|
||||
> Admin rol kilidi: Sadece `.env` içindeki `ADMIN_EMAIL` ve `ADMIN_USERNAME` ile birebir eşleşen kullanıcı API tarafından `admin` kabul edilir. Supabase üzerinde rol elle değiştirilse bile etkisizdir.
|
||||
|
||||
## Güvenlik notları
|
||||
|
||||
- `hostOnly` middleware ve CORS sadece allowlist’teki host/ip erişimine izin verir.
|
||||
- Global rate limit + login özel rate limit aktiftir.
|
||||
- JWT httpOnly cookie olarak set edilir; Redis içinde `session:{jti}` anahtarıyla oturum metadata tutulur.
|
||||
|
||||
## Socket.io
|
||||
|
||||
- Handshake sırasında cookie’deki JWT doğrulanır, Redis oturum kontrol edilir.
|
||||
- Başarılı bağlantıda `welcome` eventi döner.
|
||||
- Örnek ping/pong:
|
||||
- Client: `socket.emit('ping')`
|
||||
- Server cevabı: `pong` içinde zaman damgası.
|
||||
|
||||
## Docker notları
|
||||
|
||||
- `docker-compose.yml` içinde `api` servisi `redis` servisine bağımlıdır.
|
||||
- Geliştirme için kod klasörü container içine mount edilerek canlı yenileme (`npm run dev`) sağlanır.
|
||||
- Sağlık kontrolü: container içinde `GET /health` çağrısı ile yapılır.
|
||||
|
||||
## Yapılandırılabilir noktalar
|
||||
|
||||
- Rate limit penceresi ve max değerleri `.env` ile ayarlanır.
|
||||
- `HOST_ONLY_ALLOWLIST` ile izin verilen host/ip listesi yönetilir.
|
||||
- `ADMIN_ROLE_LOCK` kapatılmadıkça tek admin `.env`’deki kullanıcıdır.
|
||||
|
||||
## TODO / Genişletme fikirleri
|
||||
|
||||
- Refresh token için ayrı bir anahtar ve whitelist mekanizması.
|
||||
- Audit logları (morgan loglarını dosyaya yazma).
|
||||
- Supabase RPC veya policy’lerle ek güvenlik.
|
||||
- Testler (Jest/Supertest) ve CI pipeline’ı.
|
||||
Reference in New Issue
Block a user