- Project setup: package.json, tsconfig.json, jest.config.js, .env.example - Config: env.ts, constants.ts (ALLOWED_UNITS, bcrypt rounds, JWT, deletion windows) - DB: Knex connection, knexfile, migrations 001-006 (users, password_reset_tokens, pantry_items, recipes+recipe_ingredients, shopping_lists+items, deleted_records) - Seeds: 10 seeded recipes with full ingredient lists - Middleware: JWT authMiddleware (validates token + user existence), errorHandler - Utils: Pino logger, JWT sign helper, Zod validators for all request shapes - Services: authService (signup/signin/google/pwd-reset/soft-delete/restore), pantryService (CRUD + case-insensitive duplicate guard), recipeService (browse+filter+scale), shoppingListService (CRUD+merge logic), syncService (delta sync + tombstone cleanup), emailService (SendGrid) - Routes: /v1/auth, /v1/pantry, /v1/recipes, /v1/shopping-lists, /v1/sync - App: Express factory (createApp), server entry point - Jobs: node-cron daily hard-delete + tombstone cleanup - Tests: validators, utils, auth, pantry, recipes, shopping lists, sync
32 lines
1.1 KiB
TypeScript
32 lines
1.1 KiB
TypeScript
import { Router, Response, NextFunction } from 'express';
|
|
import { authMiddleware, AuthenticatedRequest } from '../middleware/auth';
|
|
import { recipeService } from '../services/recipeService';
|
|
import { recipeQuerySchema, recipeDetailQuerySchema } from '../utils/validators';
|
|
|
|
const router = Router();
|
|
router.use(authMiddleware);
|
|
|
|
// GET /recipes
|
|
router.get('/', async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
|
try {
|
|
const { filter, page, limit, search } = recipeQuerySchema.parse(req.query);
|
|
const result = await recipeService.getRecipes(req.userId!, filter, page, limit, search);
|
|
res.status(200).json({ ...result, synced_at: new Date().toISOString() });
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
// GET /recipes/:recipe_id
|
|
router.get('/:recipe_id', async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
|
try {
|
|
const { scale } = recipeDetailQuerySchema.parse(req.query);
|
|
const recipe = await recipeService.getRecipeById(req.params.recipe_id, req.userId!, scale);
|
|
res.status(200).json({ recipe });
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
export default router;
|