diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index c5f7f79..52c41a0 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -31,7 +31,6 @@ jobs: quality-checks: name: Code Quality & Security runs-on: ubuntu-latest - if: github.event_name == 'push' || github.event_name == 'pull_request' steps: - name: Checkout repository uses: actions/checkout@v4 @@ -74,7 +73,6 @@ jobs: name: Build & Test runs-on: ubuntu-latest needs: quality-checks - if: github.event_name == 'push' || github.event_name == 'pull_request' steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/README.md b/README.md index 813a1c4..f2ea2d0 100644 --- a/README.md +++ b/README.md @@ -215,14 +215,7 @@ docker-compose up -d 1. Fork or clone the repository to your GitHub account 2. Sign up at [Vercel](https://vercel.com) 3. Create a new project and import your repository -4. Configure environment variables in Vercel Dashboard: - - `UPSTASH_REDIS_REST_URL` (Required - Get from [Upstash](https://upstash.com)) - - `UPSTASH_REDIS_REST_TOKEN` (Required) - - `ORIGIN=*` (or your frontend domain) - - `RATE_LIMIT_ENABLED=true` - - `RATE_LIMIT_WINDOW_MS=60000` - - `RATE_LIMIT_LIMIT=100` -5. Click "Deploy" +4. Click "Deploy" **Why Vercel?** - ![Supported](https://img.shields.io/badge/Supported-brightgreen?style=flat-square) Serverless architecture with automatic scaling @@ -230,18 +223,7 @@ docker-compose up -d - ![Supported](https://img.shields.io/badge/Supported-brightgreen?style=flat-square) Free tier with generous limits - ![Supported](https://img.shields.io/badge/Supported-brightgreen?style=flat-square) Automatic HTTPS and custom domains - ![Supported](https://img.shields.io/badge/Supported-brightgreen?style=flat-square) Git-based deployments (auto-deploy on push) -- ![Supported](https://img.shields.io/badge/Supported-brightgreen?style=flat-square) Built-in Redis support via Upstash -**Environment Variables:** - -| Key | Value | Required | -|-----|-------|----------| -| `UPSTASH_REDIS_REST_URL` | Your Upstash Redis URL | Yes | -| `UPSTASH_REDIS_REST_TOKEN` | Your Upstash Redis Token | Yes | -| `ORIGIN` | `*` or your domain | No | -| `RATE_LIMIT_ENABLED` | `true` | No | -| `RATE_LIMIT_WINDOW_MS` | `60000` | No | -| `RATE_LIMIT_LIMIT` | `100` | No | --- diff --git a/api/index.ts b/api/index.ts index ba84ad5..3210677 100644 --- a/api/index.ts +++ b/api/index.ts @@ -1,46 +1,62 @@ -import app from '../src/app'; +import { Hono } from 'hono'; +import { handle } from 'hono/vercel'; +import { cors } from 'hono/cors'; +import { logger } from 'hono/logger'; +import hiAnimeRoutes from '../src/routes/routes'; +import config from '../src/config/config'; +import { AppError } from '../src/utils/errors'; +import { fail } from '../src/utils/response'; -type VercelResponse = { - status: (code: number) => VercelResponse; - setHeader: (name: string, value: string) => VercelResponse; - send: (body: string | object | Buffer) => VercelResponse; - json: (body: object) => VercelResponse; -}; +const app = new Hono(); -export default async function handler( - req: { - headers: Record; - method: string; - url: string; - body: unknown; - }, - res: VercelResponse -) { - try { - const protocol = req.headers['x-forwarded-proto'] || 'https'; - const host = req.headers['x-forwarded-host'] || req.headers.host; - const url = `${protocol}://${host}${req.url}`; - const webRequest = new Request(url, { - method: req.method, - headers: new Headers(req.headers as Record), - body: req.method !== 'GET' && req.method !== 'HEAD' ? JSON.stringify(req.body) : undefined, - }); +// CORS Configuration +const origins = config.origin.includes(',') + ? config.origin.split(',').map(o => o.trim()) + : config.origin === '*' + ? '*' + : [config.origin]; - const webResponse = await app.fetch(webRequest); - res.status(webResponse.status); - webResponse.headers.forEach((value: string, key: string) => { - res.setHeader(key, value); - }); +app.use( + '*', + cors({ + origin: origins, + allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], + exposeHeaders: ['Content-Length', 'X-Request-Id'], + maxAge: 600, + credentials: true, + }) +); - const body = await webResponse.text(); - res.send(body); - } catch (error: unknown) { - const err = error as Error; - console.error('Vercel handler error:', err); - res.status(500).json({ - success: false, - error: 'Internal Server Error', - message: err.message, - }); - } +// Logging +if (!config.isProduction || config.enableLogging) { + app.use('/api/v2/*', logger()); } + +// Health Check +app.get('/ping', (c) => { + return c.json({ + status: 'ok', + timestamp: new Date().toISOString(), + environment: 'vercel', + }); +}); + +// Routes +app.route('/api/v2', hiAnimeRoutes); + +// Error Handling +app.onError((err, c) => { + if (err instanceof AppError) { + return fail(c, err.message, err.statusCode, err.details); + } + + console.error('Vercel Unexpected Error:', err.message); + return fail(c, 'Internal server error', 500); +}); + +app.notFound((c) => { + return fail(c, 'Route not found', 404); +}); + +export default handle(app); diff --git a/package.json b/package.json index 65d023f..15e33d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hianime-api", - "version": "2.0.0", + "version": "2.0.1", "type": "module", "repository": { "type": "git",