enh
This commit is contained in:
parent
639428caba
commit
1b042819af
18 changed files with 303 additions and 691 deletions
|
|
@ -1,13 +1,12 @@
|
|||
import 'dotenv/config';
|
||||
import express, { NextFunction, Request, Response } from "express";
|
||||
import cors from "cors";
|
||||
// import bodyParser from "body-parser";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { getStaffUserById, getUserDetailsByUserId, isUserSuspended } from '@/src/dbService';
|
||||
import { Hono } from 'hono';
|
||||
import { cors } from 'hono/cors';
|
||||
import { logger } from 'hono/logger';
|
||||
import { serve } from '@hono/node-server';
|
||||
import { trpcServer } from '@hono/trpc-server';
|
||||
import { getStaffUserById, isUserSuspended } from '@/src/dbService';
|
||||
import mainRouter from '@/src/main-router';
|
||||
import initFunc from '@/src/lib/init';
|
||||
import { createExpressMiddleware } from '@trpc/server/adapters/express';
|
||||
import { appRouter } from '@/src/trpc/router';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { jwtVerify } from 'jose'
|
||||
|
|
@ -21,57 +20,28 @@ seed()
|
|||
initFunc()
|
||||
startAutomatedJobs()
|
||||
|
||||
const app = express();
|
||||
const app = new Hono();
|
||||
|
||||
// CORS middleware
|
||||
app.use(cors({
|
||||
origin: 'http://localhost:5174'
|
||||
origin: 'http://localhost:5174',
|
||||
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||
allowHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization'],
|
||||
credentials: true,
|
||||
}));
|
||||
|
||||
|
||||
signedUrlCache.loadFromDisk();
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
// Logger middleware
|
||||
app.use(logger());
|
||||
|
||||
// Middleware to log all request URLs
|
||||
app.use((req, res, next) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
console.log(`[${timestamp}] ${req.method} ${req.url}`);
|
||||
next();
|
||||
});
|
||||
|
||||
//cors middleware
|
||||
export function corsMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||
// Allow requests from any origin (for production, replace * with your domain)
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
|
||||
// Allow specific headers clients can send
|
||||
res.header(
|
||||
'Access-Control-Allow-Headers',
|
||||
'Origin, X-Requested-With, Content-Type, Accept, Authorization'
|
||||
);
|
||||
|
||||
// Allow specific HTTP methods
|
||||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
|
||||
|
||||
// Allow credentials if needed (optional)
|
||||
// res.header('Access-Control-Allow-Credentials', 'true');
|
||||
|
||||
// Handle preflight (OPTIONS) requests quickly
|
||||
if (req.method === 'OPTIONS') {
|
||||
return res.sendStatus(204);
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
|
||||
app.use('/api/trpc', createExpressMiddleware({
|
||||
// tRPC middleware
|
||||
app.use('/api/trpc', trpcServer({
|
||||
router: appRouter,
|
||||
createContext: async ({ req, res }) => {
|
||||
createContext: async ({ req }) => {
|
||||
let user = null;
|
||||
let staffUser = null;
|
||||
const authHeader = req.headers.authorization;
|
||||
const authHeader = req.headers.get('authorization');
|
||||
|
||||
if (authHeader?.startsWith('Bearer ')) {
|
||||
const token = authHeader.substring(7);
|
||||
|
|
@ -85,7 +55,7 @@ app.use('/api/trpc', createExpressMiddleware({
|
|||
const staff = await getStaffUserById(decoded.staffId);
|
||||
|
||||
if (staff) {
|
||||
user=staffUser
|
||||
user = staffUser
|
||||
staffUser = {
|
||||
id: staff.id,
|
||||
name: staff.name,
|
||||
|
|
@ -110,71 +80,64 @@ app.use('/api/trpc', createExpressMiddleware({
|
|||
// Invalid token, both user and staffUser remain null
|
||||
}
|
||||
}
|
||||
return { req, res, user, staffUser };
|
||||
return { req, user, staffUser };
|
||||
},
|
||||
onError({ error, path, type, ctx }) {
|
||||
console.error('🚨 tRPC Error :', {
|
||||
path,
|
||||
type,
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
userId: ctx?.user?.userId,
|
||||
stack: error.stack,
|
||||
});
|
||||
},
|
||||
console.error('🚨 tRPC Error :', {
|
||||
path,
|
||||
type,
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
userId: ctx?.user?.userId,
|
||||
stack: error.stack,
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
app.use('/api', mainRouter)
|
||||
|
||||
const fallbackUiDirCandidates = [
|
||||
path.resolve(__dirname, '../fallback-ui/dist'),
|
||||
path.resolve(__dirname, '../../fallback-ui/dist'),
|
||||
path.resolve(process.cwd(), '../fallback-ui/dist'),
|
||||
path.resolve(process.cwd(), './apps/fallback-ui/dist')
|
||||
]
|
||||
|
||||
const fallbackUiDir =
|
||||
fallbackUiDirCandidates.find((candidate) => fs.existsSync(candidate)) ??
|
||||
fallbackUiDirCandidates[0]
|
||||
|
||||
|
||||
const fallbackUiIndex = path.join(fallbackUiDir, 'index.html')
|
||||
// const fallbackUiMountPath = '/admin-web'
|
||||
const fallbackUiMountPath = '/';
|
||||
|
||||
if (fs.existsSync(fallbackUiIndex)) {
|
||||
app.use(fallbackUiMountPath, express.static(fallbackUiDir))
|
||||
app.use('/mf'+fallbackUiMountPath, express.static(fallbackUiDir))
|
||||
const fallbackUiRegex = new RegExp(
|
||||
`^${fallbackUiMountPath.replace(/\//g, '\\/')}(?:\\/.*)?$`
|
||||
)
|
||||
app.get([fallbackUiMountPath, fallbackUiRegex], (req, res) => {
|
||||
res.sendFile(fallbackUiIndex)
|
||||
})
|
||||
app.get(/.*/, (req,res) => {
|
||||
res.sendFile(fallbackUiIndex)
|
||||
})
|
||||
} else {
|
||||
console.warn(`Fallback UI build not found at ${fallbackUiIndex}`)
|
||||
}
|
||||
|
||||
// Serve /assets/public folder at /assets route
|
||||
const assetsPublicDir = path.resolve(__dirname, './assets/public');
|
||||
if (fs.existsSync(assetsPublicDir)) {
|
||||
app.use('/assets', express.static(assetsPublicDir));
|
||||
console.log('Serving /assets from', assetsPublicDir);
|
||||
} else {
|
||||
console.warn('Assets public folder not found at', assetsPublicDir);
|
||||
}
|
||||
// Mount main router
|
||||
app.route('/api', mainRouter);
|
||||
|
||||
// Global error handler
|
||||
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
app.onError((err, c) => {
|
||||
console.error(err);
|
||||
const status = err.statusCode || err.status || 500;
|
||||
const message = err.message || 'Internal Server Error';
|
||||
res.status(status).json({ message });
|
||||
// Handle different error types
|
||||
let status = 500;
|
||||
let message = 'Internal Server Error';
|
||||
|
||||
if (err instanceof TRPCError) {
|
||||
// Map TRPC error codes to HTTP status codes
|
||||
const trpcStatusMap: Record<string, number> = {
|
||||
'BAD_REQUEST': 400,
|
||||
'UNAUTHORIZED': 401,
|
||||
'FORBIDDEN': 403,
|
||||
'NOT_FOUND': 404,
|
||||
'TIMEOUT': 408,
|
||||
'CONFLICT': 409,
|
||||
'PRECONDITION_FAILED': 412,
|
||||
'PAYLOAD_TOO_LARGE': 413,
|
||||
'METHOD_NOT_SUPPORTED': 405,
|
||||
'UNPROCESSABLE_CONTENT': 422,
|
||||
'TOO_MANY_REQUESTS': 429,
|
||||
'INTERNAL_SERVER_ERROR': 500,
|
||||
};
|
||||
status = trpcStatusMap[err.code] || 500;
|
||||
message = err.message;
|
||||
} else if ((err as any).statusCode) {
|
||||
status = (err as any).statusCode;
|
||||
message = err.message;
|
||||
} else if ((err as any).status) {
|
||||
status = (err as any).status;
|
||||
message = err.message;
|
||||
} else if (err.message) {
|
||||
message = err.message;
|
||||
}
|
||||
|
||||
return c.json({ message }, status as any);
|
||||
});
|
||||
|
||||
app.listen(4000, () => {
|
||||
console.log("Server is running on http://localhost:4000/api/mobile/");
|
||||
serve({
|
||||
fetch: app.fetch,
|
||||
port: 4000,
|
||||
}, (info) => {
|
||||
console.log(`Server is running on http://localhost:${info.port}/api/mobile/`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,23 +20,22 @@
|
|||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.888.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.888.0",
|
||||
"@hono/node-server": "^1.19.11",
|
||||
"@hono/trpc-server": "^0.4.2",
|
||||
"@trpc/server": "^11.6.0",
|
||||
"@turf/turf": "^7.2.0",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/cors": "^2.8.19",
|
||||
"axios": "^1.11.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"cors": "^2.8.5",
|
||||
"dayjs": "^1.11.18",
|
||||
"dotenv": "^17.2.1",
|
||||
"expo-server-sdk": "^4.0.0",
|
||||
"express": "^5.1.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"hono": "^4.12.9",
|
||||
"jose": "^6.2.2",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/node": "^24.5.2",
|
||||
"rimraf": "^6.1.2",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { Router } from "express";
|
||||
import { Hono } from 'hono';
|
||||
import { authenticateStaff } from "@/src/middleware/staff-auth";
|
||||
|
||||
const router = Router();
|
||||
const router = new Hono();
|
||||
|
||||
// Apply staff authentication to all admin routes
|
||||
router.use(authenticateStaff);
|
||||
router.use('*', authenticateStaff);
|
||||
|
||||
const avRouter = router;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Request, Response } from "express";
|
||||
import { Context } from 'hono';
|
||||
import {
|
||||
checkProductExistsByName,
|
||||
checkUnitExists,
|
||||
|
|
@ -25,8 +25,9 @@ type CreateDeal = {
|
|||
/**
|
||||
* Create a new product
|
||||
*/
|
||||
export const createProduct = async (req: Request, res: Response) => {
|
||||
const { name, shortDescription, longDescription, unitId, storeId, price, marketPrice, incrementStep, productQuantity, isSuspended, isFlashAvailable, flashPrice, deals, tagIds } = req.body;
|
||||
export const createProduct = async (c: Context) => {
|
||||
const body = await c.req.parseBody({ all: true });
|
||||
const { name, shortDescription, longDescription, unitId, storeId, price, marketPrice, incrementStep, productQuantity, isSuspended, isFlashAvailable, flashPrice, deals, tagIds } = body;
|
||||
|
||||
// Validate required fields
|
||||
if (!name || !unitId || !storeId || !price) {
|
||||
|
|
@ -34,100 +35,111 @@ export const createProduct = async (req: Request, res: Response) => {
|
|||
}
|
||||
|
||||
// Check for duplicate name
|
||||
const existingProduct = await checkProductExistsByName(name.trim())
|
||||
const existingProduct = await checkProductExistsByName((name as string).trim())
|
||||
|
||||
if (existingProduct) {
|
||||
throw new ApiError("A product with this name already exists", 400);
|
||||
}
|
||||
|
||||
// Check if unit exists
|
||||
const unitExists = await checkUnitExists(unitId)
|
||||
const unitExists = await checkUnitExists(parseInt(unitId as string))
|
||||
|
||||
if (!unitExists) {
|
||||
throw new ApiError("Invalid unit ID", 400);
|
||||
}
|
||||
|
||||
// Extract images from req.files
|
||||
const images = (req.files as Express.Multer.File[])?.filter(item => item.fieldname === 'images');
|
||||
// Extract images from body
|
||||
const images = body.images;
|
||||
let uploadedImageUrls: string[] = [];
|
||||
|
||||
if (images && Array.isArray(images)) {
|
||||
const imageUploadPromises = images.map((file, index) => {
|
||||
const key = `product-images/${Date.now()}-${index}`;
|
||||
return imageUploadS3(file.buffer, file.mimetype, key);
|
||||
});
|
||||
if (images) {
|
||||
const imageFiles = Array.isArray(images) ? images : [images];
|
||||
const imageUploadPromises = imageFiles.map((file, index) => {
|
||||
if (file instanceof File) {
|
||||
const key = `product-images/${Date.now()}-${index}`;
|
||||
return imageUploadS3(Buffer.from(file.stream() as any), file.type, key);
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
|
||||
uploadedImageUrls = await Promise.all(imageUploadPromises);
|
||||
uploadedImageUrls = await Promise.all(imageUploadPromises as Promise<string>[]);
|
||||
}
|
||||
|
||||
// Create product
|
||||
const productData: any = {
|
||||
name,
|
||||
shortDescription,
|
||||
longDescription,
|
||||
unitId,
|
||||
storeId,
|
||||
price,
|
||||
marketPrice,
|
||||
incrementStep: incrementStep || 1,
|
||||
productQuantity: productQuantity || 1,
|
||||
isSuspended: isSuspended || false,
|
||||
isFlashAvailable: isFlashAvailable || false,
|
||||
name: name as string,
|
||||
shortDescription: shortDescription as string | undefined,
|
||||
longDescription: longDescription as string | undefined,
|
||||
unitId: parseInt(unitId as string),
|
||||
storeId: parseInt(storeId as string),
|
||||
price: parseFloat(price as string),
|
||||
marketPrice: marketPrice ? parseFloat(marketPrice as string) : null,
|
||||
incrementStep: incrementStep ? parseInt(incrementStep as string) : 1,
|
||||
productQuantity: productQuantity ? parseInt(productQuantity as string) : 1,
|
||||
isSuspended: isSuspended === 'true',
|
||||
isFlashAvailable: isFlashAvailable === 'true',
|
||||
images: uploadedImageUrls,
|
||||
};
|
||||
|
||||
if (flashPrice) {
|
||||
productData.flashPrice = parseFloat(flashPrice);
|
||||
productData.flashPrice = parseFloat(flashPrice as string);
|
||||
}
|
||||
|
||||
const newProduct = await createProductRecord(productData)
|
||||
|
||||
// Handle deals if provided
|
||||
let createdDeals: AdminSpecialDeal[] = []
|
||||
if (deals && Array.isArray(deals)) {
|
||||
createdDeals = await createSpecialDealsForProduct(newProduct.id, deals)
|
||||
if (deals) {
|
||||
const parsedDeals = typeof deals === 'string' ? JSON.parse(deals) : deals;
|
||||
if (Array.isArray(parsedDeals)) {
|
||||
createdDeals = await createSpecialDealsForProduct(newProduct.id, parsedDeals)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle tag assignments if provided
|
||||
if (tagIds && Array.isArray(tagIds)) {
|
||||
await replaceProductTags(newProduct.id, tagIds)
|
||||
if (tagIds) {
|
||||
const parsedTagIds = typeof tagIds === 'string' ? JSON.parse(tagIds) : tagIds;
|
||||
if (Array.isArray(parsedTagIds)) {
|
||||
await replaceProductTags(newProduct.id, parsedTagIds)
|
||||
}
|
||||
}
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
scheduleStoreInitialization()
|
||||
|
||||
// Send response first
|
||||
res.status(201).json({
|
||||
return c.json({
|
||||
product: newProduct,
|
||||
deals: createdDeals,
|
||||
message: "Product created successfully",
|
||||
});
|
||||
}, 201);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a product
|
||||
*/
|
||||
export const updateProduct = async (req: Request, res: Response) => {
|
||||
const id = req.params.id as string
|
||||
const { name, shortDescription, longDescription, unitId, storeId, price, marketPrice, incrementStep, productQuantity, isSuspended, isFlashAvailable, flashPrice, deals:dealsRaw, imagesToDelete:imagesToDeleteRaw, tagIds } = req.body;
|
||||
export const updateProduct = async (c: Context) => {
|
||||
const id = c.req.param('id')
|
||||
const body = await c.req.parseBody({ all: true });
|
||||
const { name, shortDescription, longDescription, unitId, storeId, price, marketPrice, incrementStep, productQuantity, isSuspended, isFlashAvailable, flashPrice, deals:dealsRaw, imagesToDelete:imagesToDeleteRaw, tagIds } = body;
|
||||
|
||||
|
||||
const deals = dealsRaw ? JSON.parse(dealsRaw) : null;
|
||||
const imagesToDelete = imagesToDeleteRaw ? JSON.parse(imagesToDeleteRaw) : [];
|
||||
const deals = dealsRaw ? (typeof dealsRaw === 'string' ? JSON.parse(dealsRaw) : dealsRaw) : null;
|
||||
const imagesToDelete = imagesToDeleteRaw ? (typeof imagesToDeleteRaw === 'string' ? JSON.parse(imagesToDeleteRaw) : imagesToDeleteRaw) : [];
|
||||
|
||||
if (!name || !unitId || !storeId || !price) {
|
||||
throw new ApiError("Name, unitId, storeId, and price are required", 400);
|
||||
}
|
||||
|
||||
// Check if unit exists
|
||||
const unitExists = await checkUnitExists(unitId)
|
||||
const unitExists = await checkUnitExists(parseInt(unitId as string))
|
||||
|
||||
if (!unitExists) {
|
||||
throw new ApiError("Invalid unit ID", 400);
|
||||
}
|
||||
|
||||
// Get current product to handle image updates
|
||||
const currentImages = await getProductImagesById(parseInt(id))
|
||||
const currentImages = await getProductImagesById(parseInt(id as string))
|
||||
|
||||
if (!currentImages) {
|
||||
throw new ApiError("Product not found", 404);
|
||||
|
|
@ -154,45 +166,49 @@ export const updateProduct = async (req: Request, res: Response) => {
|
|||
updatedImages = updatedImages.filter(img => !imagesToRemoveFromDb.includes(img));
|
||||
}
|
||||
|
||||
// Extract new images from req.files
|
||||
const images = (req.files as Express.Multer.File[])?.filter(item => item.fieldname === 'images');
|
||||
// Extract new images from body
|
||||
const images = body.images;
|
||||
let uploadedImageUrls: string[] = [];
|
||||
|
||||
if (images && Array.isArray(images)) {
|
||||
const imageUploadPromises = images.map((file, index) => {
|
||||
const key = `product-images/${Date.now()}-${index}`;
|
||||
return imageUploadS3(file.buffer, file.mimetype, key);
|
||||
});
|
||||
if (images) {
|
||||
const imageFiles = Array.isArray(images) ? images : [images];
|
||||
const imageUploadPromises = imageFiles.map((file, index) => {
|
||||
if (file instanceof File) {
|
||||
const key = `product-images/${Date.now()}-${index}`;
|
||||
return imageUploadS3(Buffer.from(file.stream() as any), file.type, key);
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
|
||||
uploadedImageUrls = await Promise.all(imageUploadPromises);
|
||||
uploadedImageUrls = await Promise.all(imageUploadPromises as Promise<string>[]);
|
||||
}
|
||||
|
||||
// Combine remaining current images with new uploaded images
|
||||
const finalImages = [...updatedImages, ...uploadedImageUrls];
|
||||
|
||||
const updateData: any = {
|
||||
name,
|
||||
shortDescription,
|
||||
longDescription,
|
||||
unitId,
|
||||
storeId,
|
||||
price,
|
||||
marketPrice,
|
||||
incrementStep: incrementStep || 1,
|
||||
productQuantity: productQuantity || 1,
|
||||
isSuspended: isSuspended || false,
|
||||
name: name as string,
|
||||
shortDescription: shortDescription as string | undefined,
|
||||
longDescription: longDescription as string | undefined,
|
||||
unitId: parseInt(unitId as string),
|
||||
storeId: parseInt(storeId as string),
|
||||
price: parseFloat(price as string),
|
||||
marketPrice: marketPrice ? parseFloat(marketPrice as string) : null,
|
||||
incrementStep: incrementStep ? parseInt(incrementStep as string) : 1,
|
||||
productQuantity: productQuantity ? parseInt(productQuantity as string) : 1,
|
||||
isSuspended: isSuspended === 'true',
|
||||
images: finalImages.length > 0 ? finalImages : undefined,
|
||||
};
|
||||
|
||||
if (isFlashAvailable !== undefined) {
|
||||
updateData.isFlashAvailable = isFlashAvailable;
|
||||
updateData.isFlashAvailable = isFlashAvailable === 'true';
|
||||
}
|
||||
|
||||
if (flashPrice !== undefined) {
|
||||
updateData.flashPrice = flashPrice ? parseFloat(flashPrice) : null;
|
||||
updateData.flashPrice = flashPrice ? parseFloat(flashPrice as string) : null;
|
||||
}
|
||||
|
||||
const updatedProduct = await updateProductRecord(parseInt(id), updateData)
|
||||
const updatedProduct = await updateProductRecord(parseInt(id as string), updateData)
|
||||
|
||||
if (!updatedProduct) {
|
||||
throw new ApiError("Product not found", 404);
|
||||
|
|
@ -200,22 +216,21 @@ export const updateProduct = async (req: Request, res: Response) => {
|
|||
|
||||
// Handle deals if provided
|
||||
if (deals && Array.isArray(deals)) {
|
||||
await updateProductDeals(parseInt(id), deals)
|
||||
await updateProductDeals(parseInt(id as string), deals)
|
||||
}
|
||||
|
||||
// Handle tag assignments if provided
|
||||
// if (tagIds && Array.isArray(tagIds)) {
|
||||
if (tagIds && Boolean(tagIds)) {
|
||||
const tagIdsArray = Array.isArray(tagIds) ? tagIds : [+tagIds]
|
||||
await replaceProductTags(parseInt(id), tagIdsArray)
|
||||
if (tagIds) {
|
||||
const parsedTagIds = typeof tagIds === 'string' ? [parseInt(tagIds)] : (Array.isArray(tagIds) ? tagIds.map((t: any) => parseInt(t)) : [parseInt(tagIds as any)])
|
||||
await replaceProductTags(parseInt(id as string), parsedTagIds)
|
||||
}
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
scheduleStoreInitialization()
|
||||
|
||||
// Send response first
|
||||
res.status(200).json({
|
||||
return c.json({
|
||||
product: updatedProduct,
|
||||
message: "Product updated successfully",
|
||||
});
|
||||
}, 200);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Request, Response } from "express";
|
||||
import { Context } from 'hono';
|
||||
import { scaffoldAssetUrl } from "@/src/lib/s3-client"
|
||||
import { getNextDeliveryDate } from "@/src/trpc/apis/common-apis/common"
|
||||
import {
|
||||
|
|
@ -9,19 +9,19 @@ import {
|
|||
/**
|
||||
* Get all products summary for dropdown
|
||||
*/
|
||||
export const getAllProductsSummary = async (req: Request, res: Response) => {
|
||||
export const getAllProductsSummary = async (c: Context) => {
|
||||
try {
|
||||
const { tagId } = req.query;
|
||||
const tagIdNum = tagId ? parseInt(tagId as string) : undefined;
|
||||
const tagId = c.req.query('tagId');
|
||||
const tagIdNum = tagId ? parseInt(tagId) : undefined;
|
||||
|
||||
// If tagId is provided but no products found, return empty array
|
||||
if (tagIdNum) {
|
||||
const products = await getAllProductsWithUnits(tagIdNum);
|
||||
if (products.length === 0) {
|
||||
return res.status(200).json({
|
||||
return c.json({
|
||||
products: [],
|
||||
count: 0,
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,13 +46,13 @@ export const getAllProductsSummary = async (req: Request, res: Response) => {
|
|||
})
|
||||
);
|
||||
|
||||
return res.status(200).json({
|
||||
return c.json({
|
||||
products: formattedProducts,
|
||||
count: formattedProducts.length,
|
||||
});
|
||||
}, 200);
|
||||
} catch (error) {
|
||||
console.error("Get products summary error:", error);
|
||||
return res.status(500).json({ error: "Failed to fetch products summary" });
|
||||
return c.json({ error: "Failed to fetch products summary" }, 500);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { Router } from "express";
|
||||
import { Hono } from 'hono';
|
||||
import { getAllProductsSummary } from "@/src/apis/common-apis/apis/common-product.controller"
|
||||
|
||||
const router = Router();
|
||||
const router = new Hono();
|
||||
|
||||
router.get("/summary", getAllProductsSummary);
|
||||
|
||||
|
||||
const commonProductsRouter= router;
|
||||
export default commonProductsRouter;
|
||||
export default commonProductsRouter;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { Router } from "express";
|
||||
import { Hono } from 'hono';
|
||||
import commonProductsRouter from "@/src/apis/common-apis/apis/common-product.router"
|
||||
|
||||
const router = Router();
|
||||
const router = new Hono();
|
||||
|
||||
router.use('/products', commonProductsRouter)
|
||||
router.route('/products', commonProductsRouter)
|
||||
|
||||
const commonRouter = router;
|
||||
|
||||
export default commonRouter;
|
||||
export default commonRouter;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import express from 'express';
|
||||
const catchAsync =
|
||||
(fn: express.RequestHandler) =>
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) =>
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
export default catchAsync;
|
||||
// catchAsync is no longer needed with Hono
|
||||
// Hono handles async errors automatically
|
||||
// This file is kept for backward compatibility but should be removed in the future
|
||||
|
||||
import { Context } from 'hono';
|
||||
|
||||
const catchAsync = (fn: (c: Context) => Promise<Response>) => {
|
||||
return fn;
|
||||
};
|
||||
|
||||
export default catchAsync;
|
||||
|
|
|
|||
|
|
@ -1,56 +1,36 @@
|
|||
import { Router, Request, Response, NextFunction } from "express";
|
||||
import { Hono } from 'hono';
|
||||
import avRouter from "@/src/apis/admin-apis/apis/av-router"
|
||||
import { ApiError } from "@/src/lib/api-error"
|
||||
import v1Router from "@/src/v1-router"
|
||||
import testController from "@/src/test-controller"
|
||||
import { authenticateUser } from "@/src/middleware/auth.middleware"
|
||||
|
||||
|
||||
const router = Router();
|
||||
const router = new Hono();
|
||||
|
||||
// Health check endpoints (no auth required)
|
||||
router.get('/health', (req: Request, res: Response) => {
|
||||
res.status(200).json({
|
||||
router.get('/health', (c) => {
|
||||
return c.json({
|
||||
status: 'OK',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime(),
|
||||
message: 'Hello world'
|
||||
});
|
||||
});
|
||||
router.get('/seed', (req:Request, res: Response) => {
|
||||
res.status(200).json({
|
||||
|
||||
router.get('/seed', (c) => {
|
||||
return c.json({
|
||||
status: 'OK',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime()
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// Apply authentication middleware to all subsequent routes
|
||||
router.use(authenticateUser);
|
||||
router.use('*', authenticateUser);
|
||||
|
||||
router.use('/v1', v1Router);
|
||||
// router.use('/av', avRouter);
|
||||
router.use('/test', testController);
|
||||
|
||||
// Global error handling middleware
|
||||
router.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
||||
console.error('Error:', err);
|
||||
|
||||
if (err instanceof ApiError) {
|
||||
return res.status(err.statusCode).json({
|
||||
error: err.message,
|
||||
details: err.details,
|
||||
statusCode: err.statusCode
|
||||
});
|
||||
}
|
||||
|
||||
// Handle unknown errors
|
||||
return res.status(500).json({
|
||||
error: 'Internal Server Error',
|
||||
message: process.env.NODE_ENV === 'development' ? err.message : undefined,
|
||||
statusCode: 500
|
||||
});
|
||||
});
|
||||
router.route('/v1', v1Router);
|
||||
// router.route('/av', avRouter);
|
||||
router.route('/test', testController);
|
||||
|
||||
const mainRouter = router;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,31 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Context, Next } from 'hono';
|
||||
import { jwtVerify } from 'jose';
|
||||
import { getStaffUserById, isUserSuspended } from '@/src/dbService';
|
||||
import { ApiError } from '@/src/lib/api-error';
|
||||
import { encodedJwtSecret } from '@/src/lib/env-exporter';
|
||||
|
||||
interface AuthenticatedRequest extends Request {
|
||||
user?: {
|
||||
userId: number;
|
||||
name?: string;
|
||||
email?: string;
|
||||
mobile?: string;
|
||||
};
|
||||
staffUser?: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
interface UserContext {
|
||||
userId: number;
|
||||
name?: string;
|
||||
email?: string;
|
||||
mobile?: string;
|
||||
}
|
||||
|
||||
export const authenticateUser = async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
||||
interface StaffContext {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const authenticateUser = async (c: Context, next: Next) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
const authHeader = c.req.header('authorization');
|
||||
|
||||
if (!authHeader?.startsWith('Bearer ')) {
|
||||
throw new ApiError('Authorization token required', 401);
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7);
|
||||
console.log(req.headers)
|
||||
console.log(c.req.header)
|
||||
|
||||
const { payload } = await jwtVerify(token, encodedJwtSecret);
|
||||
const decoded = payload as any;
|
||||
|
|
@ -51,13 +50,13 @@ export const authenticateUser = async (req: AuthenticatedRequest, res: Response,
|
|||
throw new ApiError('Invalid staff token', 401);
|
||||
}
|
||||
|
||||
req.staffUser = {
|
||||
c.set('staffUser', {
|
||||
id: staff.id,
|
||||
name: staff.name,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
// This is a regular user token
|
||||
req.user = decoded;
|
||||
c.set('user', decoded);
|
||||
|
||||
/*
|
||||
// Old implementation - direct DB queries:
|
||||
|
|
@ -82,8 +81,8 @@ export const authenticateUser = async (req: AuthenticatedRequest, res: Response,
|
|||
}
|
||||
}
|
||||
|
||||
next();
|
||||
await next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,21 +1,12 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Context, Next } from 'hono';
|
||||
import { jwtVerify, errors } from 'jose';
|
||||
import { ApiError } from '@/src/lib/api-error'
|
||||
import { encodedJwtSecret } from '@/src/lib/env-exporter';
|
||||
|
||||
// Extend the Request interface to include user property
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const verifyToken = async (req: Request, res: Response, next: NextFunction) => {
|
||||
export const verifyToken = async (c: Context, next: Next) => {
|
||||
try {
|
||||
// Get token from Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
const authHeader = c.req.header('authorization');
|
||||
|
||||
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
|
|
@ -32,37 +23,38 @@ export const verifyToken = async (req: Request, res: Response, next: NextFunctio
|
|||
const { payload } = await jwtVerify(token, encodedJwtSecret);
|
||||
|
||||
|
||||
// Add user info to request
|
||||
req.user = payload;
|
||||
// Add user info to context
|
||||
c.set('user', payload);
|
||||
|
||||
next();
|
||||
await next();
|
||||
} catch (error) {
|
||||
if (error instanceof errors.JOSEError) {
|
||||
next(new ApiError('Invalid Auth Credentials', 401));
|
||||
throw new ApiError('Invalid Auth Credentials', 401);
|
||||
} else {
|
||||
next(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const requireRole = (roles: string[]) => {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
return async (c: Context, next: Next) => {
|
||||
try {
|
||||
if (!req.user) {
|
||||
const user = c.get('user');
|
||||
if (!user) {
|
||||
throw new ApiError('Authentication required', 401);
|
||||
}
|
||||
|
||||
// Check if user has any of the required roles
|
||||
const userRoles = req.user.roles || [];
|
||||
const userRoles = user.roles || [];
|
||||
const hasPermission = roles.some(role => userRoles.includes(role));
|
||||
|
||||
if (!hasPermission) {
|
||||
throw new ApiError('Access denied. Insufficient permissions', 403);
|
||||
}
|
||||
|
||||
next();
|
||||
await next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,21 +1,9 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
import { jwtVerify, errors } from 'jose';
|
||||
import { Context, Next } from 'hono';
|
||||
import { jwtVerify } from 'jose';
|
||||
import { getStaffUserById } from '@/src/dbService';
|
||||
import { ApiError } from '@/src/lib/api-error';
|
||||
import { encodedJwtSecret } from '@/src/lib/env-exporter';
|
||||
|
||||
// Extend Request interface to include staffUser
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
staffUser?: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify JWT token and extract payload
|
||||
*/
|
||||
|
|
@ -29,12 +17,12 @@ const verifyStaffToken = async (token: string) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Middleware to authenticate staff users and attach staffUser to request
|
||||
* Middleware to authenticate staff users and attach staffUser to context
|
||||
*/
|
||||
export const authenticateStaff = async (req: Request, res: Response, next: NextFunction) => {
|
||||
export const authenticateStaff = async (c: Context, next: Next) => {
|
||||
try {
|
||||
// Extract token from Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
const authHeader = c.req.header('authorization');
|
||||
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
throw new ApiError('Staff authentication required', 401);
|
||||
|
|
@ -72,14 +60,14 @@ export const authenticateStaff = async (req: Request, res: Response, next: NextF
|
|||
throw new ApiError('Staff user not found', 401);
|
||||
}
|
||||
|
||||
// Attach staff user to request
|
||||
req.staffUser = {
|
||||
// Attach staff user to context
|
||||
c.set('staffUser', {
|
||||
id: staff.id,
|
||||
name: staff.name,
|
||||
};
|
||||
});
|
||||
|
||||
next();
|
||||
await next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { Router, Request, Response } from 'express';
|
||||
import { Hono } from 'hono';
|
||||
|
||||
const router = Router();
|
||||
const router = new Hono();
|
||||
|
||||
router.get('/', (req: Request, res: Response) => {
|
||||
res.json({
|
||||
router.get('/', (c) => {
|
||||
return c.json({
|
||||
status: 'ok',
|
||||
message: 'Health check passed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -357,7 +357,8 @@ export const authRouter = router({
|
|||
? await generateSignedUrlFromS3Url(userDetail.profileImage)
|
||||
: null
|
||||
|
||||
const token = ctx.req.headers.authorization?.replace('Bearer ', '') || ''
|
||||
const authHeader = ctx.req.header('authorization');
|
||||
const token = authHeader?.replace('Bearer ', '') || ''
|
||||
|
||||
const response: UserAuthResponse = {
|
||||
token,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { initTRPC, TRPCError } from '@trpc/server';
|
||||
import { type CreateExpressContextOptions } from '@trpc/server/adapters/express';
|
||||
import type { Context as HonoContext } from 'hono';
|
||||
|
||||
export interface Context {
|
||||
req: CreateExpressContextOptions['req'];
|
||||
res: CreateExpressContextOptions['res'];
|
||||
req: HonoContext['req'];
|
||||
user?: any;
|
||||
staffUser?: {
|
||||
id: number;
|
||||
|
|
@ -70,4 +69,4 @@ export const protectedProcedure = t.procedure.use(errorLoggerMiddleware).use(
|
|||
);
|
||||
|
||||
export const createCallerFactory = t.createCallerFactory;
|
||||
export const createTRPCRouter = t.router;
|
||||
export const createTRPCRouter = t.router;
|
||||
|
|
|
|||
13
apps/backend/src/types/hono.d.ts
vendored
Normal file
13
apps/backend/src/types/hono.d.ts
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { Context as HonoContext } from 'hono';
|
||||
|
||||
declare module 'hono' {
|
||||
interface ContextVariableMap {
|
||||
user?: any;
|
||||
staffUser?: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import { Router } from "express";
|
||||
import { Hono } from 'hono';
|
||||
import avRouter from "@/src/apis/admin-apis/apis/av-router"
|
||||
import commonRouter from "@/src/apis/common-apis/apis/common.router"
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use('/av', avRouter);
|
||||
router.use('/cm', commonRouter);
|
||||
const router = new Hono();
|
||||
|
||||
router.route('/av', avRouter);
|
||||
router.route('/cm', commonRouter);
|
||||
|
||||
const v1Router = router;
|
||||
|
||||
|
|
|
|||
415
package-lock.json
generated
415
package-lock.json
generated
|
|
@ -127,23 +127,22 @@
|
|||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.888.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.888.0",
|
||||
"@hono/node-server": "^1.19.11",
|
||||
"@hono/trpc-server": "^0.4.2",
|
||||
"@trpc/server": "^11.6.0",
|
||||
"@turf/turf": "^7.2.0",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/cors": "^2.8.19",
|
||||
"axios": "^1.11.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"cors": "^2.8.5",
|
||||
"dayjs": "^1.11.18",
|
||||
"dotenv": "^17.2.1",
|
||||
"expo-server-sdk": "^4.0.0",
|
||||
"express": "^5.1.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"hono": "^4.12.9",
|
||||
"jose": "^6.2.2",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/node": "^24.5.2",
|
||||
"rimraf": "^6.1.2",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
|
|
@ -3720,6 +3719,31 @@
|
|||
"excpretty": "build/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "1.19.11",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz",
|
||||
"integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hono": "^4"
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/trpc-server": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@hono/trpc-server/-/trpc-server-0.4.2.tgz",
|
||||
"integrity": "sha512-3TDrc42CZLgcTFkXQba+y7JlRWRiyw1AqhLqztWyNS2IFT+3bHld0lxKdGBttCtGKHYx0505dM67RMazjhdZqw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trpc/server": "^10.10.0 || >11.0.0-rc",
|
||||
"hono": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"dev": true,
|
||||
|
|
@ -7600,35 +7624,11 @@
|
|||
"version": "2.4.6",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.6",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/canvas-confetti": {
|
||||
"version": "1.9.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.38",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cors": {
|
||||
"version": "2.8.19",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-voronoi": {
|
||||
"version": "1.1.12",
|
||||
"license": "MIT"
|
||||
|
|
@ -7642,27 +7642,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "5.0.6",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/serve-static": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "5.1.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.16",
|
||||
"license": "MIT"
|
||||
|
|
@ -7706,11 +7685,6 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.5",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.6",
|
||||
"license": "MIT"
|
||||
|
|
@ -7781,16 +7755,6 @@
|
|||
"pg-types": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.15.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.7",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.0.14",
|
||||
"license": "MIT",
|
||||
|
|
@ -7821,23 +7785,6 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "1.2.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "2.2.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/stack-utils": {
|
||||
"version": "2.0.3",
|
||||
"license": "MIT"
|
||||
|
|
@ -8232,17 +8179,6 @@
|
|||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "^3.0.0",
|
||||
"negotiator": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"license": "MIT",
|
||||
|
|
@ -8915,28 +8851,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.2.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^1.0.5",
|
||||
"debug": "^4.4.3",
|
||||
"http-errors": "^2.0.0",
|
||||
"iconv-lite": "^0.7.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.14.1",
|
||||
"raw-body": "^3.0.1",
|
||||
"type-is": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
|
|
@ -9573,17 +9487,6 @@
|
|||
"@babel/types": "^7.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "1.0.1",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"license": "MIT",
|
||||
|
|
@ -9606,13 +9509,6 @@
|
|||
"version": "2.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.2.2",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-anything": {
|
||||
"version": "4.0.5",
|
||||
"license": "MIT",
|
||||
|
|
@ -9637,21 +9533,6 @@
|
|||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.6",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "5.2.1",
|
||||
"license": "MIT",
|
||||
|
|
@ -11587,47 +11468,6 @@
|
|||
"version": "3.1.3",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "5.2.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "^1.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"finalhandler": "^2.1.0",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"merge-descriptors": "^2.0.0",
|
||||
"mime-types": "^3.0.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"once": "^1.4.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"qs": "^6.14.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"router": "^2.2.0",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.2.0",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/fallback-ui": {
|
||||
"resolved": "apps/fallback-ui",
|
||||
"link": true
|
||||
|
|
@ -11809,25 +11649,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "2.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"parseurl": "^1.3.3",
|
||||
"statuses": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
"license": "MIT",
|
||||
|
|
@ -11996,13 +11817,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
|
|
@ -12439,6 +12253,15 @@
|
|||
"version": "16.13.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"version": "4.12.9",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz",
|
||||
"integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "7.0.2",
|
||||
"license": "ISC",
|
||||
|
|
@ -12486,20 +12309,6 @@
|
|||
"version": "1.1.0",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"funding": [
|
||||
|
|
@ -12940,10 +12749,6 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "4.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-regex": {
|
||||
"version": "1.2.1",
|
||||
"license": "MIT",
|
||||
|
|
@ -13773,27 +13578,10 @@
|
|||
"version": "2.0.14",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "1.1.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"license": "MIT"
|
||||
|
|
@ -14175,27 +13963,6 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "3.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
"version": "1.2.0",
|
||||
"license": "MIT",
|
||||
|
|
@ -14311,13 +14078,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/nested-error-stacks": {
|
||||
"version": "2.0.1",
|
||||
"license": "MIT"
|
||||
|
|
@ -15443,19 +15203,6 @@
|
|||
"qrcode-terminal": "bin/qrcode-terminal.js"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.15.0",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/query-string": {
|
||||
"version": "7.1.3",
|
||||
"license": "MIT",
|
||||
|
|
@ -15516,19 +15263,6 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "3.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "~3.1.2",
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.7.0",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/rbush": {
|
||||
"version": "3.0.1",
|
||||
"license": "MIT",
|
||||
|
|
@ -16394,28 +16128,6 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.2.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"path-to-regexp": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/router/node_modules/path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"funding": [
|
||||
|
|
@ -16529,30 +16241,6 @@
|
|||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "1.2.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.3",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.1",
|
||||
"mime-types": "^3.0.2",
|
||||
"ms": "^2.1.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
"statuses": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/serialize-error": {
|
||||
"version": "2.1.0",
|
||||
"license": "MIT",
|
||||
|
|
@ -16577,23 +16265,6 @@
|
|||
"seroval": "^1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "2.2.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"parseurl": "^1.3.3",
|
||||
"send": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/server-only": {
|
||||
"version": "0.0.1",
|
||||
"license": "MIT"
|
||||
|
|
@ -17944,18 +17615,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "2.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
"media-typer": "^1.1.0",
|
||||
"mime-types": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typed-array-buffer": {
|
||||
"version": "1.0.3",
|
||||
"dev": true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue