diff --git a/apps/backend/.env b/apps/backend/.env index be2c9e2..7a02db2 100755 --- a/apps/backend/.env +++ b/apps/backend/.env @@ -1,7 +1,10 @@ ENV_MODE=PROD -DATABASE_URL=postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer #technocracy +# DATABASE_URL=postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer #technocracy # DATABASE_URL=postgres://postgres:meatfarmer_master_password@5.223.55.14:7447/meatfarmer #hetzner +SQLITE_DB_PATH='./sqlite.db' +DB_DIALECT='sqlite' PHONE_PE_BASE_URL=https://api-preprod.phonepe.com/ + PHONE_PE_CLIENT_ID=TEST-M23F2IGP34ZAR_25090 PHONE_PE_CLIENT_VERSION=1 PHONE_PE_CLIENT_SECRET=MTU1MmIzOTgtM2Q0Mi00N2M5LTkyMWUtNzBiMjdmYzVmZWUy diff --git a/apps/backend/drizzle.config.ts b/apps/backend/drizzle.config.ts index 0b5c1c9..0dea3b8 100755 --- a/apps/backend/drizzle.config.ts +++ b/apps/backend/drizzle.config.ts @@ -1,11 +1,6 @@ -import 'dotenv/config'; -import { defineConfig } from 'drizzle-kit'; +import postgresConfig from '../db-helper-postgres/drizzle.config' +import sqliteConfig from '../db-helper-sqlite/drizzle.config' -export default defineConfig({ - out: './drizzle', - schema: './src/db/schema.ts', - dialect: 'postgresql', - dbCredentials: { - url: process.env.DATABASE_URL!, - }, -}); +export default process.env.DB_DIALECT === 'sqlite' + ? sqliteConfig + : postgresConfig diff --git a/apps/backend/index.ts b/apps/backend/index.ts index 9f975b4..390aef7 100644 --- a/apps/backend/index.ts +++ b/apps/backend/index.ts @@ -157,7 +157,6 @@ app.onError((err, c) => { return c.json({ message }, status) }) -<<<<<<< HEAD // Start server serve({ fetch: app.fetch, @@ -166,8 +165,3 @@ serve({ }) console.log('🚀 Server running on http://localhost:4000') -======= -app.listen(4000, '::', () => { - console.log("Server is running on http://localhost:4000/api/mobile/"); -}); ->>>>>>> main diff --git a/apps/backend/package.json b/apps/backend/package.json index ff902d1..fdc95fa 100755 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -4,16 +4,16 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "migrate": "drizzle-kit generate --config drizzle.config.postgres.ts", - "migrate:pg": "drizzle-kit generate --config drizzle.config.postgres.ts", - "migrate:sqlite": "drizzle-kit generate --config drizzle.config.sqlite.ts", - "generate:pg": "bunx drizzle-kit generate --config drizzle.config.postgres.ts", - "generate:sqlite": "bunx drizzle-kit generate --config drizzle.config.sqlite.ts", + "migrate": "drizzle-kit generate --config ../db-helper-postgres/drizzle.config.ts", + "migrate:pg": "drizzle-kit generate --config ../db-helper-postgres/drizzle.config.ts", + "migrate:sqlite": "drizzle-kit generate --config ../db-helper-sqlite/drizzle.config.ts", + "generate:pg": "bunx drizzle-kit generate --config ../db-helper-postgres/drizzle.config.ts", + "generate:sqlite": "bunx drizzle-kit generate --config ../db-helper-sqlite/drizzle.config.ts", "build": "rimraf ./dist && tsc --project tsconfig.json && tsc-alias -p tsconfig.json", "build2": "rimraf ./dist && tsc", - "db:push": "drizzle-kit push --config drizzle.config.postgres.ts", - "db:push:pg": "drizzle-kit push --config drizzle.config.postgres.ts", - "db:push:sqlite": "drizzle-kit push --config drizzle.config.sqlite.ts", + "db:push": "drizzle-kit push --config ../db-helper-postgres/drizzle.config.ts", + "db:push:pg": "drizzle-kit push --config ../db-helper-postgres/drizzle.config.ts", + "db:push:sqlite": "drizzle-kit push --config ../db-helper-sqlite/drizzle.config.ts", "db:seed": "tsx src/db/seed.ts", "dev:express": "bun --watch index-express.ts", "dev:hono": "bun --watch index.ts", @@ -46,7 +46,6 @@ "jose": "^5.10.0", "node-cron": "^4.2.1", "pg": "^8.16.3", - "razorpay": "^2.9.6", "redis": "^5.9.0", "zod": "^4.1.12" }, diff --git a/apps/backend/sqlite.db b/apps/backend/sqlite.db new file mode 100644 index 0000000..20e8c51 Binary files /dev/null and b/apps/backend/sqlite.db differ diff --git a/apps/backend/src/apis/common-apis/apis/common-product.controller.ts b/apps/backend/src/apis/common-apis/apis/common-product.controller.ts index 2da05b6..366c8a6 100644 --- a/apps/backend/src/apis/common-apis/apis/common-product.controller.ts +++ b/apps/backend/src/apis/common-apis/apis/common-product.controller.ts @@ -1,105 +1,37 @@ -import { eq, gt, and, sql, inArray } from "drizzle-orm"; -import { Context } from "hono"; -import { db } from "@/src/db/db_index" -import { productInfo, units, productSlots, deliverySlotInfo, productTags } from "@/src/db/schema" -import { scaffoldAssetUrl } from "@/src/lib/s3-client" +import { Context } from 'hono' -/** - * Get next delivery date for a product - */ -const getNextDeliveryDate = async (productId: number): Promise => { - const result = await db - .select({ deliveryTime: deliverySlotInfo.deliveryTime }) - .from(productSlots) - .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) - .where( - and( - eq(productSlots.productId, productId), - eq(deliverySlotInfo.isActive, true), - gt(deliverySlotInfo.deliveryTime, sql`NOW()`) - ) - ) - .orderBy(deliverySlotInfo.deliveryTime) - .limit(1); - - - return result[0]?.deliveryTime || null; -}; +import { getProductsSummaryData } from '@/src/db/common-product' +import { scaffoldAssetUrl } from '@/src/lib/s3-client' /** * Get all products summary for dropdown */ export const getAllProductsSummary = async (c: Context) => { try { - const tagId = c.req.query('tagId'); - const tagIdNum = tagId ? parseInt(tagId) : null; + const tagId = c.req.query('tagId') + const tagIdNum = tagId ? parseInt(tagId) : null - let productIds: number[] | null = null; + const productsWithUnits = await getProductsSummaryData(tagIdNum) - // If tagId is provided, get products that have this tag - if (tagIdNum) { - const taggedProducts = await db - .select({ productId: productTags.productId }) - .from(productTags) - .where(eq(productTags.tagId, tagIdNum)); - - productIds = taggedProducts.map(tp => tp.productId); - } - - let whereCondition = undefined; - - // Filter by product IDs if tag filtering is applied - if (productIds && productIds.length > 0) { - whereCondition = inArray(productInfo.id, productIds); - } else if (tagIdNum) { - // If tagId was provided but no products found, return empty array - return c.json({ - products: [], - count: 0, - }); - } - - const productsWithUnits = await db - .select({ - id: productInfo.id, - name: productInfo.name, - shortDescription: productInfo.shortDescription, - price: productInfo.price, - marketPrice: productInfo.marketPrice, - images: productInfo.images, - isOutOfStock: productInfo.isOutOfStock, - unitShortNotation: units.shortNotation, - productQuantity: productInfo.productQuantity, - }) - .from(productInfo) - .innerJoin(units, eq(productInfo.unitId, units.id)) - .where(whereCondition); - - // Generate signed URLs for product images - const formattedProducts = await Promise.all( - productsWithUnits.map(async (product) => { - const nextDeliveryDate = await getNextDeliveryDate(product.id); - return { - id: product.id, - name: product.name, - shortDescription: product.shortDescription, - price: product.price, - marketPrice: product.marketPrice, - unit: product.unitShortNotation, - productQuantity: product.productQuantity, - isOutOfStock: product.isOutOfStock, - nextDeliveryDate: nextDeliveryDate ? nextDeliveryDate.toISOString() : null, - images: scaffoldAssetUrl((product.images as string[]) || []), - }; - }) - ); + const formattedProducts = productsWithUnits.map((product) => ({ + id: product.id, + name: product.name, + shortDescription: product.shortDescription, + price: product.price, + marketPrice: product.marketPrice, + unit: product.unitShortNotation, + productQuantity: product.productQuantity, + isOutOfStock: product.isOutOfStock, + nextDeliveryDate: product.nextDeliveryDate ? product.nextDeliveryDate.toISOString() : null, + images: scaffoldAssetUrl((product.images as string[]) || []), + })) return c.json({ products: formattedProducts, count: formattedProducts.length, - }); + }) } catch (error) { - console.error("Get products summary error:", error); - return c.json({ error: "Failed to fetch products summary" }, 500); + console.error('Get products summary error:', error) + return c.json({ error: 'Failed to fetch products summary' }, 500) } -}; +} diff --git a/apps/backend/src/db/common-product.ts b/apps/backend/src/db/common-product.ts new file mode 100644 index 0000000..ed4ac45 --- /dev/null +++ b/apps/backend/src/db/common-product.ts @@ -0,0 +1,10 @@ +import { getProductsSummaryData as getProductsSummaryDataPostgres } from '@db-helper-postgres/apis/common-apis/common-product' +import { getProductsSummaryData as getProductsSummaryDataSqlite } from '@db-helper-sqlite/apis/common-apis/common-product' + +const dialect = process.env.DB_DIALECT || DB_DIALECT_TYPE + +const getProductsSummaryData = dialect === 'sqlite' + ? getProductsSummaryDataSqlite + : getProductsSummaryDataPostgres + +export { getProductsSummaryData } diff --git a/apps/backend/src/db/db_index.ts b/apps/backend/src/db/db_index.ts old mode 100755 new mode 100644 index 4e2d75a..79f4782 --- a/apps/backend/src/db/db_index.ts +++ b/apps/backend/src/db/db_index.ts @@ -1,8 +1,10 @@ -import { drizzle } from "drizzle-orm/node-postgres" -import { migrate } from "drizzle-orm/node-postgres/migrator" -import path from "path" -import * as schema from "@/src/db/schema-postgres" +import { db as postgresDb } from '@db-helper-postgres/db/db_index' +import { db as sqliteDb } from '@db-helper-sqlite/db/db_index' + +const dialect = process.env.DB_DIALECT || DB_DIALECT_TYPE + +type Db = typeof DB_DIALECT_TYPE extends 'sqlite' ? typeof sqliteDb : typeof postgresDb + +const db = (dialect === 'sqlite' ? sqliteDb : postgresDb) as Db -const db = drizzle({ connection: process.env.DATABASE_URL!, casing: "snake_case", schema: schema }) -// const db = drizzle('postgresql://postgres:postgres@localhost:2345/pooler'); export { db } diff --git a/apps/backend/src/db/db_index_sqlite.ts b/apps/backend/src/db/db_index_sqlite.ts deleted file mode 100644 index c9390d4..0000000 --- a/apps/backend/src/db/db_index_sqlite.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { drizzle } from 'drizzle-orm/bun-sqlite' -import { Database } from 'bun:sqlite' -import * as schema from '@/src/db/schema-sqlite' - -const sqlitePath = process.env.SQLITE_DB_PATH || 'sqlite.db' -const sqlite = new Database(sqlitePath) - -const db = drizzle(sqlite, { schema }) - -export { db } diff --git a/apps/backend/src/db/schema-postgres.ts b/apps/backend/src/db/schema-postgres.ts index 939e04d..ddb97e4 100644 --- a/apps/backend/src/db/schema-postgres.ts +++ b/apps/backend/src/db/schema-postgres.ts @@ -1,706 +1 @@ -import { pgTable, pgSchema, integer, varchar, date, boolean, timestamp, numeric, jsonb, pgEnum, unique, real, text, check, decimal } from "drizzle-orm/pg-core"; -import { relations, sql } from "drizzle-orm"; - -const mf = pgSchema('mf'); - - - -export const users = mf.table('users', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }), - email: varchar({ length: 255 }), - mobile: varchar({ length: 255 }), - createdAt: timestamp('created_at').notNull().defaultNow(), -}, (t) => ({ - unq_email: unique('unique_email').on(t.email), -})); - -export const userDetails = mf.table('user_details', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id).unique(), - bio: varchar('bio', { length: 500 }), - dateOfBirth: date('date_of_birth'), - gender: varchar('gender', { length: 20 }), - occupation: varchar('occupation', { length: 100 }), - profileImage: varchar('profile_image', { length: 500 }), - isSuspended: boolean('is_suspended').notNull().default(false), - createdAt: timestamp('created_at').notNull().defaultNow(), - updatedAt: timestamp('updated_at').notNull().defaultNow(), -}); - -export const userCreds = mf.table('user_creds', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - userPassword: varchar('user_password', { length: 255 }).notNull(), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const addresses = mf.table('addresses', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - name: varchar('name', { length: 255 }).notNull(), - phone: varchar('phone', { length: 15 }).notNull(), - addressLine1: varchar('address_line1', { length: 255 }).notNull(), - addressLine2: varchar('address_line2', { length: 255 }), - city: varchar('city', { length: 100 }).notNull(), - state: varchar('state', { length: 100 }).notNull(), - pincode: varchar('pincode', { length: 10 }).notNull(), - isDefault: boolean('is_default').notNull().default(false), - latitude: real('latitude'), - longitude: real('longitude'), - googleMapsUrl: varchar('google_maps_url', { length: 500 }), - adminLatitude: real('admin_latitude'), - adminLongitude: real('admin_longitude'), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const addressZones = mf.table('address_zones', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - zoneName: varchar('zone_name', { length: 255 }).notNull(), - addedAt: timestamp('added_at').notNull().defaultNow(), -}); - -export const addressAreas = mf.table('address_areas', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - placeName: varchar('place_name', { length: 255 }).notNull(), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const staffUsers = mf.table('staff_users', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }).notNull(), - password: varchar({ length: 255 }).notNull(), - staffRoleId: integer('staff_role_id').references(() => staffRoles.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const storeInfo = mf.table('store_info', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }).notNull(), - description: varchar({ length: 500 }), - imageUrl: varchar('image_url', { length: 500 }), - createdAt: timestamp('created_at').notNull().defaultNow(), - owner: integer('owner').notNull().references(() => staffUsers.id), -}); - -export const units = mf.table('units', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - shortNotation: varchar('short_notation', { length: 50 }).notNull(), - fullName: varchar('full_name', { length: 100 }).notNull(), -}, (t) => ({ - unq_short_notation: unique('unique_short_notation').on(t.shortNotation), -})); - -export const productAvailabilityActionEnum = pgEnum('product_availability_action', ['in', 'out']); - -export const productInfo = mf.table('product_info', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }).notNull(), - shortDescription: varchar('short_description', { length: 500 }), - longDescription: varchar('long_description', { length: 1000 }), - unitId: integer('unit_id').notNull().references(() => units.id), - price: numeric({ precision: 10, scale: 2 }).notNull(), - marketPrice: numeric('market_price', { precision: 10, scale: 2 }), - images: jsonb('images'), - isOutOfStock: boolean('is_out_of_stock').notNull().default(false), - isSuspended: boolean('is_suspended').notNull().default(false), - isFlashAvailable: boolean('is_flash_available').notNull().default(false), - flashPrice: numeric('flash_price', { precision: 10, scale: 2 }), - createdAt: timestamp('created_at').notNull().defaultNow(), - incrementStep: real('increment_step').notNull().default(1), - productQuantity: real('product_quantity').notNull().default(1), - storeId: integer('store_id').references(() => storeInfo.id), - scheduledAvailability: boolean('scheduled_availability').notNull().default(true), -}); - -export const productAvailabilitySchedules = mf.table('product_availability_schedules', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - time: varchar('time', { length: 10 }).notNull(), - scheduleName: varchar('schedule_name', { length: 255 }).notNull().unique(), - action: productAvailabilityActionEnum('action').notNull(), - productIds: integer('product_ids').array().notNull().default([]), - groupIds: integer('group_ids').array().notNull().default([]), - createdAt: timestamp('created_at').notNull().defaultNow(), - lastUpdated: timestamp('last_updated').notNull().defaultNow(), -}); - -export const productGroupInfo = mf.table('product_group_info', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - groupName: varchar('group_name', { length: 255 }).notNull(), - description: varchar({ length: 500 }), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const productGroupMembership = mf.table('product_group_membership', { - productId: integer('product_id').notNull().references(() => productInfo.id), - groupId: integer('group_id').notNull().references(() => productGroupInfo.id), - addedAt: timestamp('added_at').notNull().defaultNow(), -}, (t) => ({ - pk: unique('product_group_membership_pk').on(t.productId, t.groupId), -})); - -export const homeBanners = mf.table('home_banners', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar('name', { length: 255 }).notNull(), - imageUrl: varchar('image_url', { length: 500 }).notNull(), - description: varchar('description', { length: 500 }), - productIds: integer('product_ids').array(), - redirectUrl: varchar('redirect_url', { length: 500 }), - serialNum: integer('serial_num'), - isActive: boolean('is_active').notNull().default(false), - createdAt: timestamp('created_at').notNull().defaultNow(), - lastUpdated: timestamp('last_updated').notNull().defaultNow(), -}); - -export const productReviews = mf.table('product_reviews', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - productId: integer('product_id').notNull().references(() => productInfo.id), - reviewBody: text('review_body').notNull(), - imageUrls: jsonb('image_urls').$defaultFn(() => []), - reviewTime: timestamp('review_time').notNull().defaultNow(), - ratings: real('ratings').notNull(), - adminResponse: text('admin_response'), - adminResponseImages: jsonb('admin_response_images').$defaultFn(() => []), -}, (t) => ({ - ratingCheck: check('rating_check', sql`${t.ratings} >= 1 AND ${t.ratings} <= 5`), -})); - -export const uploadStatusEnum = pgEnum('upload_status', ['pending', 'claimed']); - -export const staffRoleEnum = pgEnum('staff_role', ['super_admin', 'admin', 'marketer', 'delivery_staff']); - -export const staffPermissionEnum = pgEnum('staff_permission', ['crud_product', 'make_coupon', 'crud_staff_users']); - -export const uploadUrlStatus = mf.table('upload_url_status', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - createdAt: timestamp('created_at').notNull().defaultNow(), - key: varchar('key', { length: 500 }).notNull(), - status: uploadStatusEnum('status').notNull().default('pending'), -}); - -export const productTagInfo = mf.table('product_tag_info', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - tagName: varchar('tag_name', { length: 100 }).notNull().unique(), - tagDescription: varchar('tag_description', { length: 500 }), - imageUrl: varchar('image_url', { length: 500 }), - isDashboardTag: boolean('is_dashboard_tag').notNull().default(false), - relatedStores: jsonb('related_stores').$defaultFn(() => []), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const productTags = mf.table('product_tags', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - productId: integer('product_id').notNull().references(() => productInfo.id), - tagId: integer('tag_id').notNull().references(() => productTagInfo.id), - assignedAt: timestamp('assigned_at').notNull().defaultNow(), -}, (t) => ({ - unq_product_tag: unique('unique_product_tag').on(t.productId, t.tagId), -})); - -export const deliverySlotInfo = mf.table('delivery_slot_info', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - deliveryTime: timestamp('delivery_time').notNull(), - freezeTime: timestamp('freeze_time').notNull(), - isActive: boolean('is_active').notNull().default(true), - isFlash: boolean('is_flash').notNull().default(false), - isCapacityFull: boolean('is_capacity_full').notNull().default(false), - deliverySequence: jsonb('delivery_sequence').$defaultFn(() => {}), - groupIds: jsonb('group_ids').$defaultFn(() => []), -}); - -export const vendorSnippets = mf.table('vendor_snippets', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - snippetCode: varchar('snippet_code', { length: 255 }).notNull().unique(), - slotId: integer('slot_id').references(() => deliverySlotInfo.id), - isPermanent: boolean('is_permanent').notNull().default(false), - productIds: integer('product_ids').array().notNull(), - validTill: timestamp('valid_till'), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const vendorSnippetsRelations = relations(vendorSnippets, ({ one }) => ({ - slot: one(deliverySlotInfo, { fields: [vendorSnippets.slotId], references: [deliverySlotInfo.id] }), -})); - -export const productSlots = mf.table('product_slots', { - productId: integer('product_id').notNull().references(() => productInfo.id), - slotId: integer('slot_id').notNull().references(() => deliverySlotInfo.id), -}, (t) => ({ - pk: unique('product_slot_pk').on(t.productId, t.slotId), -})); - -export const specialDeals = mf.table('special_deals', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - productId: integer('product_id').notNull().references(() => productInfo.id), - quantity: numeric({ precision: 10, scale: 2 }).notNull(), - price: numeric({ precision: 10, scale: 2 }).notNull(), - validTill: timestamp('valid_till').notNull(), -}); - -export const orders = mf.table('orders', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - addressId: integer('address_id').notNull().references(() => addresses.id), - slotId: integer('slot_id').references(() => deliverySlotInfo.id), - isCod: boolean('is_cod').notNull().default(false), - isOnlinePayment: boolean('is_online_payment').notNull().default(false), - paymentInfoId: integer('payment_info_id').references(() => paymentInfoTable.id), - totalAmount: numeric('total_amount', { precision: 10, scale: 2 }).notNull(), - deliveryCharge: numeric('delivery_charge', { precision: 10, scale: 2 }).notNull().default('0'), - readableId: integer('readable_id').notNull(), - adminNotes: text('admin_notes'), - userNotes: text('user_notes'), - orderGroupId: varchar('order_group_id', { length: 255 }), - orderGroupProportion: decimal('order_group_proportion', { precision: 10, scale: 4 }), - isFlashDelivery: boolean('is_flash_delivery').notNull().default(false), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const orderItems = mf.table('order_items', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - orderId: integer('order_id').notNull().references(() => orders.id), - productId: integer('product_id').notNull().references(() => productInfo.id), - quantity: varchar('quantity', { length: 50 }).notNull(), - price: numeric({ precision: 10, scale: 2 }).notNull(), - discountedPrice: numeric('discounted_price', { precision: 10, scale: 2 }), - is_packaged: boolean('is_packaged').notNull().default(false), - is_package_verified: boolean('is_package_verified').notNull().default(false), -}); - -export const paymentStatusEnum = pgEnum('payment_status', ['pending', 'success', 'cod', 'failed']); - -export const orderStatus = mf.table('order_status', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - orderTime: timestamp('order_time').notNull().defaultNow(), - userId: integer('user_id').notNull().references(() => users.id), - orderId: integer('order_id').notNull().references(() => orders.id), - isPackaged: boolean('is_packaged').notNull().default(false), - isDelivered: boolean('is_delivered').notNull().default(false), - isCancelled: boolean('is_cancelled').notNull().default(false), - cancelReason: varchar('cancel_reason', { length: 255 }), - isCancelledByAdmin: boolean('is_cancelled_by_admin'), - paymentStatus: paymentStatusEnum('payment_state').notNull().default('pending'), - cancellationUserNotes: text('cancellation_user_notes'), - cancellationAdminNotes: text('cancellation_admin_notes'), - cancellationReviewed: boolean('cancellation_reviewed').notNull().default(false), - cancellationReviewedAt: timestamp('cancellation_reviewed_at'), - refundCouponId: integer('refund_coupon_id').references(() => coupons.id), -}); - -export const paymentInfoTable = mf.table('payment_info', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - status: varchar({ length: 50 }).notNull(), - gateway: varchar({ length: 50 }).notNull(), - orderId: varchar('order_id', { length: 500 }), - token: varchar({ length: 500 }), - merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(), - payload: jsonb('payload'), -}); - -export const payments = mf.table('payments', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - status: varchar({ length: 50 }).notNull(), - gateway: varchar({ length: 50 }).notNull(), - orderId: integer('order_id').notNull().references(() => orders.id), - token: varchar({ length: 500 }), - merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(), - payload: jsonb('payload'), -}); - -export const refunds = mf.table('refunds', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - orderId: integer('order_id').notNull().references(() => orders.id), - refundAmount: numeric('refund_amount', { precision: 10, scale: 2 }), - refundStatus: varchar('refund_status', { length: 50 }).default('none'), - merchantRefundId: varchar('merchant_refund_id', { length: 255 }), - refundProcessedAt: timestamp('refund_processed_at'), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const keyValStore = mf.table('key_val_store', { - key: varchar('key', { length: 255 }).primaryKey(), - value: jsonb('value'), -}); - -export const notifications = mf.table('notifications', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - title: varchar({ length: 255 }).notNull(), - body: varchar({ length: 512 }).notNull(), - type: varchar({ length: 50 }), - isRead: boolean('is_read').notNull().default(false), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const productCategories = mf.table('product_categories', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }).notNull(), - description: varchar({ length: 500 }), -}); - -export const cartItems = mf.table('cart_items', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - productId: integer('product_id').notNull().references(() => productInfo.id), - quantity: numeric({ precision: 10, scale: 2 }).notNull(), - addedAt: timestamp('added_at').notNull().defaultNow(), -}, (t) => ({ - unq_user_product: unique('unique_user_product').on(t.userId, t.productId), -})); - -export const complaints = mf.table('complaints', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - orderId: integer('order_id').references(() => orders.id), - complaintBody: varchar('complaint_body', { length: 1000 }).notNull(), - images: jsonb('images'), - response: varchar('response', { length: 1000 }), - isResolved: boolean('is_resolved').notNull().default(false), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const coupons = mf.table('coupons', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - couponCode: varchar('coupon_code', { length: 50 }).notNull().unique('unique_coupon_code'), - isUserBased: boolean('is_user_based').notNull().default(false), - discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }), - flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }), - minOrder: numeric('min_order', { precision: 10, scale: 2 }), - productIds: jsonb('product_ids'), - createdBy: integer('created_by').references(() => staffUsers.id), - maxValue: numeric('max_value', { precision: 10, scale: 2 }), - isApplyForAll: boolean('is_apply_for_all').notNull().default(false), - validTill: timestamp('valid_till'), - maxLimitForUser: integer('max_limit_for_user'), - isInvalidated: boolean('is_invalidated').notNull().default(false), - exclusiveApply: boolean('exclusive_apply').notNull().default(false), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const couponUsage = mf.table('coupon_usage', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - couponId: integer('coupon_id').notNull().references(() => coupons.id), - orderId: integer('order_id').references(() => orders.id), - orderItemId: integer('order_item_id').references(() => orderItems.id), - usedAt: timestamp('used_at').notNull().defaultNow(), -}); - -export const couponApplicableUsers = mf.table('coupon_applicable_users', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - couponId: integer('coupon_id').notNull().references(() => coupons.id), - userId: integer('user_id').notNull().references(() => users.id), -}, (t) => ({ - unq_coupon_user: unique('unique_coupon_user').on(t.couponId, t.userId), -})); - -export const couponApplicableProducts = mf.table('coupon_applicable_products', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - couponId: integer('coupon_id').notNull().references(() => coupons.id), - productId: integer('product_id').notNull().references(() => productInfo.id), -}, (t) => ({ - unq_coupon_product: unique('unique_coupon_product').on(t.couponId, t.productId), -})); - -export const userIncidents = mf.table('user_incidents', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - userId: integer('user_id').notNull().references(() => users.id), - orderId: integer('order_id').references(() => orders.id), - dateAdded: timestamp('date_added').notNull().defaultNow(), - adminComment: text('admin_comment'), - addedBy: integer('added_by').references(() => staffUsers.id), - negativityScore: integer('negativity_score'), -}); - -export const reservedCoupons = mf.table('reserved_coupons', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - secretCode: varchar('secret_code', { length: 50 }).notNull().unique(), - couponCode: varchar('coupon_code', { length: 50 }).notNull(), - discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }), - flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }), - minOrder: numeric('min_order', { precision: 10, scale: 2 }), - productIds: jsonb('product_ids'), - maxValue: numeric('max_value', { precision: 10, scale: 2 }), - validTill: timestamp('valid_till'), - maxLimitForUser: integer('max_limit_for_user'), - exclusiveApply: boolean('exclusive_apply').notNull().default(false), - isRedeemed: boolean('is_redeemed').notNull().default(false), - redeemedBy: integer('redeemed_by').references(() => users.id), - redeemedAt: timestamp('redeemed_at'), - createdBy: integer('created_by').notNull().references(() => staffUsers.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}, (t) => ({ - unq_secret_code: unique('unique_secret_code').on(t.secretCode), -})); - -export const notifCreds = mf.table('notif_creds', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - token: varchar({ length: 500 }).notNull().unique(), - addedAt: timestamp('added_at').notNull().defaultNow(), - userId: integer('user_id').notNull().references(() => users.id), - lastVerified: timestamp('last_verified'), -}); - -export const unloggedUserTokens = mf.table('unlogged_user_tokens', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - token: varchar({ length: 500 }).notNull().unique(), - addedAt: timestamp('added_at').notNull().defaultNow(), - lastVerified: timestamp('last_verified'), -}); - -export const userNotifications = mf.table('user_notifications', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - title: varchar('title', { length: 255 }).notNull(), - imageUrl: varchar('image_url', { length: 500 }), - createdAt: timestamp('created_at').notNull().defaultNow(), - body: text('body').notNull(), - applicableUsers: jsonb('applicable_users'), -}); - -export const staffRoles = mf.table('staff_roles', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - roleName: staffRoleEnum('role_name').notNull(), - createdAt: timestamp('created_at').notNull().defaultNow(), -}, (t) => ({ - unq_role_name: unique('unique_role_name').on(t.roleName), -})); - -export const staffPermissions = mf.table('staff_permissions', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - permissionName: staffPermissionEnum('permission_name').notNull(), - createdAt: timestamp('created_at').notNull().defaultNow(), -}, (t) => ({ - unq_permission_name: unique('unique_permission_name').on(t.permissionName), -})); - -export const staffRolePermissions = mf.table('staff_role_permissions', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - staffRoleId: integer('staff_role_id').notNull().references(() => staffRoles.id), - staffPermissionId: integer('staff_permission_id').notNull().references(() => staffPermissions.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}, (t) => ({ - unq_role_permission: unique('unique_role_permission').on(t.staffRoleId, t.staffPermissionId), -})); - -// Relations -export const usersRelations = relations(users, ({ many, one }) => ({ - addresses: many(addresses), - orders: many(orders), - notifications: many(notifications), - cartItems: many(cartItems), - userCreds: one(userCreds), - coupons: many(coupons), - couponUsages: many(couponUsage), - applicableCoupons: many(couponApplicableUsers), - userDetails: one(userDetails), - notifCreds: many(notifCreds), - userIncidents: many(userIncidents), -})); - -export const userCredsRelations = relations(userCreds, ({ one }) => ({ - user: one(users, { fields: [userCreds.userId], references: [users.id] }), -})); - -export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ - role: one(staffRoles, { fields: [staffUsers.staffRoleId], references: [staffRoles.id] }), - coupons: many(coupons), - stores: many(storeInfo), -})); - -export const addressesRelations = relations(addresses, ({ one, many }) => ({ - user: one(users, { fields: [addresses.userId], references: [users.id] }), - orders: many(orders), - zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), -})); - -export const unitsRelations = relations(units, ({ many }) => ({ - products: many(productInfo), -})); - -export const productInfoRelations = relations(productInfo, ({ one, many }) => ({ - unit: one(units, { fields: [productInfo.unitId], references: [units.id] }), - store: one(storeInfo, { fields: [productInfo.storeId], references: [storeInfo.id] }), - productSlots: many(productSlots), - specialDeals: many(specialDeals), - orderItems: many(orderItems), - cartItems: many(cartItems), - tags: many(productTags), - applicableCoupons: many(couponApplicableProducts), - reviews: many(productReviews), - groups: many(productGroupMembership), -})); - -export const productTagInfoRelations = relations(productTagInfo, ({ many }) => ({ - products: many(productTags), -})); - -export const productTagsRelations = relations(productTags, ({ one }) => ({ - product: one(productInfo, { fields: [productTags.productId], references: [productInfo.id] }), - tag: one(productTagInfo, { fields: [productTags.tagId], references: [productTagInfo.id] }), -})); - -export const deliverySlotInfoRelations = relations(deliverySlotInfo, ({ many }) => ({ - productSlots: many(productSlots), - orders: many(orders), - vendorSnippets: many(vendorSnippets), -})); - -export const productSlotsRelations = relations(productSlots, ({ one }) => ({ - product: one(productInfo, { fields: [productSlots.productId], references: [productInfo.id] }), - slot: one(deliverySlotInfo, { fields: [productSlots.slotId], references: [deliverySlotInfo.id] }), -})); - -export const specialDealsRelations = relations(specialDeals, ({ one }) => ({ - product: one(productInfo, { fields: [specialDeals.productId], references: [productInfo.id] }), -})); - -export const ordersRelations = relations(orders, ({ one, many }) => ({ - user: one(users, { fields: [orders.userId], references: [users.id] }), - address: one(addresses, { fields: [orders.addressId], references: [addresses.id] }), - slot: one(deliverySlotInfo, { fields: [orders.slotId], references: [deliverySlotInfo.id] }), - orderItems: many(orderItems), - payment: one(payments), - paymentInfo: one(paymentInfoTable, { fields: [orders.paymentInfoId], references: [paymentInfoTable.id] }), - orderStatus: many(orderStatus), - refunds: many(refunds), - couponUsages: many(couponUsage), - userIncidents: many(userIncidents), -})); - -export const orderItemsRelations = relations(orderItems, ({ one }) => ({ - order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }), - product: one(productInfo, { fields: [orderItems.productId], references: [productInfo.id] }), -})); - -export const orderStatusRelations = relations(orderStatus, ({ one }) => ({ - order: one(orders, { fields: [orderStatus.orderId], references: [orders.id] }), - user: one(users, { fields: [orderStatus.userId], references: [users.id] }), - refundCoupon: one(coupons, { fields: [orderStatus.refundCouponId], references: [coupons.id] }), -})); - -export const paymentInfoRelations = relations(paymentInfoTable, ({ one }) => ({ - order: one(orders, { fields: [paymentInfoTable.id], references: [orders.paymentInfoId] }), -})); - -export const paymentsRelations = relations(payments, ({ one }) => ({ - order: one(orders, { fields: [payments.orderId], references: [orders.id] }), -})); - -export const refundsRelations = relations(refunds, ({ one }) => ({ - order: one(orders, { fields: [refunds.orderId], references: [orders.id] }), -})); - -export const notificationsRelations = relations(notifications, ({ one }) => ({ - user: one(users, { fields: [notifications.userId], references: [users.id] }), -})); - -export const productCategoriesRelations = relations(productCategories, ({}) => ({})); - -export const cartItemsRelations = relations(cartItems, ({ one }) => ({ - user: one(users, { fields: [cartItems.userId], references: [users.id] }), - product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }), -})); - -export const complaintsRelations = relations(complaints, ({ one }) => ({ - user: one(users, { fields: [complaints.userId], references: [users.id] }), - order: one(orders, { fields: [complaints.orderId], references: [orders.id] }), -})); - -export const couponsRelations = relations(coupons, ({ one, many }) => ({ - creator: one(staffUsers, { fields: [coupons.createdBy], references: [staffUsers.id] }), - usages: many(couponUsage), - applicableUsers: many(couponApplicableUsers), - applicableProducts: many(couponApplicableProducts), -})); - -export const couponUsageRelations = relations(couponUsage, ({ one }) => ({ - user: one(users, { fields: [couponUsage.userId], references: [users.id] }), - coupon: one(coupons, { fields: [couponUsage.couponId], references: [coupons.id] }), - order: one(orders, { fields: [couponUsage.orderId], references: [orders.id] }), - orderItem: one(orderItems, { fields: [couponUsage.orderItemId], references: [orderItems.id] }), -})); - -export const userDetailsRelations = relations(userDetails, ({ one }) => ({ - user: one(users, { fields: [userDetails.userId], references: [users.id] }), -})); - -export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ - user: one(users, { fields: [notifCreds.userId], references: [users.id] }), -})); - -export const userNotificationsRelations = relations(userNotifications, ({}) => ({ - // No relations needed for now -})); - -export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ - owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), - products: many(productInfo), -})); - -export const couponApplicableUsersRelations = relations(couponApplicableUsers, ({ one }) => ({ - coupon: one(coupons, { fields: [couponApplicableUsers.couponId], references: [coupons.id] }), - user: one(users, { fields: [couponApplicableUsers.userId], references: [users.id] }), -})); - -export const couponApplicableProductsRelations = relations(couponApplicableProducts, ({ one }) => ({ - coupon: one(coupons, { fields: [couponApplicableProducts.couponId], references: [coupons.id] }), - product: one(productInfo, { fields: [couponApplicableProducts.productId], references: [productInfo.id] }), -})); - -export const reservedCouponsRelations = relations(reservedCoupons, ({ one }) => ({ - redeemedUser: one(users, { fields: [reservedCoupons.redeemedBy], references: [users.id] }), - creator: one(staffUsers, { fields: [reservedCoupons.createdBy], references: [staffUsers.id] }), -})); - -export const productReviewsRelations = relations(productReviews, ({ one }) => ({ - user: one(users, { fields: [productReviews.userId], references: [users.id] }), - product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), -})); - -export const addressZonesRelations = relations(addressZones, ({ many }) => ({ - addresses: many(addresses), - areas: many(addressAreas), -})); - -export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ - zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), -})); - -export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ - memberships: many(productGroupMembership), -})); - -export const productGroupMembershipRelations = relations(productGroupMembership, ({ one }) => ({ - product: one(productInfo, { fields: [productGroupMembership.productId], references: [productInfo.id] }), - group: one(productGroupInfo, { fields: [productGroupMembership.groupId], references: [productGroupInfo.id] }), -})); - -export const homeBannersRelations = relations(homeBanners, ({}) => ({ - // Relations for productIds array would be more complex, skipping for now -})); - -export const staffRolesRelations = relations(staffRoles, ({ many }) => ({ - staffUsers: many(staffUsers), - rolePermissions: many(staffRolePermissions), -})); - -export const staffPermissionsRelations = relations(staffPermissions, ({ many }) => ({ - rolePermissions: many(staffRolePermissions), -})); - -export const staffRolePermissionsRelations = relations(staffRolePermissions, ({ one }) => ({ - role: one(staffRoles, { fields: [staffRolePermissions.staffRoleId], references: [staffRoles.id] }), - permission: one(staffPermissions, { fields: [staffRolePermissions.staffPermissionId], references: [staffPermissions.id] }), -})); - -export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({ - user: one(users, { fields: [userIncidents.userId], references: [users.id] }), - order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }), - addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }), -})); - -export const productAvailabilitySchedulesRelations = relations(productAvailabilitySchedules, ({}) => ({ -})); +export * from '@/db-helper-postgres/db/schema' diff --git a/apps/backend/src/db/schema-sqlite.ts b/apps/backend/src/db/schema-sqlite.ts index 568653d..154451a 100644 --- a/apps/backend/src/db/schema-sqlite.ts +++ b/apps/backend/src/db/schema-sqlite.ts @@ -1,735 +1 @@ -import { - sqliteTable, - integer, - text, - real, - unique, - check, -} from 'drizzle-orm/sqlite-core' -import { relations, sql } from 'drizzle-orm' - -const epochSeconds = sql`(strftime('%s','now'))` - -const sqliteEnum = ( - _name: string, - values: T -) => (columnName: string) => text(columnName, { enum: values }) - -export const users = sqliteTable('users', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name'), - email: text('email'), - mobile: text('mobile'), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - unq_email: unique('unique_email').on(t.email), -})) - -export const userDetails = sqliteTable('user_details', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id).unique(), - bio: text('bio'), - dateOfBirth: integer('date_of_birth', { mode: 'timestamp' }), - gender: text('gender'), - occupation: text('occupation'), - profileImage: text('profile_image'), - isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const userCreds = sqliteTable('user_creds', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - userPassword: text('user_password').notNull(), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const addressZones = sqliteTable('address_zones', { - id: integer('id').primaryKey({ autoIncrement: true }), - zoneName: text('zone_name').notNull(), - addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const addresses = sqliteTable('addresses', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - name: text('name').notNull(), - phone: text('phone').notNull(), - addressLine1: text('address_line1').notNull(), - addressLine2: text('address_line2'), - city: text('city').notNull(), - state: text('state').notNull(), - pincode: text('pincode').notNull(), - isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false), - latitude: real('latitude'), - longitude: real('longitude'), - googleMapsUrl: text('google_maps_url'), - adminLatitude: real('admin_latitude'), - adminLongitude: real('admin_longitude'), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const addressAreas = sqliteTable('address_areas', { - id: integer('id').primaryKey({ autoIncrement: true }), - placeName: text('place_name').notNull(), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const staffRoleEnum = sqliteEnum('staff_role', [ - 'super_admin', - 'admin', - 'marketer', - 'delivery_staff', -]) - -export const staffPermissionEnum = sqliteEnum('staff_permission', [ - 'crud_product', - 'make_coupon', - 'crud_staff_users', -]) - -export const staffRoles = sqliteTable('staff_roles', { - id: integer('id').primaryKey({ autoIncrement: true }), - roleName: staffRoleEnum('role_name').notNull(), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - unq_role_name: unique('unique_role_name').on(t.roleName), -})) - -export const staffPermissions = sqliteTable('staff_permissions', { - id: integer('id').primaryKey({ autoIncrement: true }), - permissionName: staffPermissionEnum('permission_name').notNull(), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - unq_permission_name: unique('unique_permission_name').on(t.permissionName), -})) - -export const staffRolePermissions = sqliteTable('staff_role_permissions', { - id: integer('id').primaryKey({ autoIncrement: true }), - staffRoleId: integer('staff_role_id').notNull().references(() => staffRoles.id), - staffPermissionId: integer('staff_permission_id').notNull().references(() => staffPermissions.id), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - unq_role_permission: unique('unique_role_permission').on( - t.staffRoleId, - t.staffPermissionId - ), -})) - -export const staffUsers = sqliteTable('staff_users', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - password: text('password').notNull(), - staffRoleId: integer('staff_role_id').references(() => staffRoles.id), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const storeInfo = sqliteTable('store_info', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - description: text('description'), - imageUrl: text('image_url'), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - owner: integer('owner').notNull().references(() => staffUsers.id), -}) - -export const units = sqliteTable('units', { - id: integer('id').primaryKey({ autoIncrement: true }), - shortNotation: text('short_notation').notNull(), - fullName: text('full_name').notNull(), -}, (t) => ({ - unq_short_notation: unique('unique_short_notation').on(t.shortNotation), -})) - -export const productAvailabilityActionEnum = sqliteEnum( - 'product_availability_action', - ['in', 'out'] -) - -export const productInfo = sqliteTable('product_info', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - shortDescription: text('short_description'), - longDescription: text('long_description'), - unitId: integer('unit_id').notNull().references(() => units.id), - price: text('price').notNull(), - marketPrice: text('market_price'), - images: text('images'), - isOutOfStock: integer('is_out_of_stock', { mode: 'boolean' }).notNull().default(false), - isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false), - isFlashAvailable: integer('is_flash_available', { mode: 'boolean' }).notNull().default(false), - flashPrice: text('flash_price'), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - incrementStep: real('increment_step').notNull().default(1), - productQuantity: real('product_quantity').notNull().default(1), - storeId: integer('store_id').references(() => storeInfo.id), - scheduledAvailability: integer('scheduled_availability', { mode: 'boolean' }).notNull().default(true), -}) - -export const productAvailabilitySchedules = sqliteTable('product_availability_schedules', { - id: integer('id').primaryKey({ autoIncrement: true }), - time: text('time').notNull(), - scheduleName: text('schedule_name').notNull().unique(), - action: productAvailabilityActionEnum('action').notNull(), - productIds: text('product_ids').notNull().default('[]'), - groupIds: text('group_ids').notNull().default('[]'), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - lastUpdated: integer('last_updated', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const productGroupInfo = sqliteTable('product_group_info', { - id: integer('id').primaryKey({ autoIncrement: true }), - groupName: text('group_name').notNull(), - description: text('description'), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const productGroupMembership = sqliteTable('product_group_membership', { - productId: integer('product_id').notNull().references(() => productInfo.id), - groupId: integer('group_id').notNull().references(() => productGroupInfo.id), - addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - pk: unique('product_group_membership_pk').on(t.productId, t.groupId), -})) - -export const homeBanners = sqliteTable('home_banners', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - imageUrl: text('image_url').notNull(), - description: text('description'), - productIds: text('product_ids'), - redirectUrl: text('redirect_url'), - serialNum: integer('serial_num'), - isActive: integer('is_active', { mode: 'boolean' }).notNull().default(false), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - lastUpdated: integer('last_updated', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const productReviews = sqliteTable('product_reviews', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - productId: integer('product_id').notNull().references(() => productInfo.id), - reviewBody: text('review_body').notNull(), - imageUrls: text('image_urls').default('[]'), - reviewTime: integer('review_time', { mode: 'timestamp' }).notNull().default(epochSeconds), - ratings: real('ratings').notNull(), - adminResponse: text('admin_response'), - adminResponseImages: text('admin_response_images').default('[]'), -}, (t) => ({ - ratingCheck: check('rating_check', sql`${t.ratings} >= 1 AND ${t.ratings} <= 5`), -})) - -export const uploadStatusEnum = sqliteEnum('upload_status', ['pending', 'claimed']) - -export const uploadUrlStatus = sqliteTable('upload_url_status', { - id: integer('id').primaryKey({ autoIncrement: true }), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - key: text('key').notNull(), - status: uploadStatusEnum('status').notNull().default('pending'), -}) - -export const productTagInfo = sqliteTable('product_tag_info', { - id: integer('id').primaryKey({ autoIncrement: true }), - tagName: text('tag_name').notNull().unique(), - tagDescription: text('tag_description'), - imageUrl: text('image_url'), - isDashboardTag: integer('is_dashboard_tag', { mode: 'boolean' }).notNull().default(false), - relatedStores: text('related_stores').default('[]'), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const productTags = sqliteTable('product_tags', { - id: integer('id').primaryKey({ autoIncrement: true }), - productId: integer('product_id').notNull().references(() => productInfo.id), - tagId: integer('tag_id').notNull().references(() => productTagInfo.id), - assignedAt: integer('assigned_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - unq_product_tag: unique('unique_product_tag').on(t.productId, t.tagId), -})) - -export const deliverySlotInfo = sqliteTable('delivery_slot_info', { - id: integer('id').primaryKey({ autoIncrement: true }), - deliveryTime: integer('delivery_time', { mode: 'timestamp' }).notNull(), - freezeTime: integer('freeze_time', { mode: 'timestamp' }).notNull(), - isActive: integer('is_active', { mode: 'boolean' }).notNull().default(true), - isFlash: integer('is_flash', { mode: 'boolean' }).notNull().default(false), - isCapacityFull: integer('is_capacity_full', { mode: 'boolean' }).notNull().default(false), - deliverySequence: text('delivery_sequence').default('{}'), - groupIds: text('group_ids').default('[]'), -}) - -export const vendorSnippets = sqliteTable('vendor_snippets', { - id: integer('id').primaryKey({ autoIncrement: true }), - snippetCode: text('snippet_code').notNull().unique(), - slotId: integer('slot_id').references(() => deliverySlotInfo.id), - isPermanent: integer('is_permanent', { mode: 'boolean' }).notNull().default(false), - productIds: text('product_ids').notNull(), - validTill: integer('valid_till', { mode: 'timestamp' }), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const productSlots = sqliteTable('product_slots', { - productId: integer('product_id').notNull().references(() => productInfo.id), - slotId: integer('slot_id').notNull().references(() => deliverySlotInfo.id), -}, (t) => ({ - pk: unique('product_slot_pk').on(t.productId, t.slotId), -})) - -export const specialDeals = sqliteTable('special_deals', { - id: integer('id').primaryKey({ autoIncrement: true }), - productId: integer('product_id').notNull().references(() => productInfo.id), - quantity: text('quantity').notNull(), - price: text('price').notNull(), - validTill: integer('valid_till', { mode: 'timestamp' }).notNull(), -}) - -export const paymentInfoTable = sqliteTable('payment_info', { - id: integer('id').primaryKey({ autoIncrement: true }), - status: text('status').notNull(), - gateway: text('gateway').notNull(), - orderId: text('order_id'), - token: text('token'), - merchantOrderId: text('merchant_order_id').notNull().unique(), - payload: text('payload'), -}) - -export const orders = sqliteTable('orders', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - addressId: integer('address_id').notNull().references(() => addresses.id), - slotId: integer('slot_id').references(() => deliverySlotInfo.id), - isCod: integer('is_cod', { mode: 'boolean' }).notNull().default(false), - isOnlinePayment: integer('is_online_payment', { mode: 'boolean' }).notNull().default(false), - paymentInfoId: integer('payment_info_id').references(() => paymentInfoTable.id), - totalAmount: text('total_amount').notNull(), - deliveryCharge: text('delivery_charge').notNull().default('0'), - readableId: integer('readable_id').notNull(), - adminNotes: text('admin_notes'), - userNotes: text('user_notes'), - orderGroupId: text('order_group_id'), - orderGroupProportion: text('order_group_proportion'), - isFlashDelivery: integer('is_flash_delivery', { mode: 'boolean' }).notNull().default(false), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const orderItems = sqliteTable('order_items', { - id: integer('id').primaryKey({ autoIncrement: true }), - orderId: integer('order_id').notNull().references(() => orders.id), - productId: integer('product_id').notNull().references(() => productInfo.id), - quantity: text('quantity').notNull(), - price: text('price').notNull(), - discountedPrice: text('discounted_price'), - is_packaged: integer('is_packaged', { mode: 'boolean' }).notNull().default(false), - is_package_verified: integer('is_package_verified', { mode: 'boolean' }).notNull().default(false), -}) - -export const paymentStatusEnum = sqliteEnum('payment_status', [ - 'pending', - 'success', - 'cod', - 'failed', -]) - -export const orderStatus = sqliteTable('order_status', { - id: integer('id').primaryKey({ autoIncrement: true }), - orderTime: integer('order_time', { mode: 'timestamp' }).notNull().default(epochSeconds), - userId: integer('user_id').notNull().references(() => users.id), - orderId: integer('order_id').notNull().references(() => orders.id), - isPackaged: integer('is_packaged', { mode: 'boolean' }).notNull().default(false), - isDelivered: integer('is_delivered', { mode: 'boolean' }).notNull().default(false), - isCancelled: integer('is_cancelled', { mode: 'boolean' }).notNull().default(false), - cancelReason: text('cancel_reason'), - isCancelledByAdmin: integer('is_cancelled_by_admin', { mode: 'boolean' }), - paymentStatus: paymentStatusEnum('payment_state').notNull().default('pending'), - cancellationUserNotes: text('cancellation_user_notes'), - cancellationAdminNotes: text('cancellation_admin_notes'), - cancellationReviewed: integer('cancellation_reviewed', { mode: 'boolean' }).notNull().default(false), - cancellationReviewedAt: integer('cancellation_reviewed_at', { mode: 'timestamp' }), - refundCouponId: integer('refund_coupon_id').references(() => coupons.id), -}) - -export const payments = sqliteTable('payments', { - id: integer('id').primaryKey({ autoIncrement: true }), - status: text('status').notNull(), - gateway: text('gateway').notNull(), - orderId: integer('order_id').notNull().references(() => orders.id), - token: text('token'), - merchantOrderId: text('merchant_order_id').notNull().unique(), - payload: text('payload'), -}) - -export const refunds = sqliteTable('refunds', { - id: integer('id').primaryKey({ autoIncrement: true }), - orderId: integer('order_id').notNull().references(() => orders.id), - refundAmount: text('refund_amount'), - refundStatus: text('refund_status').default('none'), - merchantRefundId: text('merchant_refund_id'), - refundProcessedAt: integer('refund_processed_at', { mode: 'timestamp' }), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const keyValStore = sqliteTable('key_val_store', { - key: text('key').primaryKey(), - value: text('value'), -}) - -export const notifications = sqliteTable('notifications', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - title: text('title').notNull(), - body: text('body').notNull(), - type: text('type'), - isRead: integer('is_read', { mode: 'boolean' }).notNull().default(false), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const productCategories = sqliteTable('product_categories', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - description: text('description'), -}) - -export const cartItems = sqliteTable('cart_items', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - productId: integer('product_id').notNull().references(() => productInfo.id), - quantity: text('quantity').notNull(), - addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - unq_user_product: unique('unique_user_product').on(t.userId, t.productId), -})) - -export const complaints = sqliteTable('complaints', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - orderId: integer('order_id').references(() => orders.id), - complaintBody: text('complaint_body').notNull(), - images: text('images'), - response: text('response'), - isResolved: integer('is_resolved', { mode: 'boolean' }).notNull().default(false), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const coupons = sqliteTable('coupons', { - id: integer('id').primaryKey({ autoIncrement: true }), - couponCode: text('coupon_code').notNull().unique('unique_coupon_code'), - isUserBased: integer('is_user_based', { mode: 'boolean' }).notNull().default(false), - discountPercent: text('discount_percent'), - flatDiscount: text('flat_discount'), - minOrder: text('min_order'), - productIds: text('product_ids'), - createdBy: integer('created_by').references(() => staffUsers.id), - maxValue: text('max_value'), - isApplyForAll: integer('is_apply_for_all', { mode: 'boolean' }).notNull().default(false), - validTill: integer('valid_till', { mode: 'timestamp' }), - maxLimitForUser: integer('max_limit_for_user'), - isInvalidated: integer('is_invalidated', { mode: 'boolean' }).notNull().default(false), - exclusiveApply: integer('exclusive_apply', { mode: 'boolean' }).notNull().default(false), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const couponUsage = sqliteTable('coupon_usage', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - couponId: integer('coupon_id').notNull().references(() => coupons.id), - orderId: integer('order_id').references(() => orders.id), - orderItemId: integer('order_item_id').references(() => orderItems.id), - usedAt: integer('used_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}) - -export const couponApplicableUsers = sqliteTable('coupon_applicable_users', { - id: integer('id').primaryKey({ autoIncrement: true }), - couponId: integer('coupon_id').notNull().references(() => coupons.id), - userId: integer('user_id').notNull().references(() => users.id), -}, (t) => ({ - unq_coupon_user: unique('unique_coupon_user').on(t.couponId, t.userId), -})) - -export const couponApplicableProducts = sqliteTable('coupon_applicable_products', { - id: integer('id').primaryKey({ autoIncrement: true }), - couponId: integer('coupon_id').notNull().references(() => coupons.id), - productId: integer('product_id').notNull().references(() => productInfo.id), -}, (t) => ({ - unq_coupon_product: unique('unique_coupon_product').on(t.couponId, t.productId), -})) - -export const userIncidents = sqliteTable('user_incidents', { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id').notNull().references(() => users.id), - orderId: integer('order_id').references(() => orders.id), - dateAdded: integer('date_added', { mode: 'timestamp' }).notNull().default(epochSeconds), - adminComment: text('admin_comment'), - addedBy: integer('added_by').references(() => staffUsers.id), - negativityScore: integer('negativity_score'), -}) - -export const reservedCoupons = sqliteTable('reserved_coupons', { - id: integer('id').primaryKey({ autoIncrement: true }), - secretCode: text('secret_code').notNull().unique(), - couponCode: text('coupon_code').notNull(), - discountPercent: text('discount_percent'), - flatDiscount: text('flat_discount'), - minOrder: text('min_order'), - productIds: text('product_ids'), - maxValue: text('max_value'), - validTill: integer('valid_till', { mode: 'timestamp' }), - maxLimitForUser: integer('max_limit_for_user'), - exclusiveApply: integer('exclusive_apply', { mode: 'boolean' }).notNull().default(false), - isRedeemed: integer('is_redeemed', { mode: 'boolean' }).notNull().default(false), - redeemedBy: integer('redeemed_by').references(() => users.id), - redeemedAt: integer('redeemed_at', { mode: 'timestamp' }), - createdBy: integer('created_by').notNull().references(() => staffUsers.id), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), -}, (t) => ({ - unq_secret_code: unique('unique_secret_code').on(t.secretCode), -})) - -export const notifCreds = sqliteTable('notif_creds', { - id: integer('id').primaryKey({ autoIncrement: true }), - token: text('token').notNull().unique(), - addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - userId: integer('user_id').notNull().references(() => users.id), - lastVerified: integer('last_verified', { mode: 'timestamp' }), -}) - -export const unloggedUserTokens = sqliteTable('unlogged_user_tokens', { - id: integer('id').primaryKey({ autoIncrement: true }), - token: text('token').notNull().unique(), - addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - lastVerified: integer('last_verified', { mode: 'timestamp' }), -}) - -export const userNotifications = sqliteTable('user_notifications', { - id: integer('id').primaryKey({ autoIncrement: true }), - title: text('title').notNull(), - imageUrl: text('image_url'), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), - body: text('body').notNull(), - applicableUsers: text('applicable_users'), -}) - -export const uploadUrlStatusRelations = relations(uploadUrlStatus, ({}) => ({})) - -export const userCredsRelations = relations(userCreds, ({ one }) => ({ - user: one(users, { fields: [userCreds.userId], references: [users.id] }), -})) - -export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ - role: one(staffRoles, { fields: [staffUsers.staffRoleId], references: [staffRoles.id] }), - coupons: many(coupons), - stores: many(storeInfo), -})) - -export const addressesRelations = relations(addresses, ({ one, many }) => ({ - user: one(users, { fields: [addresses.userId], references: [users.id] }), - orders: many(orders), - zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), -})) - -export const unitsRelations = relations(units, ({ many }) => ({ - products: many(productInfo), -})) - -export const productInfoRelations = relations(productInfo, ({ one, many }) => ({ - unit: one(units, { fields: [productInfo.unitId], references: [units.id] }), - store: one(storeInfo, { fields: [productInfo.storeId], references: [storeInfo.id] }), - productSlots: many(productSlots), - specialDeals: many(specialDeals), - orderItems: many(orderItems), - cartItems: many(cartItems), - tags: many(productTags), - applicableCoupons: many(couponApplicableProducts), - reviews: many(productReviews), - groups: many(productGroupMembership), -})) - -export const productTagInfoRelations = relations(productTagInfo, ({ many }) => ({ - products: many(productTags), -})) - -export const productTagsRelations = relations(productTags, ({ one }) => ({ - product: one(productInfo, { fields: [productTags.productId], references: [productInfo.id] }), - tag: one(productTagInfo, { fields: [productTags.tagId], references: [productTagInfo.id] }), -})) - -export const deliverySlotInfoRelations = relations(deliverySlotInfo, ({ many }) => ({ - productSlots: many(productSlots), - orders: many(orders), - vendorSnippets: many(vendorSnippets), -})) - -export const vendorSnippetsRelations = relations(vendorSnippets, ({ one }) => ({ - slot: one(deliverySlotInfo, { fields: [vendorSnippets.slotId], references: [deliverySlotInfo.id] }), -})) - -export const productSlotsRelations = relations(productSlots, ({ one }) => ({ - product: one(productInfo, { fields: [productSlots.productId], references: [productInfo.id] }), - slot: one(deliverySlotInfo, { fields: [productSlots.slotId], references: [deliverySlotInfo.id] }), -})) - -export const specialDealsRelations = relations(specialDeals, ({ one }) => ({ - product: one(productInfo, { fields: [specialDeals.productId], references: [productInfo.id] }), -})) - -export const ordersRelations = relations(orders, ({ one, many }) => ({ - user: one(users, { fields: [orders.userId], references: [users.id] }), - address: one(addresses, { fields: [orders.addressId], references: [addresses.id] }), - slot: one(deliverySlotInfo, { fields: [orders.slotId], references: [deliverySlotInfo.id] }), - orderItems: many(orderItems), - payment: one(payments), - paymentInfo: one(paymentInfoTable, { fields: [orders.paymentInfoId], references: [paymentInfoTable.id] }), - orderStatus: many(orderStatus), - refunds: many(refunds), - couponUsages: many(couponUsage), - userIncidents: many(userIncidents), -})) - -export const orderItemsRelations = relations(orderItems, ({ one }) => ({ - order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }), - product: one(productInfo, { fields: [orderItems.productId], references: [productInfo.id] }), -})) - -export const orderStatusRelations = relations(orderStatus, ({ one }) => ({ - order: one(orders, { fields: [orderStatus.orderId], references: [orders.id] }), - user: one(users, { fields: [orderStatus.userId], references: [users.id] }), - refundCoupon: one(coupons, { fields: [orderStatus.refundCouponId], references: [coupons.id] }), -})) - -export const paymentInfoRelations = relations(paymentInfoTable, ({ one }) => ({ - order: one(orders, { fields: [paymentInfoTable.id], references: [orders.paymentInfoId] }), -})) - -export const paymentsRelations = relations(payments, ({ one }) => ({ - order: one(orders, { fields: [payments.orderId], references: [orders.id] }), -})) - -export const refundsRelations = relations(refunds, ({ one }) => ({ - order: one(orders, { fields: [refunds.orderId], references: [orders.id] }), -})) - -export const notificationsRelations = relations(notifications, ({ one }) => ({ - user: one(users, { fields: [notifications.userId], references: [users.id] }), -})) - -export const productCategoriesRelations = relations(productCategories, ({}) => ({})) - -export const cartItemsRelations = relations(cartItems, ({ one }) => ({ - user: one(users, { fields: [cartItems.userId], references: [users.id] }), - product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }), -})) - -export const complaintsRelations = relations(complaints, ({ one }) => ({ - user: one(users, { fields: [complaints.userId], references: [users.id] }), - order: one(orders, { fields: [complaints.orderId], references: [orders.id] }), -})) - -export const couponsRelations = relations(coupons, ({ one, many }) => ({ - creator: one(staffUsers, { fields: [coupons.createdBy], references: [staffUsers.id] }), - usages: many(couponUsage), - applicableUsers: many(couponApplicableUsers), - applicableProducts: many(couponApplicableProducts), -})) - -export const couponUsageRelations = relations(couponUsage, ({ one }) => ({ - user: one(users, { fields: [couponUsage.userId], references: [users.id] }), - coupon: one(coupons, { fields: [couponUsage.couponId], references: [coupons.id] }), - order: one(orders, { fields: [couponUsage.orderId], references: [orders.id] }), - orderItem: one(orderItems, { fields: [couponUsage.orderItemId], references: [orderItems.id] }), -})) - -export const userDetailsRelations = relations(userDetails, ({ one }) => ({ - user: one(users, { fields: [userDetails.userId], references: [users.id] }), -})) - -export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ - user: one(users, { fields: [notifCreds.userId], references: [users.id] }), -})) - -export const userNotificationsRelations = relations(userNotifications, ({}) => ({})) - -export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ - owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), - products: many(productInfo), -})) - -export const couponApplicableUsersRelations = relations(couponApplicableUsers, ({ one }) => ({ - coupon: one(coupons, { fields: [couponApplicableUsers.couponId], references: [coupons.id] }), - user: one(users, { fields: [couponApplicableUsers.userId], references: [users.id] }), -})) - -export const couponApplicableProductsRelations = relations(couponApplicableProducts, ({ one }) => ({ - coupon: one(coupons, { fields: [couponApplicableProducts.couponId], references: [coupons.id] }), - product: one(productInfo, { fields: [couponApplicableProducts.productId], references: [productInfo.id] }), -})) - -export const reservedCouponsRelations = relations(reservedCoupons, ({ one }) => ({ - redeemedUser: one(users, { fields: [reservedCoupons.redeemedBy], references: [users.id] }), - creator: one(staffUsers, { fields: [reservedCoupons.createdBy], references: [staffUsers.id] }), -})) - -export const productReviewsRelations = relations(productReviews, ({ one }) => ({ - user: one(users, { fields: [productReviews.userId], references: [users.id] }), - product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), -})) - -export const addressZonesRelations = relations(addressZones, ({ many }) => ({ - addresses: many(addresses), - areas: many(addressAreas), -})) - -export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ - zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), -})) - -export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ - memberships: many(productGroupMembership), -})) - -export const productGroupMembershipRelations = relations(productGroupMembership, ({ one }) => ({ - product: one(productInfo, { fields: [productGroupMembership.productId], references: [productInfo.id] }), - group: one(productGroupInfo, { fields: [productGroupMembership.groupId], references: [productGroupInfo.id] }), -})) - -export const homeBannersRelations = relations(homeBanners, ({}) => ({})) - -export const staffRolesRelations = relations(staffRoles, ({ many }) => ({ - staffUsers: many(staffUsers), - rolePermissions: many(staffRolePermissions), -})) - -export const staffPermissionsRelations = relations(staffPermissions, ({ many }) => ({ - rolePermissions: many(staffRolePermissions), -})) - -export const staffRolePermissionsRelations = relations(staffRolePermissions, ({ one }) => ({ - role: one(staffRoles, { fields: [staffRolePermissions.staffRoleId], references: [staffRoles.id] }), - permission: one(staffPermissions, { fields: [staffRolePermissions.staffPermissionId], references: [staffPermissions.id] }), -})) - -export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({ - user: one(users, { fields: [userIncidents.userId], references: [users.id] }), - order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }), - addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }), -})) - -export const productAvailabilitySchedulesRelations = relations( - productAvailabilitySchedules, - ({}) => ({}) -) - -export const usersRelations = relations(users, ({ many, one }) => ({ - addresses: many(addresses), - orders: many(orders), - notifications: many(notifications), - cartItems: many(cartItems), - userCreds: one(userCreds), - coupons: many(coupons), - couponUsages: many(couponUsage), - applicableCoupons: many(couponApplicableUsers), - userDetails: one(userDetails), - notifCreds: many(notifCreds), - userIncidents: many(userIncidents), -})) +export * from '@/db-helper-sqlite/db/schema' diff --git a/apps/backend/src/db/seed.ts b/apps/backend/src/db/seed.ts index f894534..9763923 100644 --- a/apps/backend/src/db/seed.ts +++ b/apps/backend/src/db/seed.ts @@ -1,138 +1,8 @@ -import { db } from "@/src/db/db_index" -import { units, productInfo, deliverySlotInfo, productSlots, keyValStore, staffRoles, staffPermissions, staffRolePermissions } from "@/src/db/schema" -import { eq } from "drizzle-orm"; -import { minOrderValue, deliveryCharge } from '@/src/lib/env-exporter' -import { CONST_KEYS } from '@/src/lib/const-keys' +import { seed as seedPostgres } from '@db-helper-postgres/db/seed' +import { seed as seedSqlite } from '@db-helper-sqlite/db/seed' -export async function seed() { - console.log("Seeding database..."); +const dialect = process.env.DB_DIALECT || DB_DIALECT_TYPE - // Seed units individually - const unitsToSeed = [ - { shortNotation: "Kg", fullName: "Kilogram" }, - { shortNotation: "L", fullName: "Litre" }, - { shortNotation: "Dz", fullName: "Dozen" }, - { shortNotation: "Pc", fullName: "Unit Piece" }, - ]; +const seedImpl = dialect === 'sqlite' ? seedSqlite : seedPostgres - for (const unit of unitsToSeed) { - const existingUnit = await db.query.units.findFirst({ - where: eq(units.shortNotation, unit.shortNotation), - }); - if (!existingUnit) { - await db.insert(units).values(unit); - } - } - - // Seed staff roles individually - const rolesToSeed = ['super_admin', 'admin', 'marketer', 'delivery_staff'] as const; - - for (const roleName of rolesToSeed) { - const existingRole = await db.query.staffRoles.findFirst({ - where: eq(staffRoles.roleName, roleName), - }); - if (!existingRole) { - await db.insert(staffRoles).values({ roleName }); - } - } - - // Seed staff permissions individually - const permissionsToSeed = ['crud_product', 'make_coupon', 'crud_staff_users'] as const; - - for (const permissionName of permissionsToSeed) { - const existingPermission = await db.query.staffPermissions.findFirst({ - where: eq(staffPermissions.permissionName, permissionName), - }); - if (!existingPermission) { - await db.insert(staffPermissions).values({ permissionName }); - } - } - - // Seed role-permission assignments - await db.transaction(async (tx) => { - // Get role IDs - const superAdminRole = await tx.query.staffRoles.findFirst({ where: eq(staffRoles.roleName, 'super_admin') }); - const adminRole = await tx.query.staffRoles.findFirst({ where: eq(staffRoles.roleName, 'admin') }); - const marketerRole = await tx.query.staffRoles.findFirst({ where: eq(staffRoles.roleName, 'marketer') }); - - // Get permission IDs - const crudProductPerm = await tx.query.staffPermissions.findFirst({ where: eq(staffPermissions.permissionName, 'crud_product') }); - const makeCouponPerm = await tx.query.staffPermissions.findFirst({ where: eq(staffPermissions.permissionName, 'make_coupon') }); - const crudStaffUsersPerm = await tx.query.staffPermissions.findFirst({ where: eq(staffPermissions.permissionName, 'crud_staff_users') }); - - // Assign all permissions to super_admin - [crudProductPerm, makeCouponPerm, crudStaffUsersPerm].forEach(async (perm) => { - if (superAdminRole && perm) { - const existingSuperAdminPerm = await tx.query.staffRolePermissions.findFirst({ - where: eq(staffRolePermissions.staffRoleId, superAdminRole.id) && eq(staffRolePermissions.staffPermissionId, perm.id), - }); - if (!existingSuperAdminPerm) { - await tx.insert(staffRolePermissions).values({ - staffRoleId: superAdminRole.id, - staffPermissionId: perm.id, - }); - } - } - }); - - // Assign all permissions to admin - [crudProductPerm, makeCouponPerm].forEach(async (perm) => { - if (adminRole && perm) { - const existingAdminPerm = await tx.query.staffRolePermissions.findFirst({ - where: eq(staffRolePermissions.staffRoleId, adminRole.id) && eq(staffRolePermissions.staffPermissionId, perm.id), - }); - if (!existingAdminPerm) { - await tx.insert(staffRolePermissions).values({ - staffRoleId: adminRole.id, - staffPermissionId: perm.id, - }); - } - } - }); - - // Assign make_coupon to marketer - if (marketerRole && makeCouponPerm) { - const existingMarketerCoupon = await tx.query.staffRolePermissions.findFirst({ - where: eq(staffRolePermissions.staffRoleId, marketerRole.id) && eq(staffRolePermissions.staffPermissionId, makeCouponPerm.id), - }); - if (!existingMarketerCoupon) { - await tx.insert(staffRolePermissions).values({ - staffRoleId: marketerRole.id, - staffPermissionId: makeCouponPerm.id, - }); - } - } - }); - - // Seed key-val store constants using CONST_KEYS - const constantsToSeed = [ - { key: CONST_KEYS.readableOrderId, value: 0 }, - { key: CONST_KEYS.minRegularOrderValue, value: minOrderValue }, - { key: CONST_KEYS.freeDeliveryThreshold, value: minOrderValue }, - { key: CONST_KEYS.deliveryCharge, value: deliveryCharge }, - { key: CONST_KEYS.flashFreeDeliveryThreshold, value: 500 }, - { key: CONST_KEYS.flashDeliveryCharge, value: 69 }, - { key: CONST_KEYS.popularItems, value: [] }, - { key: CONST_KEYS.allItemsOrder, value: [] }, - { key: CONST_KEYS.versionNum, value: '1.1.0' }, - { key: CONST_KEYS.playStoreUrl, value: 'https://play.google.com/store/apps/details?id=in.freshyo.app' }, - { key: CONST_KEYS.appStoreUrl, value: 'https://apps.apple.com/in/app/freshyo/id6756889077' }, - { key: CONST_KEYS.isFlashDeliveryEnabled, value: false }, - { key: CONST_KEYS.supportMobile, value: '8688182552' }, - { key: CONST_KEYS.supportEmail, value: 'qushammohd@gmail.com' }, - ]; - - for (const constant of constantsToSeed) { - const existing = await db.query.keyValStore.findFirst({ - where: eq(keyValStore.key, constant.key), - }); - if (!existing) { - await db.insert(keyValStore).values({ - key: constant.key, - value: constant.value, - }); - } - } - - console.log("Seeding completed."); -} +export const seed = async () => seedImpl() diff --git a/apps/backend/src/db/sqlite-casts.ts b/apps/backend/src/db/sqlite-casts.ts index e59d9cf..f92b8b5 100644 --- a/apps/backend/src/db/sqlite-casts.ts +++ b/apps/backend/src/db/sqlite-casts.ts @@ -1,34 +1 @@ -export const parseJsonValue = (value: unknown, fallback: T): T => { - if (value === null || value === undefined) return fallback - if (typeof value === 'string') { - try { - return JSON.parse(value) as T - } catch { - return fallback - } - } - return value as T -} - -export const parseNumberArray = (value: unknown): number[] => { - const parsed = parseJsonValue(value, []) - return parsed - .map((item) => Number(item)) - .filter((item) => !Number.isNaN(item)) -} - -export const toJsonString = (value: unknown, fallback: string): string => { - if (value === null || value === undefined) return fallback - if (typeof value === 'string') return value - return JSON.stringify(value) -} - -export const toEpochSeconds = (value: Date | number): number => { - if (typeof value === 'number') return value - return Math.floor(value.getTime() / 1000) -} - -export const fromEpochSeconds = (value: number | null | undefined): Date | null => { - if (value === null || value === undefined) return null - return new Date(value * 1000) -} +export * from '@/db-helper-sqlite/db/sqlite-casts' diff --git a/apps/backend/src/db/types.ts b/apps/backend/src/db/types.ts index 64d42de..4051042 100755 --- a/apps/backend/src/db/types.ts +++ b/apps/backend/src/db/types.ts @@ -1,47 +1,58 @@ -import type { InferSelectModel } from "drizzle-orm"; import type { - users, - addresses, - units, - productInfo, - deliverySlotInfo, - productSlots, - specialDeals, - orders, - orderItems, - payments, - notifications, - productCategories, - cartItems, - coupons, -} from "@/src/db/schema"; + User as PostgresUser, + Address as PostgresAddress, + Unit as PostgresUnit, + ProductInfo as PostgresProductInfo, + DeliverySlotInfo as PostgresDeliverySlotInfo, + ProductSlot as PostgresProductSlot, + SpecialDeal as PostgresSpecialDeal, + Order as PostgresOrder, + OrderItem as PostgresOrderItem, + Payment as PostgresPayment, + Notification as PostgresNotification, + ProductCategory as PostgresProductCategory, + CartItem as PostgresCartItem, + Coupon as PostgresCoupon, + ProductWithUnit as PostgresProductWithUnit, + OrderWithItems as PostgresOrderWithItems, + CartItemWithProduct as PostgresCartItemWithProduct, +} from '@db-helper-postgres/db/types' +import type { + User as SqliteUser, + Address as SqliteAddress, + Unit as SqliteUnit, + ProductInfo as SqliteProductInfo, + DeliverySlotInfo as SqliteDeliverySlotInfo, + ProductSlot as SqliteProductSlot, + SpecialDeal as SqliteSpecialDeal, + Order as SqliteOrder, + OrderItem as SqliteOrderItem, + Payment as SqlitePayment, + Notification as SqliteNotification, + ProductCategory as SqliteProductCategory, + CartItem as SqliteCartItem, + Coupon as SqliteCoupon, + ProductWithUnit as SqliteProductWithUnit, + OrderWithItems as SqliteOrderWithItems, + CartItemWithProduct as SqliteCartItemWithProduct, +} from '@db-helper-sqlite/db/types' -export type User = InferSelectModel; -export type Address = InferSelectModel; -export type Unit = InferSelectModel; -export type ProductInfo = InferSelectModel; -export type DeliverySlotInfo = InferSelectModel; -export type ProductSlot = InferSelectModel; -export type SpecialDeal = InferSelectModel; -export type Order = InferSelectModel; -export type OrderItem = InferSelectModel; -export type Payment = InferSelectModel; -export type Notification = InferSelectModel; -export type ProductCategory = InferSelectModel; -export type CartItem = InferSelectModel; -export type Coupon = InferSelectModel; +type UseSqlite = typeof DB_DIALECT_TYPE extends 'sqlite' ? true : false -// Combined types -export type ProductWithUnit = ProductInfo & { - unit: Unit; -}; - -export type OrderWithItems = Order & { - items: (OrderItem & { product: ProductInfo })[]; - address: Address; - slot: DeliverySlotInfo; -}; - -export type CartItemWithProduct = CartItem & { - product: ProductInfo; -}; +export type User = UseSqlite extends true ? SqliteUser : PostgresUser +export type Address = UseSqlite extends true ? SqliteAddress : PostgresAddress +export type Unit = UseSqlite extends true ? SqliteUnit : PostgresUnit +export type ProductInfo = UseSqlite extends true ? SqliteProductInfo : PostgresProductInfo +export type DeliverySlotInfo = UseSqlite extends true ? SqliteDeliverySlotInfo : PostgresDeliverySlotInfo +export type ProductSlot = UseSqlite extends true ? SqliteProductSlot : PostgresProductSlot +export type SpecialDeal = UseSqlite extends true ? SqliteSpecialDeal : PostgresSpecialDeal +export type Order = UseSqlite extends true ? SqliteOrder : PostgresOrder +export type OrderItem = UseSqlite extends true ? SqliteOrderItem : PostgresOrderItem +export type Payment = UseSqlite extends true ? SqlitePayment : PostgresPayment +export type Notification = UseSqlite extends true ? SqliteNotification : PostgresNotification +export type ProductCategory = UseSqlite extends true ? SqliteProductCategory : PostgresProductCategory +export type CartItem = UseSqlite extends true ? SqliteCartItem : PostgresCartItem +export type Coupon = UseSqlite extends true ? SqliteCoupon : PostgresCoupon +export type ProductWithUnit = UseSqlite extends true ? SqliteProductWithUnit : PostgresProductWithUnit +export type OrderWithItems = UseSqlite extends true ? SqliteOrderWithItems : PostgresOrderWithItems +export type CartItemWithProduct = UseSqlite extends true ? SqliteCartItemWithProduct : PostgresCartItemWithProduct diff --git a/apps/backend/src/db/upload-url.ts b/apps/backend/src/db/upload-url.ts new file mode 100644 index 0000000..44ae085 --- /dev/null +++ b/apps/backend/src/db/upload-url.ts @@ -0,0 +1,14 @@ +import { claimUploadUrlStatus as claimUploadUrlStatusPostgres, createUploadUrlStatus as createUploadUrlStatusPostgres } from '@db-helper-postgres/lib/upload-url' +import { claimUploadUrlStatus as claimUploadUrlStatusSqlite, createUploadUrlStatus as createUploadUrlStatusSqlite } from '@db-helper-sqlite/lib/upload-url' + +const dialect = process.env.DB_DIALECT || DB_DIALECT_TYPE + +const createUploadUrlStatus = dialect === 'sqlite' + ? createUploadUrlStatusSqlite + : createUploadUrlStatusPostgres + +const claimUploadUrlStatus = dialect === 'sqlite' + ? claimUploadUrlStatusSqlite + : claimUploadUrlStatusPostgres + +export { claimUploadUrlStatus, createUploadUrlStatus } diff --git a/apps/backend/src/lib/delete-image.ts b/apps/backend/src/lib/delete-image.ts index 3edcfeb..13771a8 100644 --- a/apps/backend/src/lib/delete-image.ts +++ b/apps/backend/src/lib/delete-image.ts @@ -1,56 +1,54 @@ -import { eq } from "drizzle-orm"; -import { db } from "@/src/db/db_index" -import { deleteImageUtil, getOriginalUrlFromSignedUrl } from "@/src/lib/s3-client" -import { assetsDomain, s3Url } from "@/src/lib/env-exporter" +import { deleteImageUtil, getOriginalUrlFromSignedUrl } from '@/src/lib/s3-client' +import { assetsDomain, s3Url } from '@/src/lib/env-exporter' function extractS3Key(url: string): string | null { try { // Check if this is a signed URL first and get the original if it is - const originalUrl = getOriginalUrlFromSignedUrl(url) || url; + const originalUrl = getOriginalUrlFromSignedUrl(url) || url // Find the index of '.com/' in the URL // const comIndex = originalUrl.indexOf(".com/"); - const baseUrlIndex = originalUrl.indexOf(s3Url); + const baseUrlIndex = originalUrl.indexOf(s3Url) // If '.com/' is found, return everything after it if (baseUrlIndex !== -1) { - return originalUrl.substring(baseUrlIndex + s3Url.length); // +5 to skip '.com/' + return originalUrl.substring(baseUrlIndex + s3Url.length) // +5 to skip '.com/' } } catch (error) { - console.error("Error extracting key from URL:", error); + console.error('Error extracting key from URL:', error) } // Return null if the pattern isn't found or there was an error - return null; + return null } export async function deleteS3Image(imageUrl: string) { try { - let key:string | null = ''; + let key: string | null = '' - if(imageUrl.includes(assetsDomain)) { + if (imageUrl.includes(assetsDomain)) { key = imageUrl.replace(assetsDomain, '') } - else if(imageUrl.startsWith('http')){ - // First check if this is a signed URL and get the original if it is - const originalUrl = getOriginalUrlFromSignedUrl(imageUrl) || imageUrl; - - key = extractS3Key(originalUrl || ""); + else if (imageUrl.startsWith('http')) { + // First check if this is a signed URL and get the original if it is + const originalUrl = getOriginalUrlFromSignedUrl(imageUrl) || imageUrl + + key = extractS3Key(originalUrl || '') } else { - key = imageUrl; + key = imageUrl } if (!key) { - throw new Error("Invalid image URL format"); + throw new Error('Invalid image URL format') } - const deleteS3 = await deleteImageUtil({keys: [key] }); + const deleteS3 = await deleteImageUtil({ keys: [key] }) if (!deleteS3) { - throw new Error("Failed to delete image from S3"); + throw new Error('Failed to delete image from S3') } } catch (error) { - console.error("Error deleting image from S3:", error); + console.error('Error deleting image from S3:', error) } } diff --git a/apps/backend/src/lib/notif-service.ts b/apps/backend/src/lib/notif-service.ts index ee1a0d5..2c34765 100755 --- a/apps/backend/src/lib/notif-service.ts +++ b/apps/backend/src/lib/notif-service.ts @@ -1,7 +1,5 @@ -import { db } from "@/src/db/db_index" -import { sendPushNotificationsMany } from "@/src/lib/expo-service" +import { sendPushNotificationsMany } from '@/src/lib/expo-service' // import { usersTable, notifCredsTable, notificationTable } from "@/src/db/schema"; -import { eq, inArray } from "drizzle-orm"; // Core notification dispatch methods (renamed for clarity) export async function dispatchBulkNotification({ diff --git a/apps/backend/src/lib/roles-manager.ts b/apps/backend/src/lib/roles-manager.ts index 7242bce..80d49c6 100755 --- a/apps/backend/src/lib/roles-manager.ts +++ b/apps/backend/src/lib/roles-manager.ts @@ -1,5 +1,3 @@ -import { db } from "@/src/db/db_index" - /** * Constants for role names to avoid hardcoding and typos */ diff --git a/apps/backend/src/lib/s3-client.ts b/apps/backend/src/lib/s3-client.ts index 58a8c01..9280587 100755 --- a/apps/backend/src/lib/s3-client.ts +++ b/apps/backend/src/lib/s3-client.ts @@ -3,9 +3,7 @@ import { DeleteObjectCommand, DeleteObjectsCommand, PutObjectCommand, S3Client, import { getSignedUrl } from "@aws-sdk/s3-request-presigner" import signedUrlCache from "@/src/lib/signed-url-cache" import { s3AccessKeyId, s3Region, s3Url, s3SecretAccessKey, s3BucketName, assetsDomain } from "@/src/lib/env-exporter" -import { db } from "@/src/db/db_index"; // Adjust path if needed -import { uploadUrlStatus } from "@/src/db/schema" -import { and, eq } from 'drizzle-orm'; +import { claimUploadUrlStatus, createUploadUrlStatus } from '@/src/db/upload-url' const s3Client = new S3Client({ region: s3Region, @@ -161,10 +159,7 @@ export async function generateSignedUrlsFromS3Urls(s3Urls: (string|null)[], expi export async function generateUploadUrl(key: string, mimeType: string, expiresIn: number = 180): Promise { try { // Insert record into upload_url_status - await db.insert(uploadUrlStatus).values({ - key: key, - status: 'pending', - }); + await createUploadUrlStatus(key) // Generate signed upload URL const command = new PutObjectCommand({ @@ -207,16 +202,7 @@ export async function claimUploadUrl(url: string): Promise { semiKey = extractKeyFromPresignedUrl(url); else semiKey = url - // Update status to 'claimed' if currently 'pending' - const result = await db - .update(uploadUrlStatus) - .set({ status: 'claimed' }) - .where(and(eq(uploadUrlStatus.key, semiKey), eq(uploadUrlStatus.status, 'pending'))) - .returning(); - - if (result.length === 0) { - throw new Error('Upload URL not found or already claimed'); - } + await claimUploadUrlStatus(semiKey) } catch (error) { console.error('Error claiming upload URL:', error); throw new Error('Failed to claim upload URL'); diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/main.ts b/apps/backend/src/trpc/apis/admin-apis/dataAccessors/main.ts index 0705d1c..d472b40 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/main.ts +++ b/apps/backend/src/trpc/apis/admin-apis/dataAccessors/main.ts @@ -1,55 +1,60 @@ export type { IBannerDbService, Banner, NewBanner } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/banner-db-service.interface' -// export { bannerDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/banner-queries' -export { bannerDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/banner-queries' - export type { IComplaintDbService, Complaint, NewComplaint } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/complaint-db-service.interface' -// export { complaintDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/complaint-queries' -export { complaintDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/complaint-queries' - export type { IConstantDbService, Constant, NewConstant } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/constant-db-service.interface' -// export { constantDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/constant-queries' -export { constantDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/constant-queries' - export type { ICouponDbService, Coupon, NewCoupon, ReservedCoupon, NewReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/coupon-db-service.interface' -// export { couponDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/coupon-queries' -export { couponDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/coupon-queries' - export type { IOrderDbService, Order, OrderItem, OrderStatus, OrderWithRelations, OrderWithStatus, OrderWithCouponUsages } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/order-db-service.interface' -// export { orderDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/order-queries' -export { orderDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/order-queries' - export type { IProductDbService, Product, NewProduct, ProductGroup, NewProductGroup } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/product-db-service.interface' -// export { productDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/product-queries' -export { productDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/product-queries' - export type { IRefundDbService, Refund, NewRefund } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/refund-db-service.interface' -// export { refundDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/refund-queries' -export { refundDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/refund-queries' - export type { IScheduleDbService, Schedule, NewSchedule } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/schedule-db-service.interface' -// export { scheduleDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/schedule-queries' -export { scheduleDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/schedule-queries' - export type { ISlotDbService, Slot, NewSlot, ProductSlot, NewProductSlot, SlotWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/slot-db-service.interface' -// export { slotDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/slot-queries' -export { slotDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/slot-queries' - export type { IStaffUserDbService, StaffUser, NewStaffUser, StaffRole, StaffUserWithRole } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/staff-user-db-service.interface' -// export { staffUserDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/staff-user-queries' -export { staffUserDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/staff-user-queries' - export type { IStoreDbService, Store, NewStore } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/store-db-service.interface' -// export { storeDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/store-queries' -export { storeDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/store-queries' - export type { ITagDbService, Tag, NewTag } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/tag-db-service.interface' -// export { tagDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/tag-queries' -export { tagDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/tag-queries' - export type { IUserDbService, User, NewUser, UserDetail } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/user-db-service.interface' -// export { userDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/user-queries' -export { userDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/user-queries' - export type { IVendorSnippetDbService, VendorSnippet, NewVendorSnippet } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/vendor-snippet-db-service.interface' -// export { vendorSnippetDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/vendor-snippets-queries' -export { vendorSnippetDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/vendor-snippets-queries' + +import { bannerDbService as sqliteBannerDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/banner-queries' +import { bannerDbService as postgresBannerDbService } from '@db-helper-postgres/dataAccessors/admin-apis/banner-queries' +import { complaintDbService as sqliteComplaintDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/complaint-queries' +import { complaintDbService as postgresComplaintDbService } from '@db-helper-postgres/dataAccessors/admin-apis/complaint-queries' +import { constantDbService as sqliteConstantDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/constant-queries' +import { constantDbService as postgresConstantDbService } from '@db-helper-postgres/dataAccessors/admin-apis/constant-queries' +import { couponDbService as sqliteCouponDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/coupon-queries' +import { couponDbService as postgresCouponDbService } from '@db-helper-postgres/dataAccessors/admin-apis/coupon-queries' +import { orderDbService as sqliteOrderDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/order-queries' +import { orderDbService as postgresOrderDbService } from '@db-helper-postgres/dataAccessors/admin-apis/order-queries' +import { productDbService as sqliteProductDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/product-queries' +import { productDbService as postgresProductDbService } from '@db-helper-postgres/dataAccessors/admin-apis/product-queries' +import { refundDbService as sqliteRefundDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/refund-queries' +import { refundDbService as postgresRefundDbService } from '@db-helper-postgres/dataAccessors/admin-apis/refund-queries' +import { scheduleDbService as sqliteScheduleDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/schedule-queries' +import { scheduleDbService as postgresScheduleDbService } from '@db-helper-postgres/dataAccessors/admin-apis/schedule-queries' +import { slotDbService as sqliteSlotDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/slot-queries' +import { slotDbService as postgresSlotDbService } from '@db-helper-postgres/dataAccessors/admin-apis/slot-queries' +import { staffUserDbService as sqliteStaffUserDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/staff-user-queries' +import { staffUserDbService as postgresStaffUserDbService } from '@db-helper-postgres/dataAccessors/admin-apis/staff-user-queries' +import { storeDbService as sqliteStoreDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/store-queries' +import { storeDbService as postgresStoreDbService } from '@db-helper-postgres/dataAccessors/admin-apis/store-queries' +import { tagDbService as sqliteTagDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/tag-queries' +import { tagDbService as postgresTagDbService } from '@db-helper-postgres/dataAccessors/admin-apis/tag-queries' +import { userDbService as sqliteUserDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/user-queries' +import { userDbService as postgresUserDbService } from '@db-helper-postgres/dataAccessors/admin-apis/user-queries' +import { vendorSnippetDbService as sqliteVendorSnippetDbService } from '@db-helper-sqlite/dataAccessors/admin-apis/vendor-snippets-queries' +import { vendorSnippetDbService as postgresVendorSnippetDbService } from '@db-helper-postgres/dataAccessors/admin-apis/vendor-snippets-queries' + +const isSqlite = process.env.DB_DIALECT === 'sqlite' + +export const bannerDbService = isSqlite ? sqliteBannerDbService : postgresBannerDbService +export const complaintDbService = isSqlite ? sqliteComplaintDbService : postgresComplaintDbService +export const constantDbService = isSqlite ? sqliteConstantDbService : postgresConstantDbService +export const couponDbService = isSqlite ? sqliteCouponDbService : postgresCouponDbService +export const orderDbService = isSqlite ? sqliteOrderDbService : postgresOrderDbService +export const productDbService = isSqlite ? sqliteProductDbService : postgresProductDbService +export const refundDbService = isSqlite ? sqliteRefundDbService : postgresRefundDbService +export const scheduleDbService = isSqlite ? sqliteScheduleDbService : postgresScheduleDbService +export const slotDbService = isSqlite ? sqliteSlotDbService : postgresSlotDbService +export const staffUserDbService = isSqlite ? sqliteStaffUserDbService : postgresStaffUserDbService +export const storeDbService = isSqlite ? sqliteStoreDbService : postgresStoreDbService +export const tagDbService = isSqlite ? sqliteTagDbService : postgresTagDbService +export const userDbService = isSqlite ? sqliteUserDbService : postgresUserDbService +export const vendorSnippetDbService = isSqlite ? sqliteVendorSnippetDbService : postgresVendorSnippetDbService diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/main.ts b/apps/backend/src/trpc/apis/user-apis/dataAccessors/main.ts index b878f1f..dac3bf8 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/main.ts +++ b/apps/backend/src/trpc/apis/user-apis/dataAccessors/main.ts @@ -1,43 +1,48 @@ export type { IUserBannerDbService, UserBanner } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-banner-db-service.interface' -// export { userBannerDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-banner-queries' -export { userBannerDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-banner-queries' - export type { IUserStoreDbService, Store as UserStore, StoreBasic } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-store-db-service.interface' -// export { userStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-store-queries' -export { userStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-store-queries' - export type { IUserAddressDbService, Address, NewAddress } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-address-db-service.interface' -// export { userAddressDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-address-queries' -export { userAddressDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-address-queries' - export type { IUserCartDbService, CartItem } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-cart-db-service.interface' -// export { userCartDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-cart-queries' -export { userCartDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-cart-queries' - export type { IUserComplaintDbService, Complaint, NewComplaint } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-complaint-db-service.interface' -// export { userComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-complaint-queries' -export { userComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-complaint-queries' - export type { IUserProductDbService, Product, Store as ProductStore, Review, ProductWithUnit } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-product-db-service.interface' -// export { userProductDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-product-queries' -export { userProductDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-product-queries' - export type { IUserAuthDbService, User, UserCred, UserDetail } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-auth-db-service.interface' -// export { userAuthDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-auth-queries' -export { userAuthDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-auth-queries' - export type { IUserProfileDbService, User as ProfileUser, UserDetail as ProfileUserDetail, UserCred as ProfileUserCred, NotifCred, UnloggedToken } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-profile-db-service.interface' -// export { userProfileDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-profile-queries' -export { userProfileDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-profile-queries' - export type { IUserSlotDbService, Slot } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-slot-db-service.interface' -// export { userSlotDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-slot-queries' -export { userSlotDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-slot-queries' - export type { IUserCouponDbService, Coupon, CouponWithRelations, ReservedCoupon } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-coupon-db-service.interface' -// export { userCouponDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-coupon-queries' -export { userCouponDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-coupon-queries' - export type { IUserOrderDbService, Order, OrderInsert, OrderItemInsert, OrderStatusInsert, Coupon as OrderCoupon } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-order-db-service.interface' -// export { userOrderDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-order-queries' -export { userOrderDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-order-queries' + +import { userBannerDbService as sqliteUserBannerDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-banner-queries' +import { userBannerDbService as postgresUserBannerDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-banner-queries' +import { userStoreDbService as sqliteUserStoreDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-store-queries' +import { userStoreDbService as postgresUserStoreDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-store-queries' +import { userAddressDbService as sqliteUserAddressDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-address-queries' +import { userAddressDbService as postgresUserAddressDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-address-queries' +import { userCartDbService as sqliteUserCartDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-cart-queries' +import { userCartDbService as postgresUserCartDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-cart-queries' +import { userComplaintDbService as sqliteUserComplaintDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-complaint-queries' +import { userComplaintDbService as postgresUserComplaintDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-complaint-queries' +import { userProductDbService as sqliteUserProductDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-product-queries' +import { userProductDbService as postgresUserProductDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-product-queries' +import { userAuthDbService as sqliteUserAuthDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-auth-queries' +import { userAuthDbService as postgresUserAuthDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-auth-queries' +import { userProfileDbService as sqliteUserProfileDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-profile-queries' +import { userProfileDbService as postgresUserProfileDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-profile-queries' +import { userSlotDbService as sqliteUserSlotDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-slot-queries' +import { userSlotDbService as postgresUserSlotDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-slot-queries' +import { userCouponDbService as sqliteUserCouponDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-coupon-queries' +import { userCouponDbService as postgresUserCouponDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-coupon-queries' +import { userOrderDbService as sqliteUserOrderDbService } from '@db-helper-sqlite/dataAccessors/user-apis/user-order-queries' +import { userOrderDbService as postgresUserOrderDbService } from '@db-helper-postgres/dataAccessors/user-apis/user-order-queries' + +const isSqlite = process.env.DB_DIALECT === 'sqlite' + +export const userBannerDbService = isSqlite ? sqliteUserBannerDbService : postgresUserBannerDbService +export const userStoreDbService = isSqlite ? sqliteUserStoreDbService : postgresUserStoreDbService +export const userAddressDbService = isSqlite ? sqliteUserAddressDbService : postgresUserAddressDbService +export const userCartDbService = isSqlite ? sqliteUserCartDbService : postgresUserCartDbService +export const userComplaintDbService = isSqlite ? sqliteUserComplaintDbService : postgresUserComplaintDbService +export const userProductDbService = isSqlite ? sqliteUserProductDbService : postgresUserProductDbService +export const userAuthDbService = isSqlite ? sqliteUserAuthDbService : postgresUserAuthDbService +export const userProfileDbService = isSqlite ? sqliteUserProfileDbService : postgresUserProfileDbService +export const userSlotDbService = isSqlite ? sqliteUserSlotDbService : postgresUserSlotDbService +export const userCouponDbService = isSqlite ? sqliteUserCouponDbService : postgresUserCouponDbService +export const userOrderDbService = isSqlite ? sqliteUserOrderDbService : postgresUserOrderDbService diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index 137ac12..a323f4a 100755 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -30,6 +30,8 @@ // "baseUrl": ".", "paths": { "@/*": ["./*"], + "@db-helper-postgres/*": ["../db-helper-postgres/src/*"], + "@db-helper-sqlite/*": ["../db-helper-sqlite/src/*"], "shared-types": ["../shared-types"], "@commonTypes": ["../../packages/ui/shared-types"], "@commonTypes/*": ["../../packages/ui/shared-types/*"], @@ -122,4 +124,3 @@ }, "include": ["src", "types", "index.ts", "../shared-types", "../../packages/shared"] } - diff --git a/apps/backend/types/db-dialect.d.ts b/apps/backend/types/db-dialect.d.ts new file mode 100644 index 0000000..fbf1401 --- /dev/null +++ b/apps/backend/types/db-dialect.d.ts @@ -0,0 +1 @@ +declare const DB_DIALECT_TYPE: 'postgres' diff --git a/apps/backend/drizzle.config.postgres.ts b/apps/db-helper-postgres/drizzle.config.ts similarity index 74% rename from apps/backend/drizzle.config.postgres.ts rename to apps/db-helper-postgres/drizzle.config.ts index 39a9121..6622da0 100644 --- a/apps/backend/drizzle.config.postgres.ts +++ b/apps/db-helper-postgres/drizzle.config.ts @@ -2,8 +2,8 @@ import 'dotenv/config' import { defineConfig } from 'drizzle-kit' export default defineConfig({ - out: './drizzle/pg', - schema: './src/db/schema-postgres.ts', + out: './drizzle', + schema: './src/db/schema.ts', dialect: 'postgresql', dbCredentials: { url: process.env.DATABASE_URL!, diff --git a/apps/db-helper-postgres/package.json b/apps/db-helper-postgres/package.json new file mode 100644 index 0000000..c5da9ce --- /dev/null +++ b/apps/db-helper-postgres/package.json @@ -0,0 +1,10 @@ +{ + "name": "db-helper-postgres", + "version": "0.1.0", + "private": true, + "type": "module", + "dependencies": { + "drizzle-orm": "^0.45.1", + "pg": "^8.16.3" + } +} diff --git a/apps/db-helper-postgres/src/apis/common-apis/common-product.ts b/apps/db-helper-postgres/src/apis/common-apis/common-product.ts new file mode 100644 index 0000000..70ab4f9 --- /dev/null +++ b/apps/db-helper-postgres/src/apis/common-apis/common-product.ts @@ -0,0 +1,84 @@ +import { and, eq, gt, inArray } from 'drizzle-orm' + +import { db } from '../../db/db_index' +import { deliverySlotInfo, productInfo, productSlots, productTags, units } from '../../db/schema' + +type ProductSummaryRow = { + id: number + name: string + shortDescription: string | null + price: string + marketPrice: string + images: unknown + isOutOfStock: boolean | null + unitShortNotation: string + productQuantity: number | null + nextDeliveryDate: Date | null +} + +const getNextDeliveryDate = async (productId: number): Promise => { + const result = await db + .select({ deliveryTime: deliverySlotInfo.deliveryTime }) + .from(productSlots) + .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) + .where( + and( + eq(productSlots.productId, productId), + eq(deliverySlotInfo.isActive, true), + gt(deliverySlotInfo.deliveryTime, new Date()) + ) + ) + .orderBy(deliverySlotInfo.deliveryTime) + .limit(1) + + return result[0]?.deliveryTime || null +} + +export const getProductsSummaryData = async (tagId?: number | null): Promise => { + let productIds: number[] | null = null + + if (tagId) { + const taggedProducts = await db + .select({ productId: productTags.productId }) + .from(productTags) + .where(eq(productTags.tagId, tagId)) + + productIds = taggedProducts.map((taggedProduct) => taggedProduct.productId) + + if (productIds.length === 0) { + return [] + } + } + + const whereCondition = productIds && productIds.length > 0 + ? inArray(productInfo.id, productIds) + : undefined + + const productsWithUnits = await db + .select({ + id: productInfo.id, + name: productInfo.name, + shortDescription: productInfo.shortDescription, + price: productInfo.price, + marketPrice: productInfo.marketPrice, + images: productInfo.images, + isOutOfStock: productInfo.isOutOfStock, + unitShortNotation: units.shortNotation, + productQuantity: productInfo.productQuantity, + }) + .from(productInfo) + .innerJoin(units, eq(productInfo.unitId, units.id)) + .where(whereCondition) + + const productsWithDelivery = await Promise.all( + productsWithUnits.map(async (product) => { + const nextDeliveryDate = await getNextDeliveryDate(product.id) + return { + ...product, + nextDeliveryDate, + } + }) + ) + + return productsWithDelivery +} diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/banner-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/banner-queries.ts similarity index 81% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/banner-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/banner-queries.ts index 6db5999..8e912fc 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/banner-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/banner-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { homeBanners } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { homeBanners } from '../../db/schema' import { eq, desc } from 'drizzle-orm' -import { IBannerDbService, Banner, NewBanner } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/banner-db-service.interface' +import { IBannerDbService, Banner, NewBanner } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/banner-db-service.interface' export class BannerDbService implements IBannerDbService { async getAllBanners(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/complaint-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/complaint-queries.ts similarity index 83% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/complaint-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/complaint-queries.ts index b792066..f5b9fff 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/complaint-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/complaint-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { complaints, users } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { complaints, users } from '../../db/schema' import { eq, desc, lt } from 'drizzle-orm' -import { IComplaintDbService, Complaint, NewComplaint } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/complaint-db-service.interface' +import { IComplaintDbService, Complaint, NewComplaint } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/complaint-db-service.interface' export class ComplaintDbService implements IComplaintDbService { async getComplaints( diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/constant-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/constant-queries.ts similarity index 72% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/constant-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/constant-queries.ts index b6aceca..910c134 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/constant-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/constant-queries.ts @@ -1,6 +1,6 @@ -import { db } from '@/src/db/db_index' -import { keyValStore } from '@/src/db/schema' -import { IConstantDbService, Constant, NewConstant } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/constant-db-service.interface' +import { db } from '../../db/db_index' +import { keyValStore } from '../../db/schema' +import { IConstantDbService, Constant, NewConstant } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/constant-db-service.interface' export class ConstantDbService implements IConstantDbService { async getAllConstants(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/coupon-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/coupon-queries.ts similarity index 96% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/coupon-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/coupon-queries.ts index 82e5644..ee23ab1 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/coupon-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/coupon-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { coupons, couponApplicableUsers, couponApplicableProducts, reservedCoupons, users, orders, orderStatus } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { coupons, couponApplicableUsers, couponApplicableProducts, reservedCoupons, users, orders, orderStatus } from '../../db/schema' import { eq, and, like, or, inArray, lt, asc } from 'drizzle-orm' -import { ICouponDbService, Coupon, NewCoupon, ReservedCoupon, NewReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/coupon-db-service.interface' +import { ICouponDbService, Coupon, NewCoupon, ReservedCoupon, NewReservedCoupon, CouponWithRelations } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/coupon-db-service.interface' export class CouponDbService implements ICouponDbService { async createCoupon(data: NewCoupon): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/order-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/order-queries.ts similarity index 98% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/order-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/order-queries.ts index c68967a..7d2dc90 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/order-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/order-queries.ts @@ -1,4 +1,4 @@ -import { db } from '@/src/db/db_index' +import { db } from '../../db/db_index' import { orders, orderItems, @@ -14,7 +14,7 @@ import { productInfo, units, paymentInfoTable, -} from '@/src/db/schema' +} from '../../db/schema' import { eq, and, gte, lt, desc, inArray, SQL } from 'drizzle-orm' import { IOrderDbService, @@ -26,7 +26,7 @@ import { OrderWithRelations, OrderWithStatus, OrderWithCouponUsages, -} from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/order-db-service.interface' +} from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/order-db-service.interface' export class OrderDbService implements IOrderDbService { async updateOrderNotes(orderId: number, adminNotes: string | null): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/product-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/product-queries.ts similarity index 97% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/product-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/product-queries.ts index 8eb7091..bdf314f 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/product-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/product-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { productInfo, units, specialDeals, productSlots, productTags, productReviews, productGroupInfo, productGroupMembership, users } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productInfo, units, specialDeals, productSlots, productTags, productReviews, productGroupInfo, productGroupMembership, users } from '../../db/schema' import { eq, and, inArray, desc, sql } from 'drizzle-orm' -import { IProductDbService, Product, NewProduct, ProductGroup, NewProductGroup } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/product-db-service.interface' +import { IProductDbService, Product, NewProduct, ProductGroup, NewProductGroup } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/product-db-service.interface' export class ProductDbService implements IProductDbService { async getAllProducts(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/refund-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/refund-queries.ts similarity index 84% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/refund-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/refund-queries.ts index 3374d80..691722d 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/refund-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/refund-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { refunds, orders, orderStatus, payments } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { refunds, orders, orderStatus, payments } from '../../db/schema' import { eq, and } from 'drizzle-orm' -import { IRefundDbService, Refund, NewRefund } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/refund-db-service.interface' +import { IRefundDbService, Refund, NewRefund } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/refund-db-service.interface' export class RefundDbService implements IRefundDbService { async createRefund(data: NewRefund): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/schedule-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/schedule-queries.ts similarity index 85% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/schedule-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/schedule-queries.ts index da2e5e4..6744a88 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/schedule-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/schedule-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { productAvailabilitySchedules } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productAvailabilitySchedules } from '../../db/schema' import { eq, desc } from 'drizzle-orm' -import { IScheduleDbService, Schedule, NewSchedule } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/schedule-db-service.interface' +import { IScheduleDbService, Schedule, NewSchedule } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/schedule-db-service.interface' export class ScheduleDbService implements IScheduleDbService { async createSchedule(data: NewSchedule): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/slot-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/slot-queries.ts similarity index 95% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/slot-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/slot-queries.ts index 6da0dd1..1154210 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/slot-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/slot-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { deliverySlotInfo, productSlots, vendorSnippets, productInfo, productGroupInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { deliverySlotInfo, productSlots, vendorSnippets, productInfo, productGroupInfo } from '../../db/schema' import { eq, inArray, and, desc } from 'drizzle-orm' -import { ISlotDbService, Slot, NewSlot, ProductSlot, SlotWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/slot-db-service.interface' +import { ISlotDbService, Slot, NewSlot, ProductSlot, SlotWithRelations } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/slot-db-service.interface' export class SlotDbService implements ISlotDbService { async getAllSlots(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/staff-user-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/staff-user-queries.ts similarity index 94% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/staff-user-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/staff-user-queries.ts index ec39dc1..de0da27 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/staff-user-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/staff-user-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { staffUsers, staffRoles, users, userDetails, orders } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { staffUsers, staffRoles, users, userDetails, orders } from '../../db/schema' import { eq, or, ilike, and, lt, desc } from 'drizzle-orm' -import { IStaffUserDbService, StaffUser, NewStaffUser, StaffRole } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/staff-user-db-service.interface' +import { IStaffUserDbService, StaffUser, NewStaffUser, StaffRole } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/staff-user-db-service.interface' export class StaffUserDbService implements IStaffUserDbService { async getStaffUserByName(name: string): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/store-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/store-queries.ts similarity index 84% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/store-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/store-queries.ts index 8c5579d..e1cb55b 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/store-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/store-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { storeInfo, productInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { storeInfo, productInfo } from '../../db/schema' import { eq, inArray } from 'drizzle-orm' -import { IStoreDbService, Store, NewStore } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/store-db-service.interface' +import { IStoreDbService, Store, NewStore } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/store-db-service.interface' export class StoreDbService implements IStoreDbService { async getAllStores(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/tag-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/tag-queries.ts similarity index 82% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/tag-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/tag-queries.ts index e2eca0b..5ac42fa 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/tag-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/tag-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { productTagInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productTagInfo } from '../../db/schema' import { eq } from 'drizzle-orm' -import { ITagDbService, Tag, NewTag } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/tag-db-service.interface' +import { ITagDbService, Tag, NewTag } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/tag-db-service.interface' export class TagDbService implements ITagDbService { async getAllTags(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/user-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/user-queries.ts similarity index 96% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/user-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/user-queries.ts index 04a68b3..b360d18 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/user-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/user-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { users, userDetails, orders, orderItems, orderStatus, complaints, notifCreds, unloggedUserTokens, userIncidents } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { users, userDetails, orders, orderItems, orderStatus, complaints, notifCreds, unloggedUserTokens, userIncidents } from '../../db/schema' import { eq, sql, desc, asc, count, max, inArray } from 'drizzle-orm' -import { IUserDbService, User, NewUser, UserDetail } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/user-db-service.interface' +import { IUserDbService, User, NewUser, UserDetail } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/user-db-service.interface' export class UserDbService implements IUserDbService { async getUserById(id: number): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/vendor-snippets-queries.ts b/apps/db-helper-postgres/src/dataAccessors/admin-apis/vendor-snippets-queries.ts similarity index 95% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/vendor-snippets-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/admin-apis/vendor-snippets-queries.ts index 5e1f40f..df8af2e 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/vendor-snippets-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/admin-apis/vendor-snippets-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { vendorSnippets, deliverySlotInfo, orders, orderItems, productInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { vendorSnippets, deliverySlotInfo, orders, orderItems, productInfo } from '../../db/schema' import { eq, and, inArray, gt, asc, desc } from 'drizzle-orm' -import { IVendorSnippetDbService, VendorSnippet, NewVendorSnippet } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/vendor-snippet-db-service.interface' +import { IVendorSnippetDbService, VendorSnippet, NewVendorSnippet } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/vendor-snippet-db-service.interface' export class VendorSnippetDbService implements IVendorSnippetDbService { async createSnippet(data: NewVendorSnippet): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-address-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-address-queries.ts similarity index 91% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-address-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-address-queries.ts index e10ac73..3a30b34 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-address-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-address-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { addresses, orders, orderStatus, deliverySlotInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { addresses, orders, orderStatus, deliverySlotInfo } from '../../db/schema' import { eq, and, gte } from 'drizzle-orm' -import { IUserAddressDbService, Address, NewAddress } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-address-db-service.interface' +import { IUserAddressDbService, Address, NewAddress } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-address-db-service.interface' export class UserAddressDbService implements IUserAddressDbService { async getDefaultAddress(userId: number): Promise
{ diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-auth-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-auth-queries.ts similarity index 96% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-auth-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-auth-queries.ts index 790d411..8ccbc11 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-auth-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-auth-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { users, userCreds, userDetails, addresses, cartItems, complaints, couponApplicableUsers, couponUsage, notifCreds, notifications, orderItems, orderStatus, orders, payments, refunds, productReviews, reservedCoupons } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { users, userCreds, userDetails, addresses, cartItems, complaints, couponApplicableUsers, couponUsage, notifCreds, notifications, orderItems, orderStatus, orders, payments, refunds, productReviews, reservedCoupons } from '../../db/schema' import { eq } from 'drizzle-orm' -import { IUserAuthDbService, User, UserCred, UserDetail } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-auth-db-service.interface' +import { IUserAuthDbService, User, UserCred, UserDetail } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-auth-db-service.interface' export class UserAuthDbService implements IUserAuthDbService { async getUserByEmail(email: string): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-banner-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-banner-queries.ts similarity index 62% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-banner-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-banner-queries.ts index a323a9e..553e841 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-banner-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-banner-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { homeBanners } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { homeBanners } from '../../db/schema' import { isNotNull, asc } from 'drizzle-orm' -import { IUserBannerDbService, UserBanner } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-banner-db-service.interface' +import { IUserBannerDbService, UserBanner } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-banner-db-service.interface' export class UserBannerDbService implements IUserBannerDbService { async getActiveBanners(): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-cart-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-cart-queries.ts similarity index 90% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-cart-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-cart-queries.ts index 55fabff..267b13d 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-cart-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-cart-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { cartItems, productInfo, units } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { cartItems, productInfo, units } from '../../db/schema' import { eq, and, sql } from 'drizzle-orm' -import { IUserCartDbService, CartItem } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-cart-db-service.interface' +import { IUserCartDbService, CartItem } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-cart-db-service.interface' export class UserCartDbService implements IUserCartDbService { async getCartItemsWithProducts(userId: number) { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-complaint-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-complaint-queries.ts similarity index 78% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-complaint-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-complaint-queries.ts index 697a6e7..a1f7157 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-complaint-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-complaint-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { complaints } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { complaints } from '../../db/schema' import { eq, asc } from 'drizzle-orm' -import { IUserComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-complaint-db-service.interface' +import { IUserComplaintDbService } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-complaint-db-service.interface' export class UserComplaintDbService implements IUserComplaintDbService { async getComplaintsByUserId(userId: number) { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-coupon-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-coupon-queries.ts similarity index 92% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-coupon-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-coupon-queries.ts index bcdcff6..6d1c665 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-coupon-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-coupon-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { coupons, couponUsage, couponApplicableUsers, couponApplicableProducts, reservedCoupons } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { coupons, couponUsage, couponApplicableUsers, couponApplicableProducts, reservedCoupons } from '../../db/schema' import { eq, and, or, gt, isNull } from 'drizzle-orm' -import { IUserCouponDbService, Coupon, ReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-coupon-db-service.interface' +import { IUserCouponDbService, Coupon, ReservedCoupon, CouponWithRelations } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-coupon-db-service.interface' export class UserCouponDbService implements IUserCouponDbService { async getActiveCouponsForUser(userId: number): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-order-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-order-queries.ts similarity index 97% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-order-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-order-queries.ts index 54c0af0..64463a8 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-order-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-order-queries.ts @@ -1,4 +1,4 @@ -import { db } from '@/src/db/db_index_sqlite' +import { db } from '../../db/db_index' import { orders, orderItems, @@ -12,12 +12,12 @@ import { refunds, units, userDetails, -} from '@/src/db/schema' +} from '../../db/schema' import { and, desc, eq, gte, inArray } from 'drizzle-orm' import { IUserOrderDbService, Order, -} from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-order-db-service.interface' +} from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-order-db-service.interface' export class UserOrderDbService implements IUserOrderDbService { async getUserDetailByUserId(userId: number) { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-product-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-product-queries.ts similarity index 93% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-product-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-product-queries.ts index 2b925dc..369d4fa 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-product-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-product-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { productInfo, units, storeInfo, productSlots, deliverySlotInfo, specialDeals, productReviews, users } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productInfo, units, storeInfo, productSlots, deliverySlotInfo, specialDeals, productReviews, users } from '../../db/schema' import { eq, and, gt, sql, desc } from 'drizzle-orm' -import { IUserProductDbService, Review } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-product-db-service.interface' +import { IUserProductDbService, Review } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-product-db-service.interface' export class UserProductDbService implements IUserProductDbService { async getProductById(productId: number) { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-profile-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-profile-queries.ts similarity index 92% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-profile-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-profile-queries.ts index 303aa4d..4a0cd8b 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-profile-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-profile-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { users, userDetails, userCreds, notifCreds, unloggedUserTokens } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { users, userDetails, userCreds, notifCreds, unloggedUserTokens } from '../../db/schema' import { eq, and } from 'drizzle-orm' -import { IUserProfileDbService, User, UserDetail, UserCred, NotifCred, UnloggedToken } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-profile-db-service.interface' +import { IUserProfileDbService, User, UserDetail, UserCred, NotifCred, UnloggedToken } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-profile-db-service.interface' export class UserProfileDbService implements IUserProfileDbService { async getUserById(userId: number): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-slot-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-slot-queries.ts similarity index 75% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-slot-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-slot-queries.ts index 52963e4..cca1c1a 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-slot-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-slot-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { deliverySlotInfo, productInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { deliverySlotInfo, productInfo } from '../../db/schema' import { eq } from 'drizzle-orm' -import { IUserSlotDbService, Slot } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-slot-db-service.interface' +import { IUserSlotDbService, Slot } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-slot-db-service.interface' export class UserSlotDbService implements IUserSlotDbService { async getActiveSlots(): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-store-queries.ts b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-store-queries.ts similarity index 89% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-store-queries.ts rename to apps/db-helper-postgres/src/dataAccessors/user-apis/user-store-queries.ts index af30ed5..3ab67e5 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-store-queries.ts +++ b/apps/db-helper-postgres/src/dataAccessors/user-apis/user-store-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { storeInfo, productInfo, units } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { storeInfo, productInfo, units } from '../../db/schema' import { eq, and, sql } from 'drizzle-orm' -import { IUserStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-store-db-service.interface' +import { IUserStoreDbService } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-store-db-service.interface' export class UserStoreDbService implements IUserStoreDbService { async getStoresWithProductCount(): Promise> { diff --git a/apps/db-helper-postgres/src/db/db_index.ts b/apps/db-helper-postgres/src/db/db_index.ts new file mode 100644 index 0000000..5845263 --- /dev/null +++ b/apps/db-helper-postgres/src/db/db_index.ts @@ -0,0 +1,10 @@ +import { drizzle } from 'drizzle-orm/node-postgres' +import * as schema from './schema' + +const db = drizzle({ + connection: process.env.DATABASE_URL!, + casing: 'snake_case', + schema, +}) + +export { db } diff --git a/apps/backend/src/db/porter.ts b/apps/db-helper-postgres/src/db/porter.ts similarity index 98% rename from apps/backend/src/db/porter.ts rename to apps/db-helper-postgres/src/db/porter.ts index 4173d6f..8ea1a5d 100644 --- a/apps/backend/src/db/porter.ts +++ b/apps/db-helper-postgres/src/db/porter.ts @@ -2,13 +2,13 @@ * This was a one time script to change the composition of the signed urls */ -import { db } from '@/src/db/db_index' +import { db } from './db_index' import { userDetails, productInfo, productTagInfo, complaints -} from '@/src/db/schema'; +} from './schema'; import { eq, not, isNull } from 'drizzle-orm'; const S3_DOMAIN = 'https://s3.sgp.io.cloud.ovh.net'; diff --git a/apps/db-helper-postgres/src/db/schema.ts b/apps/db-helper-postgres/src/db/schema.ts new file mode 100644 index 0000000..939e04d --- /dev/null +++ b/apps/db-helper-postgres/src/db/schema.ts @@ -0,0 +1,706 @@ +import { pgTable, pgSchema, integer, varchar, date, boolean, timestamp, numeric, jsonb, pgEnum, unique, real, text, check, decimal } from "drizzle-orm/pg-core"; +import { relations, sql } from "drizzle-orm"; + +const mf = pgSchema('mf'); + + + +export const users = mf.table('users', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar({ length: 255 }), + email: varchar({ length: 255 }), + mobile: varchar({ length: 255 }), + createdAt: timestamp('created_at').notNull().defaultNow(), +}, (t) => ({ + unq_email: unique('unique_email').on(t.email), +})); + +export const userDetails = mf.table('user_details', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id).unique(), + bio: varchar('bio', { length: 500 }), + dateOfBirth: date('date_of_birth'), + gender: varchar('gender', { length: 20 }), + occupation: varchar('occupation', { length: 100 }), + profileImage: varchar('profile_image', { length: 500 }), + isSuspended: boolean('is_suspended').notNull().default(false), + createdAt: timestamp('created_at').notNull().defaultNow(), + updatedAt: timestamp('updated_at').notNull().defaultNow(), +}); + +export const userCreds = mf.table('user_creds', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + userPassword: varchar('user_password', { length: 255 }).notNull(), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const addresses = mf.table('addresses', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + name: varchar('name', { length: 255 }).notNull(), + phone: varchar('phone', { length: 15 }).notNull(), + addressLine1: varchar('address_line1', { length: 255 }).notNull(), + addressLine2: varchar('address_line2', { length: 255 }), + city: varchar('city', { length: 100 }).notNull(), + state: varchar('state', { length: 100 }).notNull(), + pincode: varchar('pincode', { length: 10 }).notNull(), + isDefault: boolean('is_default').notNull().default(false), + latitude: real('latitude'), + longitude: real('longitude'), + googleMapsUrl: varchar('google_maps_url', { length: 500 }), + adminLatitude: real('admin_latitude'), + adminLongitude: real('admin_longitude'), + zoneId: integer('zone_id').references(() => addressZones.id), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const addressZones = mf.table('address_zones', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + zoneName: varchar('zone_name', { length: 255 }).notNull(), + addedAt: timestamp('added_at').notNull().defaultNow(), +}); + +export const addressAreas = mf.table('address_areas', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + placeName: varchar('place_name', { length: 255 }).notNull(), + zoneId: integer('zone_id').references(() => addressZones.id), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const staffUsers = mf.table('staff_users', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar({ length: 255 }).notNull(), + password: varchar({ length: 255 }).notNull(), + staffRoleId: integer('staff_role_id').references(() => staffRoles.id), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const storeInfo = mf.table('store_info', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar({ length: 255 }).notNull(), + description: varchar({ length: 500 }), + imageUrl: varchar('image_url', { length: 500 }), + createdAt: timestamp('created_at').notNull().defaultNow(), + owner: integer('owner').notNull().references(() => staffUsers.id), +}); + +export const units = mf.table('units', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + shortNotation: varchar('short_notation', { length: 50 }).notNull(), + fullName: varchar('full_name', { length: 100 }).notNull(), +}, (t) => ({ + unq_short_notation: unique('unique_short_notation').on(t.shortNotation), +})); + +export const productAvailabilityActionEnum = pgEnum('product_availability_action', ['in', 'out']); + +export const productInfo = mf.table('product_info', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar({ length: 255 }).notNull(), + shortDescription: varchar('short_description', { length: 500 }), + longDescription: varchar('long_description', { length: 1000 }), + unitId: integer('unit_id').notNull().references(() => units.id), + price: numeric({ precision: 10, scale: 2 }).notNull(), + marketPrice: numeric('market_price', { precision: 10, scale: 2 }), + images: jsonb('images'), + isOutOfStock: boolean('is_out_of_stock').notNull().default(false), + isSuspended: boolean('is_suspended').notNull().default(false), + isFlashAvailable: boolean('is_flash_available').notNull().default(false), + flashPrice: numeric('flash_price', { precision: 10, scale: 2 }), + createdAt: timestamp('created_at').notNull().defaultNow(), + incrementStep: real('increment_step').notNull().default(1), + productQuantity: real('product_quantity').notNull().default(1), + storeId: integer('store_id').references(() => storeInfo.id), + scheduledAvailability: boolean('scheduled_availability').notNull().default(true), +}); + +export const productAvailabilitySchedules = mf.table('product_availability_schedules', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + time: varchar('time', { length: 10 }).notNull(), + scheduleName: varchar('schedule_name', { length: 255 }).notNull().unique(), + action: productAvailabilityActionEnum('action').notNull(), + productIds: integer('product_ids').array().notNull().default([]), + groupIds: integer('group_ids').array().notNull().default([]), + createdAt: timestamp('created_at').notNull().defaultNow(), + lastUpdated: timestamp('last_updated').notNull().defaultNow(), +}); + +export const productGroupInfo = mf.table('product_group_info', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + groupName: varchar('group_name', { length: 255 }).notNull(), + description: varchar({ length: 500 }), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const productGroupMembership = mf.table('product_group_membership', { + productId: integer('product_id').notNull().references(() => productInfo.id), + groupId: integer('group_id').notNull().references(() => productGroupInfo.id), + addedAt: timestamp('added_at').notNull().defaultNow(), +}, (t) => ({ + pk: unique('product_group_membership_pk').on(t.productId, t.groupId), +})); + +export const homeBanners = mf.table('home_banners', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar('name', { length: 255 }).notNull(), + imageUrl: varchar('image_url', { length: 500 }).notNull(), + description: varchar('description', { length: 500 }), + productIds: integer('product_ids').array(), + redirectUrl: varchar('redirect_url', { length: 500 }), + serialNum: integer('serial_num'), + isActive: boolean('is_active').notNull().default(false), + createdAt: timestamp('created_at').notNull().defaultNow(), + lastUpdated: timestamp('last_updated').notNull().defaultNow(), +}); + +export const productReviews = mf.table('product_reviews', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + productId: integer('product_id').notNull().references(() => productInfo.id), + reviewBody: text('review_body').notNull(), + imageUrls: jsonb('image_urls').$defaultFn(() => []), + reviewTime: timestamp('review_time').notNull().defaultNow(), + ratings: real('ratings').notNull(), + adminResponse: text('admin_response'), + adminResponseImages: jsonb('admin_response_images').$defaultFn(() => []), +}, (t) => ({ + ratingCheck: check('rating_check', sql`${t.ratings} >= 1 AND ${t.ratings} <= 5`), +})); + +export const uploadStatusEnum = pgEnum('upload_status', ['pending', 'claimed']); + +export const staffRoleEnum = pgEnum('staff_role', ['super_admin', 'admin', 'marketer', 'delivery_staff']); + +export const staffPermissionEnum = pgEnum('staff_permission', ['crud_product', 'make_coupon', 'crud_staff_users']); + +export const uploadUrlStatus = mf.table('upload_url_status', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + createdAt: timestamp('created_at').notNull().defaultNow(), + key: varchar('key', { length: 500 }).notNull(), + status: uploadStatusEnum('status').notNull().default('pending'), +}); + +export const productTagInfo = mf.table('product_tag_info', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + tagName: varchar('tag_name', { length: 100 }).notNull().unique(), + tagDescription: varchar('tag_description', { length: 500 }), + imageUrl: varchar('image_url', { length: 500 }), + isDashboardTag: boolean('is_dashboard_tag').notNull().default(false), + relatedStores: jsonb('related_stores').$defaultFn(() => []), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const productTags = mf.table('product_tags', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + productId: integer('product_id').notNull().references(() => productInfo.id), + tagId: integer('tag_id').notNull().references(() => productTagInfo.id), + assignedAt: timestamp('assigned_at').notNull().defaultNow(), +}, (t) => ({ + unq_product_tag: unique('unique_product_tag').on(t.productId, t.tagId), +})); + +export const deliverySlotInfo = mf.table('delivery_slot_info', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + deliveryTime: timestamp('delivery_time').notNull(), + freezeTime: timestamp('freeze_time').notNull(), + isActive: boolean('is_active').notNull().default(true), + isFlash: boolean('is_flash').notNull().default(false), + isCapacityFull: boolean('is_capacity_full').notNull().default(false), + deliverySequence: jsonb('delivery_sequence').$defaultFn(() => {}), + groupIds: jsonb('group_ids').$defaultFn(() => []), +}); + +export const vendorSnippets = mf.table('vendor_snippets', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + snippetCode: varchar('snippet_code', { length: 255 }).notNull().unique(), + slotId: integer('slot_id').references(() => deliverySlotInfo.id), + isPermanent: boolean('is_permanent').notNull().default(false), + productIds: integer('product_ids').array().notNull(), + validTill: timestamp('valid_till'), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const vendorSnippetsRelations = relations(vendorSnippets, ({ one }) => ({ + slot: one(deliverySlotInfo, { fields: [vendorSnippets.slotId], references: [deliverySlotInfo.id] }), +})); + +export const productSlots = mf.table('product_slots', { + productId: integer('product_id').notNull().references(() => productInfo.id), + slotId: integer('slot_id').notNull().references(() => deliverySlotInfo.id), +}, (t) => ({ + pk: unique('product_slot_pk').on(t.productId, t.slotId), +})); + +export const specialDeals = mf.table('special_deals', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + productId: integer('product_id').notNull().references(() => productInfo.id), + quantity: numeric({ precision: 10, scale: 2 }).notNull(), + price: numeric({ precision: 10, scale: 2 }).notNull(), + validTill: timestamp('valid_till').notNull(), +}); + +export const orders = mf.table('orders', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + addressId: integer('address_id').notNull().references(() => addresses.id), + slotId: integer('slot_id').references(() => deliverySlotInfo.id), + isCod: boolean('is_cod').notNull().default(false), + isOnlinePayment: boolean('is_online_payment').notNull().default(false), + paymentInfoId: integer('payment_info_id').references(() => paymentInfoTable.id), + totalAmount: numeric('total_amount', { precision: 10, scale: 2 }).notNull(), + deliveryCharge: numeric('delivery_charge', { precision: 10, scale: 2 }).notNull().default('0'), + readableId: integer('readable_id').notNull(), + adminNotes: text('admin_notes'), + userNotes: text('user_notes'), + orderGroupId: varchar('order_group_id', { length: 255 }), + orderGroupProportion: decimal('order_group_proportion', { precision: 10, scale: 4 }), + isFlashDelivery: boolean('is_flash_delivery').notNull().default(false), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const orderItems = mf.table('order_items', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + orderId: integer('order_id').notNull().references(() => orders.id), + productId: integer('product_id').notNull().references(() => productInfo.id), + quantity: varchar('quantity', { length: 50 }).notNull(), + price: numeric({ precision: 10, scale: 2 }).notNull(), + discountedPrice: numeric('discounted_price', { precision: 10, scale: 2 }), + is_packaged: boolean('is_packaged').notNull().default(false), + is_package_verified: boolean('is_package_verified').notNull().default(false), +}); + +export const paymentStatusEnum = pgEnum('payment_status', ['pending', 'success', 'cod', 'failed']); + +export const orderStatus = mf.table('order_status', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + orderTime: timestamp('order_time').notNull().defaultNow(), + userId: integer('user_id').notNull().references(() => users.id), + orderId: integer('order_id').notNull().references(() => orders.id), + isPackaged: boolean('is_packaged').notNull().default(false), + isDelivered: boolean('is_delivered').notNull().default(false), + isCancelled: boolean('is_cancelled').notNull().default(false), + cancelReason: varchar('cancel_reason', { length: 255 }), + isCancelledByAdmin: boolean('is_cancelled_by_admin'), + paymentStatus: paymentStatusEnum('payment_state').notNull().default('pending'), + cancellationUserNotes: text('cancellation_user_notes'), + cancellationAdminNotes: text('cancellation_admin_notes'), + cancellationReviewed: boolean('cancellation_reviewed').notNull().default(false), + cancellationReviewedAt: timestamp('cancellation_reviewed_at'), + refundCouponId: integer('refund_coupon_id').references(() => coupons.id), +}); + +export const paymentInfoTable = mf.table('payment_info', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + status: varchar({ length: 50 }).notNull(), + gateway: varchar({ length: 50 }).notNull(), + orderId: varchar('order_id', { length: 500 }), + token: varchar({ length: 500 }), + merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(), + payload: jsonb('payload'), +}); + +export const payments = mf.table('payments', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + status: varchar({ length: 50 }).notNull(), + gateway: varchar({ length: 50 }).notNull(), + orderId: integer('order_id').notNull().references(() => orders.id), + token: varchar({ length: 500 }), + merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(), + payload: jsonb('payload'), +}); + +export const refunds = mf.table('refunds', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + orderId: integer('order_id').notNull().references(() => orders.id), + refundAmount: numeric('refund_amount', { precision: 10, scale: 2 }), + refundStatus: varchar('refund_status', { length: 50 }).default('none'), + merchantRefundId: varchar('merchant_refund_id', { length: 255 }), + refundProcessedAt: timestamp('refund_processed_at'), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const keyValStore = mf.table('key_val_store', { + key: varchar('key', { length: 255 }).primaryKey(), + value: jsonb('value'), +}); + +export const notifications = mf.table('notifications', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + title: varchar({ length: 255 }).notNull(), + body: varchar({ length: 512 }).notNull(), + type: varchar({ length: 50 }), + isRead: boolean('is_read').notNull().default(false), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const productCategories = mf.table('product_categories', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar({ length: 255 }).notNull(), + description: varchar({ length: 500 }), +}); + +export const cartItems = mf.table('cart_items', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + productId: integer('product_id').notNull().references(() => productInfo.id), + quantity: numeric({ precision: 10, scale: 2 }).notNull(), + addedAt: timestamp('added_at').notNull().defaultNow(), +}, (t) => ({ + unq_user_product: unique('unique_user_product').on(t.userId, t.productId), +})); + +export const complaints = mf.table('complaints', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + orderId: integer('order_id').references(() => orders.id), + complaintBody: varchar('complaint_body', { length: 1000 }).notNull(), + images: jsonb('images'), + response: varchar('response', { length: 1000 }), + isResolved: boolean('is_resolved').notNull().default(false), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const coupons = mf.table('coupons', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + couponCode: varchar('coupon_code', { length: 50 }).notNull().unique('unique_coupon_code'), + isUserBased: boolean('is_user_based').notNull().default(false), + discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }), + flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }), + minOrder: numeric('min_order', { precision: 10, scale: 2 }), + productIds: jsonb('product_ids'), + createdBy: integer('created_by').references(() => staffUsers.id), + maxValue: numeric('max_value', { precision: 10, scale: 2 }), + isApplyForAll: boolean('is_apply_for_all').notNull().default(false), + validTill: timestamp('valid_till'), + maxLimitForUser: integer('max_limit_for_user'), + isInvalidated: boolean('is_invalidated').notNull().default(false), + exclusiveApply: boolean('exclusive_apply').notNull().default(false), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const couponUsage = mf.table('coupon_usage', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + couponId: integer('coupon_id').notNull().references(() => coupons.id), + orderId: integer('order_id').references(() => orders.id), + orderItemId: integer('order_item_id').references(() => orderItems.id), + usedAt: timestamp('used_at').notNull().defaultNow(), +}); + +export const couponApplicableUsers = mf.table('coupon_applicable_users', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + couponId: integer('coupon_id').notNull().references(() => coupons.id), + userId: integer('user_id').notNull().references(() => users.id), +}, (t) => ({ + unq_coupon_user: unique('unique_coupon_user').on(t.couponId, t.userId), +})); + +export const couponApplicableProducts = mf.table('coupon_applicable_products', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + couponId: integer('coupon_id').notNull().references(() => coupons.id), + productId: integer('product_id').notNull().references(() => productInfo.id), +}, (t) => ({ + unq_coupon_product: unique('unique_coupon_product').on(t.couponId, t.productId), +})); + +export const userIncidents = mf.table('user_incidents', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + userId: integer('user_id').notNull().references(() => users.id), + orderId: integer('order_id').references(() => orders.id), + dateAdded: timestamp('date_added').notNull().defaultNow(), + adminComment: text('admin_comment'), + addedBy: integer('added_by').references(() => staffUsers.id), + negativityScore: integer('negativity_score'), +}); + +export const reservedCoupons = mf.table('reserved_coupons', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + secretCode: varchar('secret_code', { length: 50 }).notNull().unique(), + couponCode: varchar('coupon_code', { length: 50 }).notNull(), + discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }), + flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }), + minOrder: numeric('min_order', { precision: 10, scale: 2 }), + productIds: jsonb('product_ids'), + maxValue: numeric('max_value', { precision: 10, scale: 2 }), + validTill: timestamp('valid_till'), + maxLimitForUser: integer('max_limit_for_user'), + exclusiveApply: boolean('exclusive_apply').notNull().default(false), + isRedeemed: boolean('is_redeemed').notNull().default(false), + redeemedBy: integer('redeemed_by').references(() => users.id), + redeemedAt: timestamp('redeemed_at'), + createdBy: integer('created_by').notNull().references(() => staffUsers.id), + createdAt: timestamp('created_at').notNull().defaultNow(), +}, (t) => ({ + unq_secret_code: unique('unique_secret_code').on(t.secretCode), +})); + +export const notifCreds = mf.table('notif_creds', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + token: varchar({ length: 500 }).notNull().unique(), + addedAt: timestamp('added_at').notNull().defaultNow(), + userId: integer('user_id').notNull().references(() => users.id), + lastVerified: timestamp('last_verified'), +}); + +export const unloggedUserTokens = mf.table('unlogged_user_tokens', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + token: varchar({ length: 500 }).notNull().unique(), + addedAt: timestamp('added_at').notNull().defaultNow(), + lastVerified: timestamp('last_verified'), +}); + +export const userNotifications = mf.table('user_notifications', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + title: varchar('title', { length: 255 }).notNull(), + imageUrl: varchar('image_url', { length: 500 }), + createdAt: timestamp('created_at').notNull().defaultNow(), + body: text('body').notNull(), + applicableUsers: jsonb('applicable_users'), +}); + +export const staffRoles = mf.table('staff_roles', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + roleName: staffRoleEnum('role_name').notNull(), + createdAt: timestamp('created_at').notNull().defaultNow(), +}, (t) => ({ + unq_role_name: unique('unique_role_name').on(t.roleName), +})); + +export const staffPermissions = mf.table('staff_permissions', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + permissionName: staffPermissionEnum('permission_name').notNull(), + createdAt: timestamp('created_at').notNull().defaultNow(), +}, (t) => ({ + unq_permission_name: unique('unique_permission_name').on(t.permissionName), +})); + +export const staffRolePermissions = mf.table('staff_role_permissions', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + staffRoleId: integer('staff_role_id').notNull().references(() => staffRoles.id), + staffPermissionId: integer('staff_permission_id').notNull().references(() => staffPermissions.id), + createdAt: timestamp('created_at').notNull().defaultNow(), +}, (t) => ({ + unq_role_permission: unique('unique_role_permission').on(t.staffRoleId, t.staffPermissionId), +})); + +// Relations +export const usersRelations = relations(users, ({ many, one }) => ({ + addresses: many(addresses), + orders: many(orders), + notifications: many(notifications), + cartItems: many(cartItems), + userCreds: one(userCreds), + coupons: many(coupons), + couponUsages: many(couponUsage), + applicableCoupons: many(couponApplicableUsers), + userDetails: one(userDetails), + notifCreds: many(notifCreds), + userIncidents: many(userIncidents), +})); + +export const userCredsRelations = relations(userCreds, ({ one }) => ({ + user: one(users, { fields: [userCreds.userId], references: [users.id] }), +})); + +export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ + role: one(staffRoles, { fields: [staffUsers.staffRoleId], references: [staffRoles.id] }), + coupons: many(coupons), + stores: many(storeInfo), +})); + +export const addressesRelations = relations(addresses, ({ one, many }) => ({ + user: one(users, { fields: [addresses.userId], references: [users.id] }), + orders: many(orders), + zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), +})); + +export const unitsRelations = relations(units, ({ many }) => ({ + products: many(productInfo), +})); + +export const productInfoRelations = relations(productInfo, ({ one, many }) => ({ + unit: one(units, { fields: [productInfo.unitId], references: [units.id] }), + store: one(storeInfo, { fields: [productInfo.storeId], references: [storeInfo.id] }), + productSlots: many(productSlots), + specialDeals: many(specialDeals), + orderItems: many(orderItems), + cartItems: many(cartItems), + tags: many(productTags), + applicableCoupons: many(couponApplicableProducts), + reviews: many(productReviews), + groups: many(productGroupMembership), +})); + +export const productTagInfoRelations = relations(productTagInfo, ({ many }) => ({ + products: many(productTags), +})); + +export const productTagsRelations = relations(productTags, ({ one }) => ({ + product: one(productInfo, { fields: [productTags.productId], references: [productInfo.id] }), + tag: one(productTagInfo, { fields: [productTags.tagId], references: [productTagInfo.id] }), +})); + +export const deliverySlotInfoRelations = relations(deliverySlotInfo, ({ many }) => ({ + productSlots: many(productSlots), + orders: many(orders), + vendorSnippets: many(vendorSnippets), +})); + +export const productSlotsRelations = relations(productSlots, ({ one }) => ({ + product: one(productInfo, { fields: [productSlots.productId], references: [productInfo.id] }), + slot: one(deliverySlotInfo, { fields: [productSlots.slotId], references: [deliverySlotInfo.id] }), +})); + +export const specialDealsRelations = relations(specialDeals, ({ one }) => ({ + product: one(productInfo, { fields: [specialDeals.productId], references: [productInfo.id] }), +})); + +export const ordersRelations = relations(orders, ({ one, many }) => ({ + user: one(users, { fields: [orders.userId], references: [users.id] }), + address: one(addresses, { fields: [orders.addressId], references: [addresses.id] }), + slot: one(deliverySlotInfo, { fields: [orders.slotId], references: [deliverySlotInfo.id] }), + orderItems: many(orderItems), + payment: one(payments), + paymentInfo: one(paymentInfoTable, { fields: [orders.paymentInfoId], references: [paymentInfoTable.id] }), + orderStatus: many(orderStatus), + refunds: many(refunds), + couponUsages: many(couponUsage), + userIncidents: many(userIncidents), +})); + +export const orderItemsRelations = relations(orderItems, ({ one }) => ({ + order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }), + product: one(productInfo, { fields: [orderItems.productId], references: [productInfo.id] }), +})); + +export const orderStatusRelations = relations(orderStatus, ({ one }) => ({ + order: one(orders, { fields: [orderStatus.orderId], references: [orders.id] }), + user: one(users, { fields: [orderStatus.userId], references: [users.id] }), + refundCoupon: one(coupons, { fields: [orderStatus.refundCouponId], references: [coupons.id] }), +})); + +export const paymentInfoRelations = relations(paymentInfoTable, ({ one }) => ({ + order: one(orders, { fields: [paymentInfoTable.id], references: [orders.paymentInfoId] }), +})); + +export const paymentsRelations = relations(payments, ({ one }) => ({ + order: one(orders, { fields: [payments.orderId], references: [orders.id] }), +})); + +export const refundsRelations = relations(refunds, ({ one }) => ({ + order: one(orders, { fields: [refunds.orderId], references: [orders.id] }), +})); + +export const notificationsRelations = relations(notifications, ({ one }) => ({ + user: one(users, { fields: [notifications.userId], references: [users.id] }), +})); + +export const productCategoriesRelations = relations(productCategories, ({}) => ({})); + +export const cartItemsRelations = relations(cartItems, ({ one }) => ({ + user: one(users, { fields: [cartItems.userId], references: [users.id] }), + product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }), +})); + +export const complaintsRelations = relations(complaints, ({ one }) => ({ + user: one(users, { fields: [complaints.userId], references: [users.id] }), + order: one(orders, { fields: [complaints.orderId], references: [orders.id] }), +})); + +export const couponsRelations = relations(coupons, ({ one, many }) => ({ + creator: one(staffUsers, { fields: [coupons.createdBy], references: [staffUsers.id] }), + usages: many(couponUsage), + applicableUsers: many(couponApplicableUsers), + applicableProducts: many(couponApplicableProducts), +})); + +export const couponUsageRelations = relations(couponUsage, ({ one }) => ({ + user: one(users, { fields: [couponUsage.userId], references: [users.id] }), + coupon: one(coupons, { fields: [couponUsage.couponId], references: [coupons.id] }), + order: one(orders, { fields: [couponUsage.orderId], references: [orders.id] }), + orderItem: one(orderItems, { fields: [couponUsage.orderItemId], references: [orderItems.id] }), +})); + +export const userDetailsRelations = relations(userDetails, ({ one }) => ({ + user: one(users, { fields: [userDetails.userId], references: [users.id] }), +})); + +export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ + user: one(users, { fields: [notifCreds.userId], references: [users.id] }), +})); + +export const userNotificationsRelations = relations(userNotifications, ({}) => ({ + // No relations needed for now +})); + +export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ + owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), + products: many(productInfo), +})); + +export const couponApplicableUsersRelations = relations(couponApplicableUsers, ({ one }) => ({ + coupon: one(coupons, { fields: [couponApplicableUsers.couponId], references: [coupons.id] }), + user: one(users, { fields: [couponApplicableUsers.userId], references: [users.id] }), +})); + +export const couponApplicableProductsRelations = relations(couponApplicableProducts, ({ one }) => ({ + coupon: one(coupons, { fields: [couponApplicableProducts.couponId], references: [coupons.id] }), + product: one(productInfo, { fields: [couponApplicableProducts.productId], references: [productInfo.id] }), +})); + +export const reservedCouponsRelations = relations(reservedCoupons, ({ one }) => ({ + redeemedUser: one(users, { fields: [reservedCoupons.redeemedBy], references: [users.id] }), + creator: one(staffUsers, { fields: [reservedCoupons.createdBy], references: [staffUsers.id] }), +})); + +export const productReviewsRelations = relations(productReviews, ({ one }) => ({ + user: one(users, { fields: [productReviews.userId], references: [users.id] }), + product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), +})); + +export const addressZonesRelations = relations(addressZones, ({ many }) => ({ + addresses: many(addresses), + areas: many(addressAreas), +})); + +export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ + zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), +})); + +export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ + memberships: many(productGroupMembership), +})); + +export const productGroupMembershipRelations = relations(productGroupMembership, ({ one }) => ({ + product: one(productInfo, { fields: [productGroupMembership.productId], references: [productInfo.id] }), + group: one(productGroupInfo, { fields: [productGroupMembership.groupId], references: [productGroupInfo.id] }), +})); + +export const homeBannersRelations = relations(homeBanners, ({}) => ({ + // Relations for productIds array would be more complex, skipping for now +})); + +export const staffRolesRelations = relations(staffRoles, ({ many }) => ({ + staffUsers: many(staffUsers), + rolePermissions: many(staffRolePermissions), +})); + +export const staffPermissionsRelations = relations(staffPermissions, ({ many }) => ({ + rolePermissions: many(staffRolePermissions), +})); + +export const staffRolePermissionsRelations = relations(staffRolePermissions, ({ one }) => ({ + role: one(staffRoles, { fields: [staffRolePermissions.staffRoleId], references: [staffRoles.id] }), + permission: one(staffPermissions, { fields: [staffRolePermissions.staffPermissionId], references: [staffPermissions.id] }), +})); + +export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({ + user: one(users, { fields: [userIncidents.userId], references: [users.id] }), + order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }), + addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }), +})); + +export const productAvailabilitySchedulesRelations = relations(productAvailabilitySchedules, ({}) => ({ +})); diff --git a/apps/db-helper-postgres/src/db/seed.ts b/apps/db-helper-postgres/src/db/seed.ts new file mode 100644 index 0000000..91578cb --- /dev/null +++ b/apps/db-helper-postgres/src/db/seed.ts @@ -0,0 +1,153 @@ +import { eq } from 'drizzle-orm' + +import { CONST_KEYS } from '../../../backend/src/lib/const-keys' +import { deliveryCharge, minOrderValue } from '../../../backend/src/lib/env-exporter' + +import { db } from './db_index' +import { keyValStore, staffPermissions, staffRolePermissions, staffRoles, units } from './schema' + +export async function seed() { + console.log('Seeding database...') + + const unitsToSeed = [ + { shortNotation: 'Kg', fullName: 'Kilogram' }, + { shortNotation: 'L', fullName: 'Litre' }, + { shortNotation: 'Dz', fullName: 'Dozen' }, + { shortNotation: 'Pc', fullName: 'Unit Piece' }, + ] + + for (const unit of unitsToSeed) { + const existingUnit = await db.query.units.findFirst({ + where: eq(units.shortNotation, unit.shortNotation), + }) + if (!existingUnit) { + await db.insert(units).values(unit) + } + } + + const rolesToSeed = ['super_admin', 'admin', 'marketer', 'delivery_staff'] as const + + for (const roleName of rolesToSeed) { + const existingRole = await db.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, roleName), + }) + if (!existingRole) { + await db.insert(staffRoles).values({ roleName }) + } + } + + const permissionsToSeed = ['crud_product', 'make_coupon', 'crud_staff_users'] as const + + for (const permissionName of permissionsToSeed) { + const existingPermission = await db.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, permissionName), + }) + if (!existingPermission) { + await db.insert(staffPermissions).values({ permissionName }) + } + } + + await db.transaction(async (tx) => { + const superAdminRole = await tx.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, 'super_admin'), + }) + const adminRole = await tx.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, 'admin'), + }) + const marketerRole = await tx.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, 'marketer'), + }) + + const crudProductPerm = await tx.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, 'crud_product'), + }) + const makeCouponPerm = await tx.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, 'make_coupon'), + }) + const crudStaffUsersPerm = await tx.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, 'crud_staff_users'), + }) + + await Promise.all( + [crudProductPerm, makeCouponPerm, crudStaffUsersPerm].map(async (perm) => { + if (!superAdminRole || !perm) { + return + } + + const existingSuperAdminPerm = await tx.query.staffRolePermissions.findFirst({ + where: eq(staffRolePermissions.staffRoleId, superAdminRole.id) + && eq(staffRolePermissions.staffPermissionId, perm.id), + }) + if (!existingSuperAdminPerm) { + await tx.insert(staffRolePermissions).values({ + staffRoleId: superAdminRole.id, + staffPermissionId: perm.id, + }) + } + }) + ) + + await Promise.all( + [crudProductPerm, makeCouponPerm].map(async (perm) => { + if (!adminRole || !perm) { + return + } + + const existingAdminPerm = await tx.query.staffRolePermissions.findFirst({ + where: eq(staffRolePermissions.staffRoleId, adminRole.id) + && eq(staffRolePermissions.staffPermissionId, perm.id), + }) + if (!existingAdminPerm) { + await tx.insert(staffRolePermissions).values({ + staffRoleId: adminRole.id, + staffPermissionId: perm.id, + }) + } + }) + ) + + if (marketerRole && makeCouponPerm) { + const existingMarketerCoupon = await tx.query.staffRolePermissions.findFirst({ + where: eq(staffRolePermissions.staffRoleId, marketerRole.id) + && eq(staffRolePermissions.staffPermissionId, makeCouponPerm.id), + }) + if (!existingMarketerCoupon) { + await tx.insert(staffRolePermissions).values({ + staffRoleId: marketerRole.id, + staffPermissionId: makeCouponPerm.id, + }) + } + } + }) + + const constantsToSeed = [ + { key: CONST_KEYS.readableOrderId, value: 0 }, + { key: CONST_KEYS.minRegularOrderValue, value: minOrderValue }, + { key: CONST_KEYS.freeDeliveryThreshold, value: minOrderValue }, + { key: CONST_KEYS.deliveryCharge, value: deliveryCharge }, + { key: CONST_KEYS.flashFreeDeliveryThreshold, value: 500 }, + { key: CONST_KEYS.flashDeliveryCharge, value: 69 }, + { key: CONST_KEYS.popularItems, value: [] }, + { key: CONST_KEYS.allItemsOrder, value: [] }, + { key: CONST_KEYS.versionNum, value: '1.1.0' }, + { key: CONST_KEYS.playStoreUrl, value: 'https://play.google.com/store/apps/details?id=in.freshyo.app' }, + { key: CONST_KEYS.appStoreUrl, value: 'https://apps.apple.com/in/app/freshyo/id6756889077' }, + { key: CONST_KEYS.isFlashDeliveryEnabled, value: false }, + { key: CONST_KEYS.supportMobile, value: '8688182552' }, + { key: CONST_KEYS.supportEmail, value: 'qushammohd@gmail.com' }, + ] + + for (const constant of constantsToSeed) { + const existing = await db.query.keyValStore.findFirst({ + where: eq(keyValStore.key, constant.key), + }) + if (!existing) { + await db.insert(keyValStore).values({ + key: constant.key, + value: constant.value, + }) + } + } + + console.log('Seeding completed.') +} diff --git a/apps/db-helper-postgres/src/db/types.ts b/apps/db-helper-postgres/src/db/types.ts new file mode 100644 index 0000000..902b5c0 --- /dev/null +++ b/apps/db-helper-postgres/src/db/types.ts @@ -0,0 +1,47 @@ +import type { InferSelectModel } from 'drizzle-orm' + +import type { + users, + addresses, + units, + productInfo, + deliverySlotInfo, + productSlots, + specialDeals, + orders, + orderItems, + payments, + notifications, + productCategories, + cartItems, + coupons, +} from './schema' + +export type User = InferSelectModel +export type Address = InferSelectModel +export type Unit = InferSelectModel +export type ProductInfo = InferSelectModel +export type DeliverySlotInfo = InferSelectModel +export type ProductSlot = InferSelectModel +export type SpecialDeal = InferSelectModel +export type Order = InferSelectModel +export type OrderItem = InferSelectModel +export type Payment = InferSelectModel +export type Notification = InferSelectModel +export type ProductCategory = InferSelectModel +export type CartItem = InferSelectModel +export type Coupon = InferSelectModel + +export type ProductWithUnit = ProductInfo & { + unit: Unit +} + +export type OrderWithItems = Order & { + items: (OrderItem & { product: ProductInfo })[] + address: Address + slot: DeliverySlotInfo +} + +export type CartItemWithProduct = CartItem & { + product: ProductInfo +} diff --git a/apps/backend/src/lib/automatedJobs.ts b/apps/db-helper-postgres/src/lib/automatedJobs.ts similarity index 96% rename from apps/backend/src/lib/automatedJobs.ts rename to apps/db-helper-postgres/src/lib/automatedJobs.ts index 12ce354..c2cee0d 100644 --- a/apps/backend/src/lib/automatedJobs.ts +++ b/apps/db-helper-postgres/src/lib/automatedJobs.ts @@ -1,6 +1,6 @@ import * as cron from 'node-cron'; -import { db } from '@/src/db/db_index' -import { productInfo, productAvailabilitySchedules } from '@/src/db/schema' +import { db } from '../db/db_index' +import { productInfo, productAvailabilitySchedules } from '../db/schema' import { inArray } from 'drizzle-orm'; import { initializeAllStores } from '../stores/store-initializer'; diff --git a/apps/backend/src/lib/cloud_cache.ts b/apps/db-helper-postgres/src/lib/cloud_cache.ts similarity index 99% rename from apps/backend/src/lib/cloud_cache.ts rename to apps/db-helper-postgres/src/lib/cloud_cache.ts index 1db648d..ea0215a 100644 --- a/apps/backend/src/lib/cloud_cache.ts +++ b/apps/db-helper-postgres/src/lib/cloud_cache.ts @@ -5,8 +5,8 @@ import { scaffoldStores } from '@/src/trpc/apis/user-apis/apis/stores' import { scaffoldSlotsWithProducts } from '@/src/trpc/apis/user-apis/apis/slots' import { scaffoldBanners } from '@/src/trpc/apis/user-apis/apis/banners' import { scaffoldStoreWithProducts } from '@/src/trpc/apis/user-apis/apis/stores' -import { storeInfo } from '@/src/db/schema' -import { db } from '@/src/db/db_index' +import { storeInfo } from '../db/schema' +import { db } from '../db/db_index' import { imageUploadS3 } from '@/src/lib/s3-client' import { apiCacheKey, cloudflareApiToken, cloudflareZoneId, assetsDomain } from '@/src/lib/env-exporter' import { CACHE_FILENAMES } from '@packages/shared' diff --git a/apps/backend/src/lib/const-store.ts b/apps/db-helper-postgres/src/lib/const-store.ts similarity index 95% rename from apps/backend/src/lib/const-store.ts rename to apps/db-helper-postgres/src/lib/const-store.ts index c16609e..25d1142 100644 --- a/apps/backend/src/lib/const-store.ts +++ b/apps/db-helper-postgres/src/lib/const-store.ts @@ -1,5 +1,5 @@ -import { db } from '@/src/db/db_index' -import { keyValStore } from '@/src/db/schema' +import { db } from '../db/db_index' +import { keyValStore } from '../db/schema' import redisClient from '@/src/lib/redis-client' import { CONST_KEYS, CONST_KEYS_ARRAY, type ConstKey } from '@/src/lib/const-keys' diff --git a/apps/backend/src/lib/delete-orders.ts b/apps/db-helper-postgres/src/lib/delete-orders.ts similarity index 94% rename from apps/backend/src/lib/delete-orders.ts rename to apps/db-helper-postgres/src/lib/delete-orders.ts index 4fb9516..4321bb9 100644 --- a/apps/backend/src/lib/delete-orders.ts +++ b/apps/db-helper-postgres/src/lib/delete-orders.ts @@ -1,5 +1,5 @@ -import { db } from '@/src/db/db_index' -import { orders, orderItems, orderStatus, payments, refunds, couponUsage, complaints } from '@/src/db/schema' +import { db } from '../db/db_index' +import { orders, orderItems, orderStatus, payments, refunds, couponUsage, complaints } from '../db/schema' import { eq, inArray } from 'drizzle-orm'; /** diff --git a/apps/backend/src/lib/manage-scheduled-availability.ts b/apps/db-helper-postgres/src/lib/manage-scheduled-availability.ts similarity index 97% rename from apps/backend/src/lib/manage-scheduled-availability.ts rename to apps/db-helper-postgres/src/lib/manage-scheduled-availability.ts index 95c3815..ce4779b 100644 --- a/apps/backend/src/lib/manage-scheduled-availability.ts +++ b/apps/db-helper-postgres/src/lib/manage-scheduled-availability.ts @@ -1,5 +1,5 @@ -import { db } from '@/src/db/db_index' -import { productInfo, productAvailabilitySchedules } from '@/src/db/schema' +import { db } from '../db/db_index' +import { productInfo, productAvailabilitySchedules } from '../db/schema' import { eq, inArray } from 'drizzle-orm'; import { initializeAllStores } from '@/src/stores/store-initializer'; import dayjs from 'dayjs'; diff --git a/apps/backend/src/lib/notif-job.ts b/apps/db-helper-postgres/src/lib/notif-job.ts similarity index 99% rename from apps/backend/src/lib/notif-job.ts rename to apps/db-helper-postgres/src/lib/notif-job.ts index c0c18f9..489e696 100644 --- a/apps/backend/src/lib/notif-job.ts +++ b/apps/db-helper-postgres/src/lib/notif-job.ts @@ -1,7 +1,7 @@ import { Queue, Worker } from 'bullmq'; import { Expo } from 'expo-server-sdk'; import { redisUrl } from '@/src/lib/env-exporter' -import { db } from '@/src/db/db_index' +import { db } from '../db/db_index' import { generateSignedUrlFromS3Url } from '@/src/lib/s3-client' import { NOTIFS_QUEUE, diff --git a/apps/backend/src/lib/post-order-handler.ts b/apps/db-helper-postgres/src/lib/post-order-handler.ts similarity index 98% rename from apps/backend/src/lib/post-order-handler.ts rename to apps/db-helper-postgres/src/lib/post-order-handler.ts index f29f940..2245bbb 100644 --- a/apps/backend/src/lib/post-order-handler.ts +++ b/apps/db-helper-postgres/src/lib/post-order-handler.ts @@ -1,5 +1,5 @@ -import { db } from '@/src/db/db_index' -import { orders, orderStatus } from '@/src/db/schema' +import { db } from '../db/db_index' +import { orders, orderStatus } from '../db/schema' import redisClient from '@/src/lib/redis-client' import { sendTelegramMessage } from '@/src/lib/telegram-service' import { inArray, eq } from 'drizzle-orm'; diff --git a/apps/db-helper-postgres/src/lib/upload-url.ts b/apps/db-helper-postgres/src/lib/upload-url.ts new file mode 100644 index 0000000..9f263c9 --- /dev/null +++ b/apps/db-helper-postgres/src/lib/upload-url.ts @@ -0,0 +1,23 @@ +import { and, eq } from 'drizzle-orm' + +import { db } from '../db/db_index' +import { uploadUrlStatus } from '../db/schema' + +export const createUploadUrlStatus = async (key: string): Promise => { + await db.insert(uploadUrlStatus).values({ + key, + status: 'pending', + }) +} + +export const claimUploadUrlStatus = async (key: string): Promise => { + const result = await db + .update(uploadUrlStatus) + .set({ status: 'claimed' }) + .where(and(eq(uploadUrlStatus.key, key), eq(uploadUrlStatus.status, 'pending'))) + .returning() + + if (result.length === 0) { + throw new Error('Upload URL not found or already claimed') + } +} diff --git a/apps/backend/src/middleware/auth.middleware.ts b/apps/db-helper-postgres/src/middleware/auth.middleware.ts similarity index 95% rename from apps/backend/src/middleware/auth.middleware.ts rename to apps/db-helper-postgres/src/middleware/auth.middleware.ts index 439dd9e..f0ec185 100644 --- a/apps/backend/src/middleware/auth.middleware.ts +++ b/apps/db-helper-postgres/src/middleware/auth.middleware.ts @@ -1,6 +1,6 @@ import { createMiddleware } from 'hono/factory' -import { db } from '@/src/db/db_index' -import { staffUsers, userDetails } from '@/src/db/schema' +import { db } from '../db/db_index' +import { staffUsers, userDetails } from '../db/schema' import { eq } from 'drizzle-orm' import { ApiError } from '@/src/lib/api-error' import { verifyToken, UserJWTPayload, StaffJWTPayload } from '@/src/lib/jwt-utils' diff --git a/apps/backend/src/middleware/staff-auth.ts b/apps/db-helper-postgres/src/middleware/staff-auth.ts similarity index 95% rename from apps/backend/src/middleware/staff-auth.ts rename to apps/db-helper-postgres/src/middleware/staff-auth.ts index e2197e7..5a76e5c 100644 --- a/apps/backend/src/middleware/staff-auth.ts +++ b/apps/db-helper-postgres/src/middleware/staff-auth.ts @@ -1,6 +1,6 @@ import { createMiddleware } from 'hono/factory' -import { db } from '@/src/db/db_index' -import { staffUsers } from '@/src/db/schema' +import { db } from '../db/db_index' +import { staffUsers } from '../db/schema' import { eq } from 'drizzle-orm'; import { ApiError } from '@/src/lib/api-error' import { verifyToken, StaffJWTPayload } from '@/src/lib/jwt-utils' diff --git a/apps/backend/src/services/user/product-service.ts b/apps/db-helper-postgres/src/services/user/product-service.ts similarity index 93% rename from apps/backend/src/services/user/product-service.ts rename to apps/db-helper-postgres/src/services/user/product-service.ts index 0f214d9..01040e0 100644 --- a/apps/backend/src/services/user/product-service.ts +++ b/apps/db-helper-postgres/src/services/user/product-service.ts @@ -1,5 +1,5 @@ -import { db } from '@/src/db/db_index' -import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productReviews, users } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productReviews, users } from '../../db/schema' import { eq, and, gt, sql, desc } from 'drizzle-orm' /** @@ -55,8 +55,8 @@ export async function getProductDeliverySlots(productId: number) { and( eq(productSlots.productId, productId), eq(deliverySlotInfo.isActive, true), - gt(deliverySlotInfo.deliveryTime, sql`NOW()`), - gt(deliverySlotInfo.freezeTime, sql`NOW()`) + gt(deliverySlotInfo.deliveryTime, new Date()), + gt(deliverySlotInfo.freezeTime, new Date()) ) ) .orderBy(deliverySlotInfo.deliveryTime) @@ -76,7 +76,7 @@ export async function getProductSpecialDeals(productId: number) { .where( and( eq(specialDeals.productId, productId), - gt(specialDeals.validTill, sql`NOW()`) + gt(specialDeals.validTill, new Date()) ) ) .orderBy(specialDeals.quantity) diff --git a/apps/backend/src/stores/banner-store.ts b/apps/db-helper-postgres/src/stores/banner-store.ts similarity index 96% rename from apps/backend/src/stores/banner-store.ts rename to apps/db-helper-postgres/src/stores/banner-store.ts index 7fbd6f1..a9b79c6 100644 --- a/apps/backend/src/stores/banner-store.ts +++ b/apps/db-helper-postgres/src/stores/banner-store.ts @@ -1,7 +1,7 @@ // import redisClient from '@/src/stores/redis-client'; import redisClient from '@/src/lib/redis-client'; -import { db } from '@/src/db/db_index' -import { homeBanners } from '@/src/db/schema' +import { db } from '../db/db_index' +import { homeBanners } from '../db/schema' import { isNotNull, asc } from 'drizzle-orm'; import { scaffoldAssetUrl } from '@/src/lib/s3-client'; diff --git a/apps/backend/src/stores/product-store.ts b/apps/db-helper-postgres/src/stores/product-store.ts similarity index 96% rename from apps/backend/src/stores/product-store.ts rename to apps/db-helper-postgres/src/stores/product-store.ts index d4cf3a2..beed758 100644 --- a/apps/backend/src/stores/product-store.ts +++ b/apps/db-helper-postgres/src/stores/product-store.ts @@ -1,7 +1,7 @@ // import redisClient from '@/src/stores/redis-client'; import redisClient from '@/src/lib/redis-client'; -import { db } from '@/src/db/db_index' -import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productTags, productTagInfo } from '@/src/db/schema' +import { db } from '../db/db_index' +import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productTags, productTagInfo } from '../db/schema' import { eq, and, gt, sql } from 'drizzle-orm'; import { generateSignedUrlsFromS3Urls, scaffoldAssetUrl } from '@/src/lib/s3-client'; @@ -72,7 +72,7 @@ export async function initializeProducts(): Promise { and( eq(deliverySlotInfo.isActive, true), eq(deliverySlotInfo.isCapacityFull, false), - gt(deliverySlotInfo.deliveryTime, sql`NOW()`) + gt(deliverySlotInfo.deliveryTime, new Date()) ) ); const deliverySlotsMap = new Map(); @@ -90,7 +90,7 @@ export async function initializeProducts(): Promise { validTill: specialDeals.validTill, }) .from(specialDeals) - .where(gt(specialDeals.validTill, sql`NOW()`)); + .where(gt(specialDeals.validTill, new Date())); const specialDealsMap = new Map(); for (const deal of allSpecialDeals) { if (!specialDealsMap.has(deal.productId)) specialDealsMap.set(deal.productId, []); diff --git a/apps/backend/src/stores/product-tag-store.ts b/apps/db-helper-postgres/src/stores/product-tag-store.ts similarity index 97% rename from apps/backend/src/stores/product-tag-store.ts rename to apps/db-helper-postgres/src/stores/product-tag-store.ts index f6a3175..58a4999 100644 --- a/apps/backend/src/stores/product-tag-store.ts +++ b/apps/db-helper-postgres/src/stores/product-tag-store.ts @@ -1,7 +1,7 @@ // import redisClient from '@/src/stores/redis-client'; import redisClient from '@/src/lib/redis-client'; -import { db } from '@/src/db/db_index' -import { productTagInfo, productTags } from '@/src/db/schema' +import { db } from '../db/db_index' +import { productTagInfo, productTags } from '../db/schema' import { eq, inArray } from 'drizzle-orm'; import { generateSignedUrlFromS3Url } from '@/src/lib/s3-client'; diff --git a/apps/backend/src/stores/slot-store.ts b/apps/db-helper-postgres/src/stores/slot-store.ts similarity index 99% rename from apps/backend/src/stores/slot-store.ts rename to apps/db-helper-postgres/src/stores/slot-store.ts index 3a3ed68..1c29998 100644 --- a/apps/backend/src/stores/slot-store.ts +++ b/apps/db-helper-postgres/src/stores/slot-store.ts @@ -1,6 +1,6 @@ import redisClient from '@/src/lib/redis-client'; -import { db } from '@/src/db/db_index' -import { deliverySlotInfo, productSlots, productInfo, units } from '@/src/db/schema' +import { db } from '../db/db_index' +import { deliverySlotInfo, productSlots, productInfo, units } from '../db/schema' import { eq, and, gt, asc } from 'drizzle-orm'; import { generateSignedUrlsFromS3Urls, scaffoldAssetUrl } from '@/src/lib/s3-client'; import dayjs from 'dayjs'; diff --git a/apps/backend/src/stores/user-negativity-store.ts b/apps/db-helper-postgres/src/stores/user-negativity-store.ts similarity index 97% rename from apps/backend/src/stores/user-negativity-store.ts rename to apps/db-helper-postgres/src/stores/user-negativity-store.ts index d518435..56db6d8 100644 --- a/apps/backend/src/stores/user-negativity-store.ts +++ b/apps/db-helper-postgres/src/stores/user-negativity-store.ts @@ -1,6 +1,6 @@ import redisClient from '@/src/lib/redis-client'; -import { db } from '@/src/db/db_index' -import { userIncidents } from '@/src/db/schema' +import { db } from '../db/db_index' +import { userIncidents } from '../db/schema' import { eq, sum } from 'drizzle-orm'; export async function initializeUserNegativityStore(): Promise { diff --git a/apps/backend/src/trpc/apis/common-apis/common-trpc-index.ts b/apps/db-helper-postgres/src/trpc/apis/common-apis/common-trpc-index.ts similarity index 97% rename from apps/backend/src/trpc/apis/common-apis/common-trpc-index.ts rename to apps/db-helper-postgres/src/trpc/apis/common-apis/common-trpc-index.ts index b45764c..783b0e6 100644 --- a/apps/backend/src/trpc/apis/common-apis/common-trpc-index.ts +++ b/apps/db-helper-postgres/src/trpc/apis/common-apis/common-trpc-index.ts @@ -1,7 +1,7 @@ import { router, publicProcedure, protectedProcedure } from '@/src/trpc/trpc-index' import { commonRouter } from '@/src/trpc/apis/common-apis/common' -import { db } from '@/src/db/db_index' -import { keyValStore, productInfo, storeInfo } from '@/src/db/schema' +import { db } from '../../../db/db_index' +import { keyValStore, productInfo, storeInfo } from '../../../db/schema' import * as turf from '@turf/turf'; import { z } from 'zod'; import { mbnrGeoJson } from '@/src/lib/mbnr-geojson' diff --git a/apps/backend/src/trpc/apis/common-apis/common.ts b/apps/db-helper-postgres/src/trpc/apis/common-apis/common.ts similarity index 96% rename from apps/backend/src/trpc/apis/common-apis/common.ts rename to apps/db-helper-postgres/src/trpc/apis/common-apis/common.ts index 2896035..7a2f355 100644 --- a/apps/backend/src/trpc/apis/common-apis/common.ts +++ b/apps/db-helper-postgres/src/trpc/apis/common-apis/common.ts @@ -1,6 +1,6 @@ import { router, publicProcedure } from '@/src/trpc/trpc-index' -import { db } from '@/src/db/db_index' -import { productInfo, units, productSlots, deliverySlotInfo, storeInfo } from '@/src/db/schema' +import { db } from '../../../db/db_index' +import { productInfo, units, productSlots, deliverySlotInfo, storeInfo } from '../../../db/schema' import { eq, gt, and, sql, inArray } from 'drizzle-orm'; import { generateSignedUrlsFromS3Urls, generateSignedUrlFromS3Url } from '@/src/lib/s3-client' import { getAllProducts as getAllProductsFromCache } from '@/src/stores/product-store' @@ -16,7 +16,7 @@ export const getNextDeliveryDate = async (productId: number): Promise { + if (!images) { + return [] + } + + try { + const parsed = JSON.parse(images) + if (Array.isArray(parsed)) { + return parsed.filter((value) => typeof value === 'string') + } + } catch (error) { + console.error('Failed to parse product images', error) + } + + return [] +} + +const getNextDeliveryDate = async (productId: number): Promise => { + const result = await db + .select({ deliveryTime: deliverySlotInfo.deliveryTime }) + .from(productSlots) + .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) + .where( + and( + eq(productSlots.productId, productId), + eq(deliverySlotInfo.isActive, true), + gt(deliverySlotInfo.deliveryTime, new Date()) + ) + ) + .orderBy(deliverySlotInfo.deliveryTime) + .limit(1) + + return result[0]?.deliveryTime || null +} + +export const getProductsSummaryData = async (tagId?: number | null): Promise => { + let productIds: number[] | null = null + + if (tagId) { + const taggedProducts = await db + .select({ productId: productTags.productId }) + .from(productTags) + .where(eq(productTags.tagId, tagId)) + + productIds = taggedProducts.map((taggedProduct) => taggedProduct.productId) + + if (productIds.length === 0) { + return [] + } + } + + const whereCondition = productIds && productIds.length > 0 + ? inArray(productInfo.id, productIds) + : undefined + + const productsWithUnits = await db + .select({ + id: productInfo.id, + name: productInfo.name, + shortDescription: productInfo.shortDescription, + price: productInfo.price, + marketPrice: productInfo.marketPrice, + images: productInfo.images, + isOutOfStock: productInfo.isOutOfStock, + unitShortNotation: units.shortNotation, + productQuantity: productInfo.productQuantity, + }) + .from(productInfo) + .innerJoin(units, eq(productInfo.unitId, units.id)) + .where(whereCondition) + + const productsWithDelivery = await Promise.all( + productsWithUnits.map(async (product) => { + const nextDeliveryDate = await getNextDeliveryDate(product.id) + return { + ...product, + images: normalizeImages(product.images), + nextDeliveryDate, + } + }) + ) + + return productsWithDelivery +} diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/banner-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/banner-queries.ts similarity index 81% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/banner-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/banner-queries.ts index 9b62365..6105745 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/banner-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/banner-queries.ts @@ -1,8 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { homeBanners } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { homeBanners } from '../../db/schema' import { eq, desc } from 'drizzle-orm' -import { IBannerDbService, Banner, NewBanner } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/banner-db-service.interface' -import { toJsonString } from '@/src/db/sqlite-casts' +import { IBannerDbService, Banner, NewBanner } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/banner-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' export class BannerDbService implements IBannerDbService { async getAllBanners(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/complaint-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/complaint-queries.ts similarity index 83% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/complaint-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/complaint-queries.ts index 79376aa..f5b9fff 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/complaint-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/complaint-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { complaints, users } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { complaints, users } from '../../db/schema' import { eq, desc, lt } from 'drizzle-orm' -import { IComplaintDbService, Complaint, NewComplaint } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/complaint-db-service.interface' +import { IComplaintDbService, Complaint, NewComplaint } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/complaint-db-service.interface' export class ComplaintDbService implements IComplaintDbService { async getComplaints( diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/constant-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/constant-queries.ts similarity index 70% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/constant-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/constant-queries.ts index d12a84b..d665265 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/constant-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/constant-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { keyValStore } from '@/src/db/schema' -import { IConstantDbService, Constant, NewConstant } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/constant-db-service.interface' -import { toJsonString } from '@/src/db/sqlite-casts' +import { db } from '../../db/db_index' +import { keyValStore } from '../../db/schema' +import { IConstantDbService, Constant, NewConstant } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/constant-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' export class ConstantDbService implements IConstantDbService { async getAllConstants(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/coupon-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/coupon-queries.ts similarity index 95% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/coupon-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/coupon-queries.ts index 60d8d44..6f148f6 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/coupon-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/coupon-queries.ts @@ -1,8 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { coupons, couponApplicableUsers, couponApplicableProducts, reservedCoupons, users, orders, orderStatus } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { coupons, couponApplicableUsers, couponApplicableProducts, reservedCoupons, users, orders, orderStatus } from '../../db/schema' import { eq, and, like, or, inArray, lt, asc } from 'drizzle-orm' -import { ICouponDbService, Coupon, NewCoupon, ReservedCoupon, NewReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/coupon-db-service.interface' -import { parseNumberArray, toJsonString } from '@/src/db/sqlite-casts' +import { ICouponDbService, Coupon, NewCoupon, ReservedCoupon, NewReservedCoupon, CouponWithRelations } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/coupon-db-service.interface' +import { parseNumberArray, toJsonString } from '../../db/sqlite-casts' export class CouponDbService implements ICouponDbService { async createCoupon(data: NewCoupon): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/order-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/order-queries.ts similarity index 98% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/order-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/order-queries.ts index 0bc7798..e9c3b28 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/order-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/order-queries.ts @@ -1,4 +1,4 @@ -import { db } from '@/src/db/db_index_sqlite' +import { db } from '../../db/db_index' import { orders, orderItems, @@ -14,7 +14,7 @@ import { productInfo, units, paymentInfoTable, -} from '@/src/db/schema' +} from '../../db/schema' import { eq, and, gte, lt, desc, inArray, SQL } from 'drizzle-orm' import { IOrderDbService, @@ -26,7 +26,7 @@ import { OrderWithRelations, OrderWithStatus, OrderWithCouponUsages, -} from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/order-db-service.interface' +} from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/order-db-service.interface' export class OrderDbService implements IOrderDbService { async updateOrderNotes(orderId: number, adminNotes: string | null): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/product-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/product-queries.ts similarity index 96% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/product-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/product-queries.ts index 31bcdf1..7da83c2 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/product-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/product-queries.ts @@ -1,8 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { productInfo, units, specialDeals, productSlots, productTags, productReviews, productGroupInfo, productGroupMembership, users } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productInfo, units, specialDeals, productSlots, productTags, productReviews, productGroupInfo, productGroupMembership, users } from '../../db/schema' import { eq, and, inArray, desc, sql } from 'drizzle-orm' -import { IProductDbService, Product, NewProduct, ProductGroup, NewProductGroup } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/product-db-service.interface' -import { toJsonString } from '@/src/db/sqlite-casts' +import { IProductDbService, Product, NewProduct, ProductGroup, NewProductGroup } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/product-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' export class ProductDbService implements IProductDbService { async getAllProducts(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/refund-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/refund-queries.ts similarity index 84% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/refund-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/refund-queries.ts index 7ea1257..5fe737a 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/refund-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/refund-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { refunds, orders, orderStatus, payments } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { refunds, orders, orderStatus, payments } from '../../db/schema' import { eq, and } from 'drizzle-orm' -import { IRefundDbService, Refund, NewRefund } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/refund-db-service.interface' +import { IRefundDbService, Refund, NewRefund } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/refund-db-service.interface' export class RefundDbService implements IRefundDbService { async createRefund(data: NewRefund): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/schedule-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/schedule-queries.ts similarity index 86% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/schedule-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/schedule-queries.ts index 5f88f17..21e86eb 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/schedule-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/schedule-queries.ts @@ -1,8 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { productAvailabilitySchedules } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productAvailabilitySchedules } from '../../db/schema' import { eq, desc } from 'drizzle-orm' -import { IScheduleDbService, Schedule, NewSchedule } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/schedule-db-service.interface' -import { toJsonString } from '@/src/db/sqlite-casts' +import { IScheduleDbService, Schedule, NewSchedule } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/schedule-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' export class ScheduleDbService implements IScheduleDbService { async createSchedule(data: NewSchedule): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/slot-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/slot-queries.ts similarity index 95% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/slot-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/slot-queries.ts index c56ffdc..5d8044c 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/slot-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/slot-queries.ts @@ -1,8 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { deliverySlotInfo, productSlots, vendorSnippets, productInfo, productGroupInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { deliverySlotInfo, productSlots, vendorSnippets, productInfo, productGroupInfo } from '../../db/schema' import { eq, inArray, and, desc } from 'drizzle-orm' -import { ISlotDbService, Slot, NewSlot, ProductSlot, SlotWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/slot-db-service.interface' -import { parseNumberArray, toJsonString } from '@/src/db/sqlite-casts' +import { ISlotDbService, Slot, NewSlot, ProductSlot, SlotWithRelations } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/slot-db-service.interface' +import { parseNumberArray, toJsonString } from '../../db/sqlite-casts' export class SlotDbService implements ISlotDbService { async getAllSlots(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/staff-user-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/staff-user-queries.ts similarity index 93% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/staff-user-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/staff-user-queries.ts index 7179861..a28d8a3 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/staff-user-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/staff-user-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { staffUsers, staffRoles, users, userDetails, orders } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { staffUsers, staffRoles, users, userDetails, orders } from '../../db/schema' import { eq, or, like, and, lt, desc } from 'drizzle-orm' -import { IStaffUserDbService, StaffUser, NewStaffUser, StaffRole, StaffUserWithRole } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/staff-user-db-service.interface' +import { IStaffUserDbService, StaffUser, NewStaffUser, StaffRole, StaffUserWithRole } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/staff-user-db-service.interface' export class StaffUserDbService implements IStaffUserDbService { async getStaffUserByName(name: string): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/store-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/store-queries.ts similarity index 84% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/store-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/store-queries.ts index 09cd96a..e1cb55b 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/postgres/store-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/store-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { storeInfo, productInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { storeInfo, productInfo } from '../../db/schema' import { eq, inArray } from 'drizzle-orm' -import { IStoreDbService, Store, NewStore } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/store-db-service.interface' +import { IStoreDbService, Store, NewStore } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/store-db-service.interface' export class StoreDbService implements IStoreDbService { async getAllStores(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/tag-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/tag-queries.ts similarity index 83% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/tag-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/tag-queries.ts index a90ce4e..c3a5163 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/tag-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/tag-queries.ts @@ -1,8 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { productTagInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productTagInfo } from '../../db/schema' import { eq } from 'drizzle-orm' -import { ITagDbService, Tag, NewTag } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/tag-db-service.interface' -import { toJsonString } from '@/src/db/sqlite-casts' +import { ITagDbService, Tag, NewTag } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/tag-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' export class TagDbService implements ITagDbService { async getAllTags(): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/user-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/user-queries.ts similarity index 96% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/user-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/user-queries.ts index e9c4072..5e75809 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/user-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/user-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { users, userDetails, orders, orderItems, orderStatus, complaints, notifCreds, unloggedUserTokens, userIncidents } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { users, userDetails, orders, orderItems, orderStatus, complaints, notifCreds, unloggedUserTokens, userIncidents } from '../../db/schema' import { eq, desc, asc, count, max, inArray, and, like, gt } from 'drizzle-orm' -import { IUserDbService, User, NewUser, UserDetail } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/user-db-service.interface' +import { IUserDbService, User, NewUser, UserDetail } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/user-db-service.interface' export class UserDbService implements IUserDbService { async getUserById(id: number): Promise { diff --git a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/vendor-snippets-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/vendor-snippets-queries.ts similarity index 94% rename from apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/vendor-snippets-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/admin-apis/vendor-snippets-queries.ts index b13886a..39ea40c 100644 --- a/apps/backend/src/trpc/apis/admin-apis/dataAccessors/sqlite/vendor-snippets-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/admin-apis/vendor-snippets-queries.ts @@ -1,8 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { vendorSnippets, deliverySlotInfo, orders, orderItems, productInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { vendorSnippets, deliverySlotInfo, orders, orderItems, productInfo } from '../../db/schema' import { eq, and, inArray, gt, asc, desc } from 'drizzle-orm' -import { IVendorSnippetDbService, VendorSnippet, NewVendorSnippet } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/vendor-snippet-db-service.interface' -import { toJsonString } from '@/src/db/sqlite-casts' +import { IVendorSnippetDbService, VendorSnippet, NewVendorSnippet } from '../../../../backend/src/trpc/apis/admin-apis/dataAccessors/interfaces/vendor-snippet-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' export class VendorSnippetDbService implements IVendorSnippetDbService { async createSnippet(data: NewVendorSnippet): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-address-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-address-queries.ts similarity index 91% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-address-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-address-queries.ts index bafdcdf..3a30b34 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-address-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-address-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { addresses, orders, orderStatus, deliverySlotInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { addresses, orders, orderStatus, deliverySlotInfo } from '../../db/schema' import { eq, and, gte } from 'drizzle-orm' -import { IUserAddressDbService, Address, NewAddress } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-address-db-service.interface' +import { IUserAddressDbService, Address, NewAddress } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-address-db-service.interface' export class UserAddressDbService implements IUserAddressDbService { async getDefaultAddress(userId: number): Promise
{ diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-auth-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-auth-queries.ts similarity index 96% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-auth-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-auth-queries.ts index ecf7393..8ccbc11 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-auth-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-auth-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { users, userCreds, userDetails, addresses, cartItems, complaints, couponApplicableUsers, couponUsage, notifCreds, notifications, orderItems, orderStatus, orders, payments, refunds, productReviews, reservedCoupons } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { users, userCreds, userDetails, addresses, cartItems, complaints, couponApplicableUsers, couponUsage, notifCreds, notifications, orderItems, orderStatus, orders, payments, refunds, productReviews, reservedCoupons } from '../../db/schema' import { eq } from 'drizzle-orm' -import { IUserAuthDbService, User, UserCred, UserDetail } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-auth-db-service.interface' +import { IUserAuthDbService, User, UserCred, UserDetail } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-auth-db-service.interface' export class UserAuthDbService implements IUserAuthDbService { async getUserByEmail(email: string): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-banner-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-banner-queries.ts similarity index 62% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-banner-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-banner-queries.ts index a969359..553e841 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-banner-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-banner-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { homeBanners } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { homeBanners } from '../../db/schema' import { isNotNull, asc } from 'drizzle-orm' -import { IUserBannerDbService, UserBanner } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-banner-db-service.interface' +import { IUserBannerDbService, UserBanner } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-banner-db-service.interface' export class UserBannerDbService implements IUserBannerDbService { async getActiveBanners(): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-cart-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-cart-queries.ts similarity index 90% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-cart-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-cart-queries.ts index c7e2dda..267b13d 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-cart-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-cart-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { cartItems, productInfo, units } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { cartItems, productInfo, units } from '../../db/schema' import { eq, and, sql } from 'drizzle-orm' -import { IUserCartDbService, CartItem } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-cart-db-service.interface' +import { IUserCartDbService, CartItem } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-cart-db-service.interface' export class UserCartDbService implements IUserCartDbService { async getCartItemsWithProducts(userId: number) { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-complaint-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-complaint-queries.ts similarity index 57% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-complaint-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-complaint-queries.ts index f3f4945..fdd176d 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-complaint-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-complaint-queries.ts @@ -1,7 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { complaints } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { complaints } from '../../db/schema' import { eq, asc } from 'drizzle-orm' -import { IUserComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-complaint-db-service.interface' +import { IUserComplaintDbService, NewComplaint } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-complaint-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' export class UserComplaintDbService implements IUserComplaintDbService { async getComplaintsByUserId(userId: number) { @@ -20,8 +21,12 @@ export class UserComplaintDbService implements IUserComplaintDbService { .orderBy(asc(complaints.createdAt)) } - async createComplaint(data: { userId: number; orderId?: number | null; complaintBody: string; images: string[] }) { - await db.insert(complaints).values(data) + async createComplaint(data: NewComplaint) { + const normalized = { + ...data, + images: data.images ? toJsonString(data.images, '[]') : data.images, + } + await db.insert(complaints).values(normalized) } } diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-coupon-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-coupon-queries.ts similarity index 92% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-coupon-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-coupon-queries.ts index 54bb491..6d1c665 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-coupon-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-coupon-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { coupons, couponUsage, couponApplicableUsers, couponApplicableProducts, reservedCoupons } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { coupons, couponUsage, couponApplicableUsers, couponApplicableProducts, reservedCoupons } from '../../db/schema' import { eq, and, or, gt, isNull } from 'drizzle-orm' -import { IUserCouponDbService, Coupon, ReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-coupon-db-service.interface' +import { IUserCouponDbService, Coupon, ReservedCoupon, CouponWithRelations } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-coupon-db-service.interface' export class UserCouponDbService implements IUserCouponDbService { async getActiveCouponsForUser(userId: number): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-order-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-order-queries.ts similarity index 97% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-order-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-order-queries.ts index fefc0c1..64463a8 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-order-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-order-queries.ts @@ -1,4 +1,4 @@ -import { db } from '@/src/db/db_index' +import { db } from '../../db/db_index' import { orders, orderItems, @@ -12,12 +12,12 @@ import { refunds, units, userDetails, -} from '@/src/db/schema' +} from '../../db/schema' import { and, desc, eq, gte, inArray } from 'drizzle-orm' import { IUserOrderDbService, Order, -} from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-order-db-service.interface' +} from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-order-db-service.interface' export class UserOrderDbService implements IUserOrderDbService { async getUserDetailByUserId(userId: number) { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-product-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-product-queries.ts similarity index 87% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-product-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-product-queries.ts index a18748e..eb9e726 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-product-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-product-queries.ts @@ -1,7 +1,8 @@ -import { db } from '@/src/db/db_index_sqlite' -import { productInfo, units, storeInfo, productSlots, deliverySlotInfo, specialDeals, productReviews, users } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { productInfo, units, storeInfo, productSlots, deliverySlotInfo, specialDeals, productReviews, users } from '../../db/schema' import { eq, and, gt, sql, desc } from 'drizzle-orm' -import { IUserProductDbService, Review } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-product-db-service.interface' +import { toJsonString } from '../../db/sqlite-casts' +import { IUserProductDbService, Review } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-product-db-service.interface' export class UserProductDbService implements IUserProductDbService { async getProductById(productId: number) { @@ -103,7 +104,11 @@ export class UserProductDbService implements IUserProductDbService { } async createReview(data: { userId: number; productId: number; reviewBody: string; ratings: number; imageUrls: string[] }): Promise { - const [newReview] = await db.insert(productReviews).values(data).returning() + const normalized = { + ...data, + imageUrls: toJsonString(data.imageUrls, '[]'), + } + const [newReview] = await db.insert(productReviews).values(normalized).returning() return newReview } } diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-profile-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-profile-queries.ts similarity index 92% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-profile-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-profile-queries.ts index 53f42b7..4a0cd8b 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-profile-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-profile-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { users, userDetails, userCreds, notifCreds, unloggedUserTokens } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { users, userDetails, userCreds, notifCreds, unloggedUserTokens } from '../../db/schema' import { eq, and } from 'drizzle-orm' -import { IUserProfileDbService, User, UserDetail, UserCred, NotifCred, UnloggedToken } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-profile-db-service.interface' +import { IUserProfileDbService, User, UserDetail, UserCred, NotifCred, UnloggedToken } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-profile-db-service.interface' export class UserProfileDbService implements IUserProfileDbService { async getUserById(userId: number): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-slot-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-slot-queries.ts similarity index 75% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-slot-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-slot-queries.ts index dc60891..cca1c1a 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/sqlite/user-slot-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-slot-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index_sqlite' -import { deliverySlotInfo, productInfo } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { deliverySlotInfo, productInfo } from '../../db/schema' import { eq } from 'drizzle-orm' -import { IUserSlotDbService, Slot } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-slot-db-service.interface' +import { IUserSlotDbService, Slot } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-slot-db-service.interface' export class UserSlotDbService implements IUserSlotDbService { async getActiveSlots(): Promise { diff --git a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-store-queries.ts b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-store-queries.ts similarity index 89% rename from apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-store-queries.ts rename to apps/db-helper-sqlite/src/dataAccessors/user-apis/user-store-queries.ts index fa7556f..3ab67e5 100644 --- a/apps/backend/src/trpc/apis/user-apis/dataAccessors/postgres/user-store-queries.ts +++ b/apps/db-helper-sqlite/src/dataAccessors/user-apis/user-store-queries.ts @@ -1,7 +1,7 @@ -import { db } from '@/src/db/db_index' -import { storeInfo, productInfo, units } from '@/src/db/schema' +import { db } from '../../db/db_index' +import { storeInfo, productInfo, units } from '../../db/schema' import { eq, and, sql } from 'drizzle-orm' -import { IUserStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-store-db-service.interface' +import { IUserStoreDbService } from '../../../../backend/src/trpc/apis/user-apis/dataAccessors/interfaces/user-store-db-service.interface' export class UserStoreDbService implements IUserStoreDbService { async getStoresWithProductCount(): Promise> { diff --git a/apps/db-helper-sqlite/src/db/db_index.ts b/apps/db-helper-sqlite/src/db/db_index.ts new file mode 100644 index 0000000..efd1a06 --- /dev/null +++ b/apps/db-helper-sqlite/src/db/db_index.ts @@ -0,0 +1,7 @@ +import { drizzle } from 'drizzle-orm/bun-sqlite' +import { Database } from 'bun:sqlite' +import * as schema from './schema' + +const db = drizzle(new Database(process.env.SQLITE_DB_PATH || 'sqlite.db'), { schema }) + +export { db } diff --git a/apps/db-helper-sqlite/src/db/schema.ts b/apps/db-helper-sqlite/src/db/schema.ts new file mode 100644 index 0000000..568653d --- /dev/null +++ b/apps/db-helper-sqlite/src/db/schema.ts @@ -0,0 +1,735 @@ +import { + sqliteTable, + integer, + text, + real, + unique, + check, +} from 'drizzle-orm/sqlite-core' +import { relations, sql } from 'drizzle-orm' + +const epochSeconds = sql`(strftime('%s','now'))` + +const sqliteEnum = ( + _name: string, + values: T +) => (columnName: string) => text(columnName, { enum: values }) + +export const users = sqliteTable('users', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name'), + email: text('email'), + mobile: text('mobile'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + unq_email: unique('unique_email').on(t.email), +})) + +export const userDetails = sqliteTable('user_details', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id).unique(), + bio: text('bio'), + dateOfBirth: integer('date_of_birth', { mode: 'timestamp' }), + gender: text('gender'), + occupation: text('occupation'), + profileImage: text('profile_image'), + isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const userCreds = sqliteTable('user_creds', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + userPassword: text('user_password').notNull(), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const addressZones = sqliteTable('address_zones', { + id: integer('id').primaryKey({ autoIncrement: true }), + zoneName: text('zone_name').notNull(), + addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const addresses = sqliteTable('addresses', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + name: text('name').notNull(), + phone: text('phone').notNull(), + addressLine1: text('address_line1').notNull(), + addressLine2: text('address_line2'), + city: text('city').notNull(), + state: text('state').notNull(), + pincode: text('pincode').notNull(), + isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false), + latitude: real('latitude'), + longitude: real('longitude'), + googleMapsUrl: text('google_maps_url'), + adminLatitude: real('admin_latitude'), + adminLongitude: real('admin_longitude'), + zoneId: integer('zone_id').references(() => addressZones.id), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const addressAreas = sqliteTable('address_areas', { + id: integer('id').primaryKey({ autoIncrement: true }), + placeName: text('place_name').notNull(), + zoneId: integer('zone_id').references(() => addressZones.id), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const staffRoleEnum = sqliteEnum('staff_role', [ + 'super_admin', + 'admin', + 'marketer', + 'delivery_staff', +]) + +export const staffPermissionEnum = sqliteEnum('staff_permission', [ + 'crud_product', + 'make_coupon', + 'crud_staff_users', +]) + +export const staffRoles = sqliteTable('staff_roles', { + id: integer('id').primaryKey({ autoIncrement: true }), + roleName: staffRoleEnum('role_name').notNull(), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + unq_role_name: unique('unique_role_name').on(t.roleName), +})) + +export const staffPermissions = sqliteTable('staff_permissions', { + id: integer('id').primaryKey({ autoIncrement: true }), + permissionName: staffPermissionEnum('permission_name').notNull(), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + unq_permission_name: unique('unique_permission_name').on(t.permissionName), +})) + +export const staffRolePermissions = sqliteTable('staff_role_permissions', { + id: integer('id').primaryKey({ autoIncrement: true }), + staffRoleId: integer('staff_role_id').notNull().references(() => staffRoles.id), + staffPermissionId: integer('staff_permission_id').notNull().references(() => staffPermissions.id), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + unq_role_permission: unique('unique_role_permission').on( + t.staffRoleId, + t.staffPermissionId + ), +})) + +export const staffUsers = sqliteTable('staff_users', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + password: text('password').notNull(), + staffRoleId: integer('staff_role_id').references(() => staffRoles.id), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const storeInfo = sqliteTable('store_info', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + description: text('description'), + imageUrl: text('image_url'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + owner: integer('owner').notNull().references(() => staffUsers.id), +}) + +export const units = sqliteTable('units', { + id: integer('id').primaryKey({ autoIncrement: true }), + shortNotation: text('short_notation').notNull(), + fullName: text('full_name').notNull(), +}, (t) => ({ + unq_short_notation: unique('unique_short_notation').on(t.shortNotation), +})) + +export const productAvailabilityActionEnum = sqliteEnum( + 'product_availability_action', + ['in', 'out'] +) + +export const productInfo = sqliteTable('product_info', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + shortDescription: text('short_description'), + longDescription: text('long_description'), + unitId: integer('unit_id').notNull().references(() => units.id), + price: text('price').notNull(), + marketPrice: text('market_price'), + images: text('images'), + isOutOfStock: integer('is_out_of_stock', { mode: 'boolean' }).notNull().default(false), + isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false), + isFlashAvailable: integer('is_flash_available', { mode: 'boolean' }).notNull().default(false), + flashPrice: text('flash_price'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + incrementStep: real('increment_step').notNull().default(1), + productQuantity: real('product_quantity').notNull().default(1), + storeId: integer('store_id').references(() => storeInfo.id), + scheduledAvailability: integer('scheduled_availability', { mode: 'boolean' }).notNull().default(true), +}) + +export const productAvailabilitySchedules = sqliteTable('product_availability_schedules', { + id: integer('id').primaryKey({ autoIncrement: true }), + time: text('time').notNull(), + scheduleName: text('schedule_name').notNull().unique(), + action: productAvailabilityActionEnum('action').notNull(), + productIds: text('product_ids').notNull().default('[]'), + groupIds: text('group_ids').notNull().default('[]'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + lastUpdated: integer('last_updated', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const productGroupInfo = sqliteTable('product_group_info', { + id: integer('id').primaryKey({ autoIncrement: true }), + groupName: text('group_name').notNull(), + description: text('description'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const productGroupMembership = sqliteTable('product_group_membership', { + productId: integer('product_id').notNull().references(() => productInfo.id), + groupId: integer('group_id').notNull().references(() => productGroupInfo.id), + addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + pk: unique('product_group_membership_pk').on(t.productId, t.groupId), +})) + +export const homeBanners = sqliteTable('home_banners', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + imageUrl: text('image_url').notNull(), + description: text('description'), + productIds: text('product_ids'), + redirectUrl: text('redirect_url'), + serialNum: integer('serial_num'), + isActive: integer('is_active', { mode: 'boolean' }).notNull().default(false), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + lastUpdated: integer('last_updated', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const productReviews = sqliteTable('product_reviews', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + productId: integer('product_id').notNull().references(() => productInfo.id), + reviewBody: text('review_body').notNull(), + imageUrls: text('image_urls').default('[]'), + reviewTime: integer('review_time', { mode: 'timestamp' }).notNull().default(epochSeconds), + ratings: real('ratings').notNull(), + adminResponse: text('admin_response'), + adminResponseImages: text('admin_response_images').default('[]'), +}, (t) => ({ + ratingCheck: check('rating_check', sql`${t.ratings} >= 1 AND ${t.ratings} <= 5`), +})) + +export const uploadStatusEnum = sqliteEnum('upload_status', ['pending', 'claimed']) + +export const uploadUrlStatus = sqliteTable('upload_url_status', { + id: integer('id').primaryKey({ autoIncrement: true }), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + key: text('key').notNull(), + status: uploadStatusEnum('status').notNull().default('pending'), +}) + +export const productTagInfo = sqliteTable('product_tag_info', { + id: integer('id').primaryKey({ autoIncrement: true }), + tagName: text('tag_name').notNull().unique(), + tagDescription: text('tag_description'), + imageUrl: text('image_url'), + isDashboardTag: integer('is_dashboard_tag', { mode: 'boolean' }).notNull().default(false), + relatedStores: text('related_stores').default('[]'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const productTags = sqliteTable('product_tags', { + id: integer('id').primaryKey({ autoIncrement: true }), + productId: integer('product_id').notNull().references(() => productInfo.id), + tagId: integer('tag_id').notNull().references(() => productTagInfo.id), + assignedAt: integer('assigned_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + unq_product_tag: unique('unique_product_tag').on(t.productId, t.tagId), +})) + +export const deliverySlotInfo = sqliteTable('delivery_slot_info', { + id: integer('id').primaryKey({ autoIncrement: true }), + deliveryTime: integer('delivery_time', { mode: 'timestamp' }).notNull(), + freezeTime: integer('freeze_time', { mode: 'timestamp' }).notNull(), + isActive: integer('is_active', { mode: 'boolean' }).notNull().default(true), + isFlash: integer('is_flash', { mode: 'boolean' }).notNull().default(false), + isCapacityFull: integer('is_capacity_full', { mode: 'boolean' }).notNull().default(false), + deliverySequence: text('delivery_sequence').default('{}'), + groupIds: text('group_ids').default('[]'), +}) + +export const vendorSnippets = sqliteTable('vendor_snippets', { + id: integer('id').primaryKey({ autoIncrement: true }), + snippetCode: text('snippet_code').notNull().unique(), + slotId: integer('slot_id').references(() => deliverySlotInfo.id), + isPermanent: integer('is_permanent', { mode: 'boolean' }).notNull().default(false), + productIds: text('product_ids').notNull(), + validTill: integer('valid_till', { mode: 'timestamp' }), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const productSlots = sqliteTable('product_slots', { + productId: integer('product_id').notNull().references(() => productInfo.id), + slotId: integer('slot_id').notNull().references(() => deliverySlotInfo.id), +}, (t) => ({ + pk: unique('product_slot_pk').on(t.productId, t.slotId), +})) + +export const specialDeals = sqliteTable('special_deals', { + id: integer('id').primaryKey({ autoIncrement: true }), + productId: integer('product_id').notNull().references(() => productInfo.id), + quantity: text('quantity').notNull(), + price: text('price').notNull(), + validTill: integer('valid_till', { mode: 'timestamp' }).notNull(), +}) + +export const paymentInfoTable = sqliteTable('payment_info', { + id: integer('id').primaryKey({ autoIncrement: true }), + status: text('status').notNull(), + gateway: text('gateway').notNull(), + orderId: text('order_id'), + token: text('token'), + merchantOrderId: text('merchant_order_id').notNull().unique(), + payload: text('payload'), +}) + +export const orders = sqliteTable('orders', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + addressId: integer('address_id').notNull().references(() => addresses.id), + slotId: integer('slot_id').references(() => deliverySlotInfo.id), + isCod: integer('is_cod', { mode: 'boolean' }).notNull().default(false), + isOnlinePayment: integer('is_online_payment', { mode: 'boolean' }).notNull().default(false), + paymentInfoId: integer('payment_info_id').references(() => paymentInfoTable.id), + totalAmount: text('total_amount').notNull(), + deliveryCharge: text('delivery_charge').notNull().default('0'), + readableId: integer('readable_id').notNull(), + adminNotes: text('admin_notes'), + userNotes: text('user_notes'), + orderGroupId: text('order_group_id'), + orderGroupProportion: text('order_group_proportion'), + isFlashDelivery: integer('is_flash_delivery', { mode: 'boolean' }).notNull().default(false), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const orderItems = sqliteTable('order_items', { + id: integer('id').primaryKey({ autoIncrement: true }), + orderId: integer('order_id').notNull().references(() => orders.id), + productId: integer('product_id').notNull().references(() => productInfo.id), + quantity: text('quantity').notNull(), + price: text('price').notNull(), + discountedPrice: text('discounted_price'), + is_packaged: integer('is_packaged', { mode: 'boolean' }).notNull().default(false), + is_package_verified: integer('is_package_verified', { mode: 'boolean' }).notNull().default(false), +}) + +export const paymentStatusEnum = sqliteEnum('payment_status', [ + 'pending', + 'success', + 'cod', + 'failed', +]) + +export const orderStatus = sqliteTable('order_status', { + id: integer('id').primaryKey({ autoIncrement: true }), + orderTime: integer('order_time', { mode: 'timestamp' }).notNull().default(epochSeconds), + userId: integer('user_id').notNull().references(() => users.id), + orderId: integer('order_id').notNull().references(() => orders.id), + isPackaged: integer('is_packaged', { mode: 'boolean' }).notNull().default(false), + isDelivered: integer('is_delivered', { mode: 'boolean' }).notNull().default(false), + isCancelled: integer('is_cancelled', { mode: 'boolean' }).notNull().default(false), + cancelReason: text('cancel_reason'), + isCancelledByAdmin: integer('is_cancelled_by_admin', { mode: 'boolean' }), + paymentStatus: paymentStatusEnum('payment_state').notNull().default('pending'), + cancellationUserNotes: text('cancellation_user_notes'), + cancellationAdminNotes: text('cancellation_admin_notes'), + cancellationReviewed: integer('cancellation_reviewed', { mode: 'boolean' }).notNull().default(false), + cancellationReviewedAt: integer('cancellation_reviewed_at', { mode: 'timestamp' }), + refundCouponId: integer('refund_coupon_id').references(() => coupons.id), +}) + +export const payments = sqliteTable('payments', { + id: integer('id').primaryKey({ autoIncrement: true }), + status: text('status').notNull(), + gateway: text('gateway').notNull(), + orderId: integer('order_id').notNull().references(() => orders.id), + token: text('token'), + merchantOrderId: text('merchant_order_id').notNull().unique(), + payload: text('payload'), +}) + +export const refunds = sqliteTable('refunds', { + id: integer('id').primaryKey({ autoIncrement: true }), + orderId: integer('order_id').notNull().references(() => orders.id), + refundAmount: text('refund_amount'), + refundStatus: text('refund_status').default('none'), + merchantRefundId: text('merchant_refund_id'), + refundProcessedAt: integer('refund_processed_at', { mode: 'timestamp' }), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const keyValStore = sqliteTable('key_val_store', { + key: text('key').primaryKey(), + value: text('value'), +}) + +export const notifications = sqliteTable('notifications', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + title: text('title').notNull(), + body: text('body').notNull(), + type: text('type'), + isRead: integer('is_read', { mode: 'boolean' }).notNull().default(false), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const productCategories = sqliteTable('product_categories', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + description: text('description'), +}) + +export const cartItems = sqliteTable('cart_items', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + productId: integer('product_id').notNull().references(() => productInfo.id), + quantity: text('quantity').notNull(), + addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + unq_user_product: unique('unique_user_product').on(t.userId, t.productId), +})) + +export const complaints = sqliteTable('complaints', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + orderId: integer('order_id').references(() => orders.id), + complaintBody: text('complaint_body').notNull(), + images: text('images'), + response: text('response'), + isResolved: integer('is_resolved', { mode: 'boolean' }).notNull().default(false), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const coupons = sqliteTable('coupons', { + id: integer('id').primaryKey({ autoIncrement: true }), + couponCode: text('coupon_code').notNull().unique('unique_coupon_code'), + isUserBased: integer('is_user_based', { mode: 'boolean' }).notNull().default(false), + discountPercent: text('discount_percent'), + flatDiscount: text('flat_discount'), + minOrder: text('min_order'), + productIds: text('product_ids'), + createdBy: integer('created_by').references(() => staffUsers.id), + maxValue: text('max_value'), + isApplyForAll: integer('is_apply_for_all', { mode: 'boolean' }).notNull().default(false), + validTill: integer('valid_till', { mode: 'timestamp' }), + maxLimitForUser: integer('max_limit_for_user'), + isInvalidated: integer('is_invalidated', { mode: 'boolean' }).notNull().default(false), + exclusiveApply: integer('exclusive_apply', { mode: 'boolean' }).notNull().default(false), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const couponUsage = sqliteTable('coupon_usage', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + couponId: integer('coupon_id').notNull().references(() => coupons.id), + orderId: integer('order_id').references(() => orders.id), + orderItemId: integer('order_item_id').references(() => orderItems.id), + usedAt: integer('used_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}) + +export const couponApplicableUsers = sqliteTable('coupon_applicable_users', { + id: integer('id').primaryKey({ autoIncrement: true }), + couponId: integer('coupon_id').notNull().references(() => coupons.id), + userId: integer('user_id').notNull().references(() => users.id), +}, (t) => ({ + unq_coupon_user: unique('unique_coupon_user').on(t.couponId, t.userId), +})) + +export const couponApplicableProducts = sqliteTable('coupon_applicable_products', { + id: integer('id').primaryKey({ autoIncrement: true }), + couponId: integer('coupon_id').notNull().references(() => coupons.id), + productId: integer('product_id').notNull().references(() => productInfo.id), +}, (t) => ({ + unq_coupon_product: unique('unique_coupon_product').on(t.couponId, t.productId), +})) + +export const userIncidents = sqliteTable('user_incidents', { + id: integer('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').notNull().references(() => users.id), + orderId: integer('order_id').references(() => orders.id), + dateAdded: integer('date_added', { mode: 'timestamp' }).notNull().default(epochSeconds), + adminComment: text('admin_comment'), + addedBy: integer('added_by').references(() => staffUsers.id), + negativityScore: integer('negativity_score'), +}) + +export const reservedCoupons = sqliteTable('reserved_coupons', { + id: integer('id').primaryKey({ autoIncrement: true }), + secretCode: text('secret_code').notNull().unique(), + couponCode: text('coupon_code').notNull(), + discountPercent: text('discount_percent'), + flatDiscount: text('flat_discount'), + minOrder: text('min_order'), + productIds: text('product_ids'), + maxValue: text('max_value'), + validTill: integer('valid_till', { mode: 'timestamp' }), + maxLimitForUser: integer('max_limit_for_user'), + exclusiveApply: integer('exclusive_apply', { mode: 'boolean' }).notNull().default(false), + isRedeemed: integer('is_redeemed', { mode: 'boolean' }).notNull().default(false), + redeemedBy: integer('redeemed_by').references(() => users.id), + redeemedAt: integer('redeemed_at', { mode: 'timestamp' }), + createdBy: integer('created_by').notNull().references(() => staffUsers.id), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), +}, (t) => ({ + unq_secret_code: unique('unique_secret_code').on(t.secretCode), +})) + +export const notifCreds = sqliteTable('notif_creds', { + id: integer('id').primaryKey({ autoIncrement: true }), + token: text('token').notNull().unique(), + addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + userId: integer('user_id').notNull().references(() => users.id), + lastVerified: integer('last_verified', { mode: 'timestamp' }), +}) + +export const unloggedUserTokens = sqliteTable('unlogged_user_tokens', { + id: integer('id').primaryKey({ autoIncrement: true }), + token: text('token').notNull().unique(), + addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + lastVerified: integer('last_verified', { mode: 'timestamp' }), +}) + +export const userNotifications = sqliteTable('user_notifications', { + id: integer('id').primaryKey({ autoIncrement: true }), + title: text('title').notNull(), + imageUrl: text('image_url'), + createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds), + body: text('body').notNull(), + applicableUsers: text('applicable_users'), +}) + +export const uploadUrlStatusRelations = relations(uploadUrlStatus, ({}) => ({})) + +export const userCredsRelations = relations(userCreds, ({ one }) => ({ + user: one(users, { fields: [userCreds.userId], references: [users.id] }), +})) + +export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ + role: one(staffRoles, { fields: [staffUsers.staffRoleId], references: [staffRoles.id] }), + coupons: many(coupons), + stores: many(storeInfo), +})) + +export const addressesRelations = relations(addresses, ({ one, many }) => ({ + user: one(users, { fields: [addresses.userId], references: [users.id] }), + orders: many(orders), + zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), +})) + +export const unitsRelations = relations(units, ({ many }) => ({ + products: many(productInfo), +})) + +export const productInfoRelations = relations(productInfo, ({ one, many }) => ({ + unit: one(units, { fields: [productInfo.unitId], references: [units.id] }), + store: one(storeInfo, { fields: [productInfo.storeId], references: [storeInfo.id] }), + productSlots: many(productSlots), + specialDeals: many(specialDeals), + orderItems: many(orderItems), + cartItems: many(cartItems), + tags: many(productTags), + applicableCoupons: many(couponApplicableProducts), + reviews: many(productReviews), + groups: many(productGroupMembership), +})) + +export const productTagInfoRelations = relations(productTagInfo, ({ many }) => ({ + products: many(productTags), +})) + +export const productTagsRelations = relations(productTags, ({ one }) => ({ + product: one(productInfo, { fields: [productTags.productId], references: [productInfo.id] }), + tag: one(productTagInfo, { fields: [productTags.tagId], references: [productTagInfo.id] }), +})) + +export const deliverySlotInfoRelations = relations(deliverySlotInfo, ({ many }) => ({ + productSlots: many(productSlots), + orders: many(orders), + vendorSnippets: many(vendorSnippets), +})) + +export const vendorSnippetsRelations = relations(vendorSnippets, ({ one }) => ({ + slot: one(deliverySlotInfo, { fields: [vendorSnippets.slotId], references: [deliverySlotInfo.id] }), +})) + +export const productSlotsRelations = relations(productSlots, ({ one }) => ({ + product: one(productInfo, { fields: [productSlots.productId], references: [productInfo.id] }), + slot: one(deliverySlotInfo, { fields: [productSlots.slotId], references: [deliverySlotInfo.id] }), +})) + +export const specialDealsRelations = relations(specialDeals, ({ one }) => ({ + product: one(productInfo, { fields: [specialDeals.productId], references: [productInfo.id] }), +})) + +export const ordersRelations = relations(orders, ({ one, many }) => ({ + user: one(users, { fields: [orders.userId], references: [users.id] }), + address: one(addresses, { fields: [orders.addressId], references: [addresses.id] }), + slot: one(deliverySlotInfo, { fields: [orders.slotId], references: [deliverySlotInfo.id] }), + orderItems: many(orderItems), + payment: one(payments), + paymentInfo: one(paymentInfoTable, { fields: [orders.paymentInfoId], references: [paymentInfoTable.id] }), + orderStatus: many(orderStatus), + refunds: many(refunds), + couponUsages: many(couponUsage), + userIncidents: many(userIncidents), +})) + +export const orderItemsRelations = relations(orderItems, ({ one }) => ({ + order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }), + product: one(productInfo, { fields: [orderItems.productId], references: [productInfo.id] }), +})) + +export const orderStatusRelations = relations(orderStatus, ({ one }) => ({ + order: one(orders, { fields: [orderStatus.orderId], references: [orders.id] }), + user: one(users, { fields: [orderStatus.userId], references: [users.id] }), + refundCoupon: one(coupons, { fields: [orderStatus.refundCouponId], references: [coupons.id] }), +})) + +export const paymentInfoRelations = relations(paymentInfoTable, ({ one }) => ({ + order: one(orders, { fields: [paymentInfoTable.id], references: [orders.paymentInfoId] }), +})) + +export const paymentsRelations = relations(payments, ({ one }) => ({ + order: one(orders, { fields: [payments.orderId], references: [orders.id] }), +})) + +export const refundsRelations = relations(refunds, ({ one }) => ({ + order: one(orders, { fields: [refunds.orderId], references: [orders.id] }), +})) + +export const notificationsRelations = relations(notifications, ({ one }) => ({ + user: one(users, { fields: [notifications.userId], references: [users.id] }), +})) + +export const productCategoriesRelations = relations(productCategories, ({}) => ({})) + +export const cartItemsRelations = relations(cartItems, ({ one }) => ({ + user: one(users, { fields: [cartItems.userId], references: [users.id] }), + product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }), +})) + +export const complaintsRelations = relations(complaints, ({ one }) => ({ + user: one(users, { fields: [complaints.userId], references: [users.id] }), + order: one(orders, { fields: [complaints.orderId], references: [orders.id] }), +})) + +export const couponsRelations = relations(coupons, ({ one, many }) => ({ + creator: one(staffUsers, { fields: [coupons.createdBy], references: [staffUsers.id] }), + usages: many(couponUsage), + applicableUsers: many(couponApplicableUsers), + applicableProducts: many(couponApplicableProducts), +})) + +export const couponUsageRelations = relations(couponUsage, ({ one }) => ({ + user: one(users, { fields: [couponUsage.userId], references: [users.id] }), + coupon: one(coupons, { fields: [couponUsage.couponId], references: [coupons.id] }), + order: one(orders, { fields: [couponUsage.orderId], references: [orders.id] }), + orderItem: one(orderItems, { fields: [couponUsage.orderItemId], references: [orderItems.id] }), +})) + +export const userDetailsRelations = relations(userDetails, ({ one }) => ({ + user: one(users, { fields: [userDetails.userId], references: [users.id] }), +})) + +export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ + user: one(users, { fields: [notifCreds.userId], references: [users.id] }), +})) + +export const userNotificationsRelations = relations(userNotifications, ({}) => ({})) + +export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ + owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), + products: many(productInfo), +})) + +export const couponApplicableUsersRelations = relations(couponApplicableUsers, ({ one }) => ({ + coupon: one(coupons, { fields: [couponApplicableUsers.couponId], references: [coupons.id] }), + user: one(users, { fields: [couponApplicableUsers.userId], references: [users.id] }), +})) + +export const couponApplicableProductsRelations = relations(couponApplicableProducts, ({ one }) => ({ + coupon: one(coupons, { fields: [couponApplicableProducts.couponId], references: [coupons.id] }), + product: one(productInfo, { fields: [couponApplicableProducts.productId], references: [productInfo.id] }), +})) + +export const reservedCouponsRelations = relations(reservedCoupons, ({ one }) => ({ + redeemedUser: one(users, { fields: [reservedCoupons.redeemedBy], references: [users.id] }), + creator: one(staffUsers, { fields: [reservedCoupons.createdBy], references: [staffUsers.id] }), +})) + +export const productReviewsRelations = relations(productReviews, ({ one }) => ({ + user: one(users, { fields: [productReviews.userId], references: [users.id] }), + product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), +})) + +export const addressZonesRelations = relations(addressZones, ({ many }) => ({ + addresses: many(addresses), + areas: many(addressAreas), +})) + +export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ + zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), +})) + +export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ + memberships: many(productGroupMembership), +})) + +export const productGroupMembershipRelations = relations(productGroupMembership, ({ one }) => ({ + product: one(productInfo, { fields: [productGroupMembership.productId], references: [productInfo.id] }), + group: one(productGroupInfo, { fields: [productGroupMembership.groupId], references: [productGroupInfo.id] }), +})) + +export const homeBannersRelations = relations(homeBanners, ({}) => ({})) + +export const staffRolesRelations = relations(staffRoles, ({ many }) => ({ + staffUsers: many(staffUsers), + rolePermissions: many(staffRolePermissions), +})) + +export const staffPermissionsRelations = relations(staffPermissions, ({ many }) => ({ + rolePermissions: many(staffRolePermissions), +})) + +export const staffRolePermissionsRelations = relations(staffRolePermissions, ({ one }) => ({ + role: one(staffRoles, { fields: [staffRolePermissions.staffRoleId], references: [staffRoles.id] }), + permission: one(staffPermissions, { fields: [staffRolePermissions.staffPermissionId], references: [staffPermissions.id] }), +})) + +export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({ + user: one(users, { fields: [userIncidents.userId], references: [users.id] }), + order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }), + addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }), +})) + +export const productAvailabilitySchedulesRelations = relations( + productAvailabilitySchedules, + ({}) => ({}) +) + +export const usersRelations = relations(users, ({ many, one }) => ({ + addresses: many(addresses), + orders: many(orders), + notifications: many(notifications), + cartItems: many(cartItems), + userCreds: one(userCreds), + coupons: many(coupons), + couponUsages: many(couponUsage), + applicableCoupons: many(couponApplicableUsers), + userDetails: one(userDetails), + notifCreds: many(notifCreds), + userIncidents: many(userIncidents), +})) diff --git a/apps/db-helper-sqlite/src/db/seed.ts b/apps/db-helper-sqlite/src/db/seed.ts new file mode 100644 index 0000000..df48d68 --- /dev/null +++ b/apps/db-helper-sqlite/src/db/seed.ts @@ -0,0 +1,154 @@ +import { eq } from 'drizzle-orm' + +import { CONST_KEYS } from '../../../backend/src/lib/const-keys' +import { deliveryCharge, minOrderValue } from '../../../backend/src/lib/env-exporter' + +import { db } from './db_index' +import { keyValStore, staffPermissions, staffRolePermissions, staffRoles, units } from './schema' +import { toJsonString } from './sqlite-casts' + +export async function seed() { + console.log('Seeding database...') + + const unitsToSeed = [ + { shortNotation: 'Kg', fullName: 'Kilogram' }, + { shortNotation: 'L', fullName: 'Litre' }, + { shortNotation: 'Dz', fullName: 'Dozen' }, + { shortNotation: 'Pc', fullName: 'Unit Piece' }, + ] + + for (const unit of unitsToSeed) { + const existingUnit = await db.query.units.findFirst({ + where: eq(units.shortNotation, unit.shortNotation), + }) + if (!existingUnit) { + await db.insert(units).values(unit) + } + } + + const rolesToSeed = ['super_admin', 'admin', 'marketer', 'delivery_staff'] as const + + for (const roleName of rolesToSeed) { + const existingRole = await db.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, roleName), + }) + if (!existingRole) { + await db.insert(staffRoles).values({ roleName }) + } + } + + const permissionsToSeed = ['crud_product', 'make_coupon', 'crud_staff_users'] as const + + for (const permissionName of permissionsToSeed) { + const existingPermission = await db.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, permissionName), + }) + if (!existingPermission) { + await db.insert(staffPermissions).values({ permissionName }) + } + } + + await db.transaction(async (tx) => { + const superAdminRole = await tx.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, 'super_admin'), + }) + const adminRole = await tx.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, 'admin'), + }) + const marketerRole = await tx.query.staffRoles.findFirst({ + where: eq(staffRoles.roleName, 'marketer'), + }) + + const crudProductPerm = await tx.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, 'crud_product'), + }) + const makeCouponPerm = await tx.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, 'make_coupon'), + }) + const crudStaffUsersPerm = await tx.query.staffPermissions.findFirst({ + where: eq(staffPermissions.permissionName, 'crud_staff_users'), + }) + + await Promise.all( + [crudProductPerm, makeCouponPerm, crudStaffUsersPerm].map(async (perm) => { + if (!superAdminRole || !perm) { + return + } + + const existingSuperAdminPerm = await tx.query.staffRolePermissions.findFirst({ + where: eq(staffRolePermissions.staffRoleId, superAdminRole.id) + && eq(staffRolePermissions.staffPermissionId, perm.id), + }) + if (!existingSuperAdminPerm) { + await tx.insert(staffRolePermissions).values({ + staffRoleId: superAdminRole.id, + staffPermissionId: perm.id, + }) + } + }) + ) + + await Promise.all( + [crudProductPerm, makeCouponPerm].map(async (perm) => { + if (!adminRole || !perm) { + return + } + + const existingAdminPerm = await tx.query.staffRolePermissions.findFirst({ + where: eq(staffRolePermissions.staffRoleId, adminRole.id) + && eq(staffRolePermissions.staffPermissionId, perm.id), + }) + if (!existingAdminPerm) { + await tx.insert(staffRolePermissions).values({ + staffRoleId: adminRole.id, + staffPermissionId: perm.id, + }) + } + }) + ) + + if (marketerRole && makeCouponPerm) { + const existingMarketerCoupon = await tx.query.staffRolePermissions.findFirst({ + where: eq(staffRolePermissions.staffRoleId, marketerRole.id) + && eq(staffRolePermissions.staffPermissionId, makeCouponPerm.id), + }) + if (!existingMarketerCoupon) { + await tx.insert(staffRolePermissions).values({ + staffRoleId: marketerRole.id, + staffPermissionId: makeCouponPerm.id, + }) + } + } + }) + + const constantsToSeed = [ + { key: CONST_KEYS.readableOrderId, value: 0 }, + { key: CONST_KEYS.minRegularOrderValue, value: minOrderValue }, + { key: CONST_KEYS.freeDeliveryThreshold, value: minOrderValue }, + { key: CONST_KEYS.deliveryCharge, value: deliveryCharge }, + { key: CONST_KEYS.flashFreeDeliveryThreshold, value: 500 }, + { key: CONST_KEYS.flashDeliveryCharge, value: 69 }, + { key: CONST_KEYS.popularItems, value: [] }, + { key: CONST_KEYS.allItemsOrder, value: [] }, + { key: CONST_KEYS.versionNum, value: '1.1.0' }, + { key: CONST_KEYS.playStoreUrl, value: 'https://play.google.com/store/apps/details?id=in.freshyo.app' }, + { key: CONST_KEYS.appStoreUrl, value: 'https://apps.apple.com/in/app/freshyo/id6756889077' }, + { key: CONST_KEYS.isFlashDeliveryEnabled, value: false }, + { key: CONST_KEYS.supportMobile, value: '8688182552' }, + { key: CONST_KEYS.supportEmail, value: 'qushammohd@gmail.com' }, + ] + + for (const constant of constantsToSeed) { + const existing = await db.query.keyValStore.findFirst({ + where: eq(keyValStore.key, constant.key), + }) + if (!existing) { + await db.insert(keyValStore).values({ + key: constant.key, + value: toJsonString(constant.value, 'null'), + }) + } + } + + console.log('Seeding completed.') +} diff --git a/apps/db-helper-sqlite/src/db/sqlite-casts.ts b/apps/db-helper-sqlite/src/db/sqlite-casts.ts new file mode 100644 index 0000000..e59d9cf --- /dev/null +++ b/apps/db-helper-sqlite/src/db/sqlite-casts.ts @@ -0,0 +1,34 @@ +export const parseJsonValue = (value: unknown, fallback: T): T => { + if (value === null || value === undefined) return fallback + if (typeof value === 'string') { + try { + return JSON.parse(value) as T + } catch { + return fallback + } + } + return value as T +} + +export const parseNumberArray = (value: unknown): number[] => { + const parsed = parseJsonValue(value, []) + return parsed + .map((item) => Number(item)) + .filter((item) => !Number.isNaN(item)) +} + +export const toJsonString = (value: unknown, fallback: string): string => { + if (value === null || value === undefined) return fallback + if (typeof value === 'string') return value + return JSON.stringify(value) +} + +export const toEpochSeconds = (value: Date | number): number => { + if (typeof value === 'number') return value + return Math.floor(value.getTime() / 1000) +} + +export const fromEpochSeconds = (value: number | null | undefined): Date | null => { + if (value === null || value === undefined) return null + return new Date(value * 1000) +} diff --git a/apps/db-helper-sqlite/src/db/types.ts b/apps/db-helper-sqlite/src/db/types.ts new file mode 100644 index 0000000..902b5c0 --- /dev/null +++ b/apps/db-helper-sqlite/src/db/types.ts @@ -0,0 +1,47 @@ +import type { InferSelectModel } from 'drizzle-orm' + +import type { + users, + addresses, + units, + productInfo, + deliverySlotInfo, + productSlots, + specialDeals, + orders, + orderItems, + payments, + notifications, + productCategories, + cartItems, + coupons, +} from './schema' + +export type User = InferSelectModel +export type Address = InferSelectModel +export type Unit = InferSelectModel +export type ProductInfo = InferSelectModel +export type DeliverySlotInfo = InferSelectModel +export type ProductSlot = InferSelectModel +export type SpecialDeal = InferSelectModel +export type Order = InferSelectModel +export type OrderItem = InferSelectModel +export type Payment = InferSelectModel +export type Notification = InferSelectModel +export type ProductCategory = InferSelectModel +export type CartItem = InferSelectModel +export type Coupon = InferSelectModel + +export type ProductWithUnit = ProductInfo & { + unit: Unit +} + +export type OrderWithItems = Order & { + items: (OrderItem & { product: ProductInfo })[] + address: Address + slot: DeliverySlotInfo +} + +export type CartItemWithProduct = CartItem & { + product: ProductInfo +} diff --git a/apps/db-helper-sqlite/src/lib/upload-url.ts b/apps/db-helper-sqlite/src/lib/upload-url.ts new file mode 100644 index 0000000..9f263c9 --- /dev/null +++ b/apps/db-helper-sqlite/src/lib/upload-url.ts @@ -0,0 +1,23 @@ +import { and, eq } from 'drizzle-orm' + +import { db } from '../db/db_index' +import { uploadUrlStatus } from '../db/schema' + +export const createUploadUrlStatus = async (key: string): Promise => { + await db.insert(uploadUrlStatus).values({ + key, + status: 'pending', + }) +} + +export const claimUploadUrlStatus = async (key: string): Promise => { + const result = await db + .update(uploadUrlStatus) + .set({ status: 'claimed' }) + .where(and(eq(uploadUrlStatus.key, key), eq(uploadUrlStatus.status, 'pending'))) + .returning() + + if (result.length === 0) { + throw new Error('Upload URL not found or already claimed') + } +} diff --git a/apps/db-helper-sqlite/src/services/user/product-service.ts b/apps/db-helper-sqlite/src/services/user/product-service.ts new file mode 100644 index 0000000..f94d26c --- /dev/null +++ b/apps/db-helper-sqlite/src/services/user/product-service.ts @@ -0,0 +1,143 @@ +import { db } from '../../db/db_index' +import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productReviews, users } from '../../db/schema' +import { eq, and, gt, sql, desc } from 'drizzle-orm' +import { toJsonString } from '../../db/sqlite-casts' + +/** + * Get product basic info with unit + */ +export async function getProductWithUnit(productId: number) { + return db + .select({ + id: productInfo.id, + name: productInfo.name, + shortDescription: productInfo.shortDescription, + longDescription: productInfo.longDescription, + price: productInfo.price, + marketPrice: productInfo.marketPrice, + images: productInfo.images, + isOutOfStock: productInfo.isOutOfStock, + storeId: productInfo.storeId, + unitShortNotation: units.shortNotation, + incrementStep: productInfo.incrementStep, + productQuantity: productInfo.productQuantity, + isFlashAvailable: productInfo.isFlashAvailable, + flashPrice: productInfo.flashPrice, + }) + .from(productInfo) + .innerJoin(units, eq(productInfo.unitId, units.id)) + .where(eq(productInfo.id, productId)) + .limit(1) +} + +/** + * Get store info by ID + */ +export async function getStoreById(storeId: number) { + return db.query.storeInfo.findFirst({ + where: eq(storeInfo.id, storeId), + columns: { id: true, name: true, description: true }, + }) +} + +/** + * Get delivery slots for product + */ +export async function getProductDeliverySlots(productId: number) { + return db + .select({ + id: deliverySlotInfo.id, + deliveryTime: deliverySlotInfo.deliveryTime, + freezeTime: deliverySlotInfo.freezeTime, + }) + .from(productSlots) + .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) + .where( + and( + eq(productSlots.productId, productId), + eq(deliverySlotInfo.isActive, true), + gt(deliverySlotInfo.deliveryTime, new Date()), + gt(deliverySlotInfo.freezeTime, new Date()) + ) + ) + .orderBy(deliverySlotInfo.deliveryTime) +} + +/** + * Get special deals for product + */ +export async function getProductSpecialDeals(productId: number) { + return db + .select({ + quantity: specialDeals.quantity, + price: specialDeals.price, + validTill: specialDeals.validTill, + }) + .from(specialDeals) + .where( + and( + eq(specialDeals.productId, productId), + gt(specialDeals.validTill, new Date()) + ) + ) + .orderBy(specialDeals.quantity) +} + +/** + * Get product reviews with user info + */ +export async function getProductReviews(productId: number, limit: number, offset: number) { + return db + .select({ + id: productReviews.id, + reviewBody: productReviews.reviewBody, + ratings: productReviews.ratings, + imageUrls: productReviews.imageUrls, + reviewTime: productReviews.reviewTime, + userName: users.name, + }) + .from(productReviews) + .innerJoin(users, eq(productReviews.userId, users.id)) + .where(eq(productReviews.productId, productId)) + .orderBy(desc(productReviews.reviewTime)) + .limit(limit) + .offset(offset) +} + +/** + * Count reviews for product + */ +export async function countProductReviews(productId: number) { + const result = await db + .select({ count: sql`count(*)` }) + .from(productReviews) + .where(eq(productReviews.productId, productId)) + + return Number(result[0].count) +} + +/** + * Check if product exists + */ +export async function checkProductExists(productId: number) { + return db.query.productInfo.findFirst({ + where: eq(productInfo.id, productId), + }) +} + +/** + * Insert new review + */ +export async function insertReview(data: { + userId: number + productId: number + reviewBody: string + ratings: number + imageUrls: string[] +}) { + const normalized = { + ...data, + imageUrls: toJsonString(data.imageUrls, '[]'), + } + return db.insert(productReviews).values(normalized).returning() +} diff --git a/apps/db-helper-sqlite/src/stores/product-store.ts b/apps/db-helper-sqlite/src/stores/product-store.ts new file mode 100644 index 0000000..beed758 --- /dev/null +++ b/apps/db-helper-sqlite/src/stores/product-store.ts @@ -0,0 +1,188 @@ +// import redisClient from '@/src/stores/redis-client'; +import redisClient from '@/src/lib/redis-client'; +import { db } from '../db/db_index' +import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo, productTags, productTagInfo } from '../db/schema' +import { eq, and, gt, sql } from 'drizzle-orm'; +import { generateSignedUrlsFromS3Urls, scaffoldAssetUrl } from '@/src/lib/s3-client'; + +// Uniform Product Type (matches getProductDetails return) +interface Product { + id: number; + name: string; + shortDescription: string | null; + longDescription: string | null; + price: string; + marketPrice: string | null; + unitNotation: string; + images: string[]; + isOutOfStock: boolean; + store: { id: number; name: string; description: string | null } | null; + incrementStep: number; + productQuantity: number; + isFlashAvailable: boolean; + flashPrice: string | null; + deliverySlots: Array<{ id: number; deliveryTime: Date; freezeTime: Date; isCapacityFull: boolean }>; + specialDeals: Array<{ quantity: string; price: string; validTill: Date }>; + productTags: string[]; +} + +export async function initializeProducts(): Promise { + try { + console.log('Initializing product store in Redis...'); + + // Fetch all products with full details (similar to productMega logic) + const productsData = await db + .select({ + id: productInfo.id, + name: productInfo.name, + shortDescription: productInfo.shortDescription, + longDescription: productInfo.longDescription, + price: productInfo.price, + marketPrice: productInfo.marketPrice, + images: productInfo.images, + isOutOfStock: productInfo.isOutOfStock, + storeId: productInfo.storeId, + unitShortNotation: units.shortNotation, + incrementStep: productInfo.incrementStep, + productQuantity: productInfo.productQuantity, + isFlashAvailable: productInfo.isFlashAvailable, + flashPrice: productInfo.flashPrice, + }) + .from(productInfo) + .innerJoin(units, eq(productInfo.unitId, units.id)); + + // Fetch all stores + const allStores = await db.query.storeInfo.findMany({ + columns: { id: true, name: true, description: true }, + }); + const storeMap = new Map(allStores.map(s => [s.id, s])); + + // Fetch all delivery slots (excluding full capacity slots) + const allDeliverySlots = await db + .select({ + productId: productSlots.productId, + id: deliverySlotInfo.id, + deliveryTime: deliverySlotInfo.deliveryTime, + freezeTime: deliverySlotInfo.freezeTime, + isCapacityFull: deliverySlotInfo.isCapacityFull, + }) + .from(productSlots) + .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) + .where( + and( + eq(deliverySlotInfo.isActive, true), + eq(deliverySlotInfo.isCapacityFull, false), + gt(deliverySlotInfo.deliveryTime, new Date()) + ) + ); + const deliverySlotsMap = new Map(); + for (const slot of allDeliverySlots) { + if (!deliverySlotsMap.has(slot.productId)) deliverySlotsMap.set(slot.productId, []); + deliverySlotsMap.get(slot.productId)!.push(slot); + } + + // Fetch all special deals + const allSpecialDeals = await db + .select({ + productId: specialDeals.productId, + quantity: specialDeals.quantity, + price: specialDeals.price, + validTill: specialDeals.validTill, + }) + .from(specialDeals) + .where(gt(specialDeals.validTill, new Date())); + const specialDealsMap = new Map(); + for (const deal of allSpecialDeals) { + if (!specialDealsMap.has(deal.productId)) specialDealsMap.set(deal.productId, []); + specialDealsMap.get(deal.productId)!.push(deal); + } + + // Fetch all product tags + const allProductTags = await db + .select({ + productId: productTags.productId, + tagName: productTagInfo.tagName, + }) + .from(productTags) + .innerJoin(productTagInfo, eq(productTags.tagId, productTagInfo.id)); + const productTagsMap = new Map(); + for (const tag of allProductTags) { + if (!productTagsMap.has(tag.productId)) productTagsMap.set(tag.productId, []); + productTagsMap.get(tag.productId)!.push(tag.tagName); + } + + // Store each product in Redis + for (const product of productsData) { + const signedImages = scaffoldAssetUrl((product.images as string[]) || []); + const store = product.storeId ? storeMap.get(product.storeId) || null : null; + const deliverySlots = deliverySlotsMap.get(product.id) || []; + const specialDeals = specialDealsMap.get(product.id) || []; + const productTags = productTagsMap.get(product.id) || []; + + const productObj: Product = { + id: product.id, + name: product.name, + shortDescription: product.shortDescription, + longDescription: product.longDescription, + price: product.price.toString(), + marketPrice: product.marketPrice?.toString() || null, + unitNotation: product.unitShortNotation, + images: signedImages, + isOutOfStock: product.isOutOfStock, + store: store ? { id: store.id, name: store.name, description: store.description } : null, + incrementStep: product.incrementStep, + productQuantity: product.productQuantity, + isFlashAvailable: product.isFlashAvailable, + flashPrice: product.flashPrice?.toString() || null, + deliverySlots: deliverySlots.map(s => ({ id: s.id, deliveryTime: s.deliveryTime, freezeTime: s.freezeTime, isCapacityFull: s.isCapacityFull })), + specialDeals: specialDeals.map(d => ({ quantity: d.quantity.toString(), price: d.price.toString(), validTill: d.validTill })), + productTags: productTags, + }; + + + await redisClient.set(`product:${product.id}`, JSON.stringify(productObj)); + } + + console.log('Product store initialized successfully'); + } catch (error) { + console.error('Error initializing product store:', error); + } +} + +export async function getProductById(id: number): Promise { + try { + const key = `product:${id}`; + const data = await redisClient.get(key); + if (!data) return null; + return JSON.parse(data) as Product; + } catch (error) { + console.error(`Error getting product ${id}:`, error); + return null; + } +} + +export async function getAllProducts(): Promise { + try { + // Get all keys matching the pattern "product:*" + const keys = await redisClient.KEYS('product:*'); + + if (keys.length === 0) { + return []; + } + + // Get all products using MGET for better performance + const productsData = await redisClient.MGET(keys); + + const products: Product[] = []; + for (const productData of productsData) { + if (productData) { + products.push(JSON.parse(productData) as Product); + } + } + + return products; + } catch (error) { + console.error('Error getting all products:', error); + return []; + } +} diff --git a/apps/db-helper-sqlite/src/trpc/apis/common-apis/common-trpc-index.ts b/apps/db-helper-sqlite/src/trpc/apis/common-apis/common-trpc-index.ts new file mode 100644 index 0000000..783b0e6 --- /dev/null +++ b/apps/db-helper-sqlite/src/trpc/apis/common-apis/common-trpc-index.ts @@ -0,0 +1,137 @@ +import { router, publicProcedure, protectedProcedure } from '@/src/trpc/trpc-index' +import { commonRouter } from '@/src/trpc/apis/common-apis/common' +import { db } from '../../../db/db_index' +import { keyValStore, productInfo, storeInfo } from '../../../db/schema' +import * as turf from '@turf/turf'; +import { z } from 'zod'; +import { mbnrGeoJson } from '@/src/lib/mbnr-geojson' +import { generateUploadUrl } from '@/src/lib/s3-client' +import { ApiError } from '@/src/lib/api-error' +import { getAllConstValues } from '@/src/lib/const-store' +import { CONST_KEYS } from '@/src/lib/const-keys' +import { assetsDomain, apiCacheKey } from '@/src/lib/env-exporter' + +const polygon = turf.polygon(mbnrGeoJson.features[0].geometry.coordinates); + +export async function scaffoldEssentialConsts() { + const consts = await getAllConstValues(); + + return { + freeDeliveryThreshold: consts[CONST_KEYS.freeDeliveryThreshold] ?? 200, + deliveryCharge: consts[CONST_KEYS.deliveryCharge] ?? 0, + flashFreeDeliveryThreshold: consts[CONST_KEYS.flashFreeDeliveryThreshold] ?? 500, + flashDeliveryCharge: consts[CONST_KEYS.flashDeliveryCharge] ?? 69, + popularItems: consts[CONST_KEYS.popularItems] ?? '5,3,2,4,1', + versionNum: consts[CONST_KEYS.versionNum] ?? '1.1.0', + playStoreUrl: consts[CONST_KEYS.playStoreUrl] ?? 'https://play.google.com/store/apps/details?id=in.freshyo.app', + appStoreUrl: consts[CONST_KEYS.appStoreUrl] ?? 'https://apps.apple.com/in/app/freshyo/id6756889077', + webViewHtml: null, + isWebviewClosable: true, + isFlashDeliveryEnabled: consts[CONST_KEYS.isFlashDeliveryEnabled] ?? true, + supportMobile: consts[CONST_KEYS.supportMobile] ?? '', + supportEmail: consts[CONST_KEYS.supportEmail] ?? '', + assetsDomain, + apiCacheKey, + }; +} + +export const commonApiRouter = router({ + product: commonRouter, + getStoresSummary: publicProcedure + .query(async () => { + const stores = await db.query.storeInfo.findMany({ + columns: { + id: true, + name: true, + description: true, + }, + }); + + return { + stores, + }; + }), + checkLocationInPolygon: publicProcedure + .input(z.object({ + lat: z.number().min(-90).max(90), + lng: z.number().min(-180).max(180), + })) + .query(({ input }) => { + try { + const { lat, lng } = input; + const point = turf.point([lng, lat]); // GeoJSON: [longitude, latitude] + const isInside = turf.booleanPointInPolygon(point, polygon); + return { isInside }; + } catch (error) { + throw new Error('Invalid coordinates or polygon data'); + } + }), + + generateUploadUrls: protectedProcedure + .input(z.object({ + contextString: z.enum(['review', 'product_info', 'store', 'notification', 'profile', 'complaint']), + mimeTypes: z.array(z.string()), + })) + .mutation(async ({ input }): Promise<{ uploadUrls: string[] }> => { + const { contextString, mimeTypes } = input; + + const uploadUrls: string[] = []; + const keys: string[] = []; + + for (const mimeType of mimeTypes) { + // Generate key based on context and mime type + let folder: string; + if (contextString === 'review') { + folder = 'review-images'; + } else if (contextString === 'product_info') { + folder = 'product-images'; + } else if (contextString === 'store') { + folder = 'store-images'; + } else if (contextString === 'profile') { + folder = 'profile-images'; + } else if (contextString === 'complaint') { + folder = 'complaint-images'; + } + // else if (contextString === 'review_response') { + // + // folder = 'review-response-images'; + // } + else { + folder = ''; + } + + const extension = mimeType === 'image/jpeg' ? '.jpg' : + mimeType === 'image/png' ? '.png' : + mimeType === 'image/gif' ? '.gif' : '.jpg'; + const key = `${folder}/${Date.now()}${extension}`; + + try { + const uploadUrl = await generateUploadUrl(key, mimeType); + uploadUrls.push(uploadUrl); + keys.push(key); + + } catch (error) { + console.error('Error generating upload URL:', error); + throw new ApiError('Failed to generate upload URL', 500); + } + } + return { uploadUrls }; + }), + healthCheck: publicProcedure + .query(async () => { + // Test DB connection by selecting product names + // await db.select({ name: productInfo.name }).from(productInfo).limit(1); + await db.select({ key: keyValStore.key }).from(keyValStore).limit(1); + + return { + status: "ok", + }; + }), + essentialConsts: publicProcedure + .query(async () => { + const response = await scaffoldEssentialConsts(); + return response; + }), +}); + +export type CommonApiRouter = typeof commonApiRouter; diff --git a/apps/db-helper-sqlite/src/trpc/apis/common-apis/common.ts b/apps/db-helper-sqlite/src/trpc/apis/common-apis/common.ts new file mode 100644 index 0000000..7a2f355 --- /dev/null +++ b/apps/db-helper-sqlite/src/trpc/apis/common-apis/common.ts @@ -0,0 +1,116 @@ +import { router, publicProcedure } from '@/src/trpc/trpc-index' +import { db } from '../../../db/db_index' +import { productInfo, units, productSlots, deliverySlotInfo, storeInfo } from '../../../db/schema' +import { eq, gt, and, sql, inArray } from 'drizzle-orm'; +import { generateSignedUrlsFromS3Urls, generateSignedUrlFromS3Url } from '@/src/lib/s3-client' +import { getAllProducts as getAllProductsFromCache } from '@/src/stores/product-store' +import { getDashboardTags as getDashboardTagsFromCache } from '@/src/stores/product-tag-store' + +export const getNextDeliveryDate = async (productId: number): Promise => { + const result = await db + .select({ deliveryTime: deliverySlotInfo.deliveryTime }) + .from(productSlots) + .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) + .where( + and( + eq(productSlots.productId, productId), + eq(deliverySlotInfo.isActive, true), + eq(deliverySlotInfo.isCapacityFull, false), + gt(deliverySlotInfo.deliveryTime, new Date()) + ) + ) + .orderBy(deliverySlotInfo.deliveryTime) + .limit(1); + + + return result[0]?.deliveryTime || null; +}; + +export async function scaffoldProducts() { + // Get all products from cache + let products = await getAllProductsFromCache(); + products = products.filter(item => Boolean(item.id)) + + // Get suspended product IDs to filter them out + const suspendedProducts = await db + .select({ id: productInfo.id }) + .from(productInfo) + .where(eq(productInfo.isSuspended, true)); + + const suspendedProductIds = new Set(suspendedProducts.map(sp => sp.id)); + + // Filter out suspended products + products = products.filter(product => !suspendedProductIds.has(product.id)); + + // Format products to match the expected response structure + const formattedProducts = await Promise.all( + products.map(async (product) => { + const nextDeliveryDate = await getNextDeliveryDate(product.id); + return { + id: product.id, + name: product.name, + shortDescription: product.shortDescription, + price: parseFloat(product.price), + marketPrice: product.marketPrice ? parseFloat(product.marketPrice) : null, + unit: product.unitNotation, + unitNotation: product.unitNotation, + incrementStep: product.incrementStep, + productQuantity: product.productQuantity, + storeId: product.store?.id || null, + isOutOfStock: product.isOutOfStock, + isFlashAvailable: product.isFlashAvailable, + nextDeliveryDate: nextDeliveryDate ? nextDeliveryDate.toISOString() : null, + images: product.images, + flashPrice: product.flashPrice + }; + }) + ); + + return { + products: formattedProducts, + count: formattedProducts.length, + }; +} + +export const commonRouter = router({ + getDashboardTags: publicProcedure + .query(async () => { + // Get dashboard tags from cache + const tags = await getDashboardTagsFromCache(); + + return { + tags: tags, + }; + }), + + getAllProductsSummary: publicProcedure + .query(async () => { + const response = await scaffoldProducts(); + return response; + }), + + getStoresSummary: publicProcedure + .query(async () => { + const stores = await db.query.storeInfo.findMany({ + columns: { + id: true, + name: true, + description: true, + }, + }); + + return { + stores, + }; + }), + + healthCheck: publicProcedure + .query(async () => { + // Test DB connection by selecting product names + await db.select({ name: productInfo.name }).from(productInfo).limit(1); + + return { + status: "ok", + }; + }), +}); diff --git a/apps/db-helper-sqlite/tsconfig.json b/apps/db-helper-sqlite/tsconfig.json new file mode 100644 index 0000000..3b1e847 --- /dev/null +++ b/apps/db-helper-sqlite/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/src/db/*": ["./src/db/*"], + "@/src/*": ["../backend/src/*"], + "@/src/trpc/*": ["../backend/src/trpc/*"], + "@/src/lib/*": ["../backend/src/lib/*"], + "@/src/stores/*": ["../backend/src/stores/*"] + } + } +} diff --git a/apps/migrator/README.md b/apps/migrator/README.md index 30c87c0..09a8832 100644 --- a/apps/migrator/README.md +++ b/apps/migrator/README.md @@ -17,39 +17,40 @@ Data-only migration tool between Postgres and SQLite using Drizzle. Run from repo root: -```bash -bun --cwd apps/migrator run migrate --from postgres --to sqlite --source "" --target "" -``` +1) Edit the TypeScript config file: + +`apps/migrator/migrator.config.ts` + +2) Run migration: ```bash -bun --cwd apps/migrator run migrate --from sqlite --to postgres --source "" --target "" +bun --cwd apps/migrator run migrate ``` -### Flags +### Config fields (`apps/migrator/migrator.config.ts`) -- `--from`: `postgres` or `sqlite` -- `--to`: `postgres` or `sqlite` -- `--source`: Postgres connection string or SQLite file path -- `--target`: Postgres connection string or SQLite file path -- `--batch`: optional batch size (default: `500`) +- `from`: `postgres` or `sqlite` +- `to`: `postgres` or `sqlite` +- `source`: Postgres connection string or SQLite file path +- `target`: Postgres connection string or SQLite file path +- `batchSize`: optional batch size (default: `500`) ## Examples ```bash -bun --cwd apps/migrator run migrate \ - --from postgres \ - --to sqlite \ - --source "postgres://user:pass@host:5432/db" \ - --target "./sqlite.db" +Update `apps/migrator/migrator.config.ts` and run: + +```bash +bun --cwd apps/migrator run migrate +``` ``` ```bash -bun --cwd apps/migrator run migrate \ - --from sqlite \ - --to postgres \ - --source "./sqlite.db" \ - --target "postgres://user:pass@host:5432/db" \ - --batch 1000 +Update `apps/migrator/migrator.config.ts` and run: + +```bash +bun --cwd apps/migrator run migrate +``` ``` ## Notes diff --git a/apps/migrator/migrator.config.ts b/apps/migrator/migrator.config.ts new file mode 100644 index 0000000..fccdd96 --- /dev/null +++ b/apps/migrator/migrator.config.ts @@ -0,0 +1,11 @@ +import type { MigrationConfig } from './src/lib/types' + +const config: MigrationConfig = { + from: 'postgres', + to: 'sqlite', + source: 'postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer' , + target: './sqlite.db', + batchSize: 500, +} + +export default config diff --git a/apps/migrator/mydatabase.db b/apps/migrator/mydatabase.db new file mode 100644 index 0000000..e69de29 diff --git a/apps/migrator/sqlite.db b/apps/migrator/sqlite.db new file mode 100644 index 0000000..c2eb574 Binary files /dev/null and b/apps/migrator/sqlite.db differ diff --git a/apps/migrator/src/index.ts b/apps/migrator/src/index.ts index abf9d3c..42db74c 100644 --- a/apps/migrator/src/index.ts +++ b/apps/migrator/src/index.ts @@ -1,8 +1,11 @@ import { runPostgresToSqlite } from './postgres_to_sqlite' import { runSqliteToPostgres } from './sqlite_to_postgres' -import { parseArgs } from './lib/args' +import config from '../migrator.config' -const args = parseArgs(Bun.argv.slice(2)) +const args = { + ...config, + batchSize: config.batchSize ?? 500, +} if (args.from === 'postgres' && args.to === 'sqlite') { await runPostgresToSqlite(args) diff --git a/apps/migrator/src/lib/args.ts b/apps/migrator/src/lib/args.ts deleted file mode 100644 index b34c823..0000000 --- a/apps/migrator/src/lib/args.ts +++ /dev/null @@ -1,50 +0,0 @@ -export type MigrationArgs = { - from: 'postgres' | 'sqlite' - to: 'postgres' | 'sqlite' - source: string - target: string - batchSize: number -} - -const getFlagValue = (args: string[], flag: string): string | undefined => { - const index = args.indexOf(flag) - if (index === -1) return undefined - return args[index + 1] -} - -export const parseArgs = (args: string[]): MigrationArgs => { - const from = getFlagValue(args, '--from') - const to = getFlagValue(args, '--to') - const source = getFlagValue(args, '--source') - const target = getFlagValue(args, '--target') - const batch = getFlagValue(args, '--batch') - - if (!from || (from !== 'postgres' && from !== 'sqlite')) { - throw new Error('Missing or invalid --from (postgres|sqlite)') - } - - if (!to || (to !== 'postgres' && to !== 'sqlite')) { - throw new Error('Missing or invalid --to (postgres|sqlite)') - } - - if (!source) { - throw new Error('Missing --source') - } - - if (!target) { - throw new Error('Missing --target') - } - - const batchSize = batch ? Number(batch) : 500 - if (Number.isNaN(batchSize) || batchSize <= 0) { - throw new Error('Invalid --batch, must be a positive number') - } - - return { - from, - to, - source, - target, - batchSize, - } -} diff --git a/apps/migrator/src/lib/migrate.ts b/apps/migrator/src/lib/migrate.ts index df35d5f..bf4d4d9 100644 --- a/apps/migrator/src/lib/migrate.ts +++ b/apps/migrator/src/lib/migrate.ts @@ -3,7 +3,7 @@ import type { TableName } from './table-order' import { tableOrder } from './table-order' import { postgresTables, sqliteTables, type TableMap } from './schema-maps' import { booleanColumns, jsonArrayColumns, jsonColumns, jsonObjectColumns, timestampColumns } from './column-maps' -import { parseJsonValue, toBoolean, toDate, toEpochSeconds, toJsonString } from './casts' +import { parseJsonValue, toBoolean, toDate, toJsonString } from './casts' export type Dialect = 'postgres' | 'sqlite' @@ -59,11 +59,8 @@ const normalizeJson = (value: unknown, tableName: TableName, column: string, toD return parseJsonValue(value, fallback) } -const normalizeTimestamps = (value: unknown, toDialect: Dialect) => { +const normalizeTimestamps = (value: unknown) => { if (value === null || value === undefined) return value - if (toDialect === 'sqlite') { - return toEpochSeconds(value as Date | number) - } return toDate(value as number | Date) } @@ -86,7 +83,7 @@ const transformRow = (row: Record, tableName: TableName, toDial }) timeCols.forEach((column) => { - output[column] = normalizeTimestamps(output[column], toDialect) + output[column] = normalizeTimestamps(output[column]) }) boolCols.forEach((column) => { diff --git a/apps/migrator/src/lib/types.ts b/apps/migrator/src/lib/types.ts new file mode 100644 index 0000000..c9c914b --- /dev/null +++ b/apps/migrator/src/lib/types.ts @@ -0,0 +1,7 @@ +export type MigrationConfig = { + from: 'postgres' | 'sqlite' + to: 'postgres' | 'sqlite' + source: string + target: string + batchSize?: number +} diff --git a/apps/migrator/src/postgres_to_sqlite/index.ts b/apps/migrator/src/postgres_to_sqlite/index.ts index 878e547..8ed80c7 100644 --- a/apps/migrator/src/postgres_to_sqlite/index.ts +++ b/apps/migrator/src/postgres_to_sqlite/index.ts @@ -1,8 +1,8 @@ -import type { MigrationArgs } from '../lib/args' +import type { MigrationConfig } from '../lib/types' import { createPostgresDb, createSqliteDb } from '../lib/db' import { runMigration } from '../lib/migrate' -export const runPostgresToSqlite = async (args: MigrationArgs) => { +export const runPostgresToSqlite = async (args: MigrationConfig & { batchSize: number }) => { const { db: sourceDb, pool } = createPostgresDb(args.source) const { db: targetDb, sqlite } = createSqliteDb(args.target) diff --git a/apps/migrator/src/postgres_to_sqlite/sqlite.db b/apps/migrator/src/postgres_to_sqlite/sqlite.db new file mode 100644 index 0000000..d296495 Binary files /dev/null and b/apps/migrator/src/postgres_to_sqlite/sqlite.db differ diff --git a/apps/migrator/src/sqlite_to_postgres/index.ts b/apps/migrator/src/sqlite_to_postgres/index.ts index 3396241..70acd77 100644 --- a/apps/migrator/src/sqlite_to_postgres/index.ts +++ b/apps/migrator/src/sqlite_to_postgres/index.ts @@ -1,8 +1,8 @@ -import type { MigrationArgs } from '../lib/args' +import type { MigrationConfig } from '../lib/types' import { createPostgresDb, createSqliteDb } from '../lib/db' import { runMigration } from '../lib/migrate' -export const runSqliteToPostgres = async (args: MigrationArgs) => { +export const runSqliteToPostgres = async (args: MigrationConfig & { batchSize: number }) => { const { db: sourceDb, sqlite } = createSqliteDb(args.source) const { db: targetDb, pool } = createPostgresDb(args.target) diff --git a/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx b/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx index 05604b0..77aa0ad 100755 --- a/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx +++ b/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx @@ -380,6 +380,7 @@ export default function Dashboard() { error, } = useAllProducts(); + const { data: essentialConsts, isLoading: isLoadingConsts, error: constsError, refetch: refetchConsts } = useGetEssentialConsts(); const { data: storesData, refetch: refetchStores } = useStores(); diff --git a/apps/user-ui/components/ProductCard.tsx b/apps/user-ui/components/ProductCard.tsx index 5206dd8..6be6716 100644 --- a/apps/user-ui/components/ProductCard.tsx +++ b/apps/user-ui/components/ProductCard.tsx @@ -1,5 +1,6 @@ -import React from 'react'; -import { View, Alert, ActivityIndicator } from 'react-native'; + +import React, { useMemo } from 'react'; +import { View, Alert, TouchableOpacity, Text } from 'react-native'; import { Image } from 'expo-image'; import { tw, theme, MyText, MyTouchableOpacity, Quantifier, MiniQuantifier } from 'common-ui'; import CartIcon from '@/components/icons/CartIcon'; @@ -14,11 +15,7 @@ import { } from '@/hooks/cart-query-hooks'; import { useProductSlotIdentifier } from '@/hooks/useProductSlotIdentifier'; import { useCartStore } from '@/src/store/cartStore'; -<<<<<<< HEAD -import { useCentralSlotStore } from '@/src/store/centralSlotStore'; -======= import { trpc } from '@/src/trpc-client'; ->>>>>>> main import { Image as RnImage } from 'react-native' @@ -50,18 +47,6 @@ const ProductCard: React.FC = ({ containerComp: ContainerComp = React.Fragment, useAddToCartDialog = false, }) => { - const imageUri = item.images?.[0] - const [imageStatus, setImageStatus] = React.useState<'loading' | 'loaded' | 'error'>('loading') - const [imageError, setImageError] = React.useState(null) - const [updater, setUpdater] = React.useState(0) - - React.useEffect(() => { - const intervalId = setInterval(() => { - setUpdater(prev => prev + 1) - }, 5000) - return () => clearInterval(intervalId) - }, []) - const { data: cartData } = useGetCart(); const { getQuickestSlot } = useProductSlotIdentifier(); const { setAddedToCartProduct } = useCartStore(); @@ -85,41 +70,25 @@ const ProductCard: React.FC = ({ const cartItem = cartData?.items?.find((cartItem: any) => cartItem.productId === item.id); const quantity = cartItem?.quantity || 0; - // Get slots data from central store - const slots = useCentralSlotStore((state) => state.slots); - const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap); + // Query all slots with products + const { data: slotsData } = trpc.user.slots.getSlotsWithProducts.useQuery(); // Create slot lookup map - const slotMap = React.useMemo(() => { + const slotMap = useMemo(() => { const map: Record = {}; - slots?.forEach((slot: any) => { + slotsData?.slots?.forEach((slot: any) => { map[slot.id] = slot; }); return map; - }, [slots]); + }, [slotsData]); // Get cart item's slot delivery time if item is in cart const cartSlot = cartItem?.slotId ? slotMap[cartItem.slotId] : null; const displayDeliveryDate = cartSlot?.deliveryTime || item.nextDeliveryDate; - React.useEffect(() => { - if (imageUri) { - setImageStatus('loading') - setImageError(null) - return - } - - setImageStatus('error') - setImageError('No image available') - }, [imageUri]) - // Precompute the next slot and determine display out of stock status const slotId = getQuickestSlot(item.id); - - // Use isOutOfStock from productSlotsMap (all products now included) - const productSlotInfo = productSlotsMap[item.id]; - const isOutOfStockFromSlots = productSlotInfo?.isOutOfStock; - const displayIsOutOfStock = isOutOfStockFromSlots || !slotId; + const displayIsOutOfStock = item.isOutOfStock || !slotId; // if(item.name.startsWith('Mutton Curry Cut')) { // console.log({slotId, displayIsOutOfStock}) @@ -151,7 +120,6 @@ const ProductCard: React.FC = ({ } }; - // console.log('rendering the product cart for id', item.id) return ( = ({ > >>>>>> main style={{ width: "100%", height: itemWidth, resizeMode: "cover" }} - onLoadStart={() => { - setImageStatus('loading') - setImageError(null) - }} - // onLoadEnd={() => { - // setImageError('loading stopped indefinitely') - // - // }} - onLoad={() => setImageStatus('loaded')} - onError={(event) => { - setImageStatus('error') - setImageError( 'Image failed to load') - }} /> - - {imageStatus === 'error' && ( - - - - {imageError || 'Image failed to load'} - - - )} {displayIsOutOfStock && ( diff --git a/apps/user-ui/src/hooks/prominent-api-hooks.ts b/apps/user-ui/src/hooks/prominent-api-hooks.ts index fab59e8..fed1c32 100644 --- a/apps/user-ui/src/hooks/prominent-api-hooks.ts +++ b/apps/user-ui/src/hooks/prominent-api-hooks.ts @@ -33,6 +33,7 @@ function useCacheUrl(filename: string): string | null { export function useAllProducts() { const cacheUrl = useCacheUrl(CACHE_FILENAMES.products) + console.log(cacheUrl) return useQuery({ queryKey: ['all-products', cacheUrl], queryFn: async () => { diff --git a/packages/ui/index.ts b/packages/ui/index.ts index b182e69..e333601 100755 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -64,16 +64,13 @@ const isDevMode = Constants.executionEnvironment !== "standalone"; // const BASE_API_URL = API_URL; // const BASE_API_URL = 'http://10.0.2.2:4000'; // const BASE_API_URL = 'http://192.168.100.101:4000'; -// const BASE_API_URL = 'http://192.168.1.5:4000'; -<<<<<<< HEAD +const BASE_API_URL = 'http://192.168.1.5:4000'; // let BASE_API_URL = "https://mf.freshyo.in"; // let BASE_API_URL = "https://freshyo.technocracy.ovh"; -let BASE_API_URL = 'http://192.168.100.108:4000'; -======= -let BASE_API_URL = "https://raw.freshyo.in"; +// let BASE_API_URL = 'http://192.168.100.108:4000'; +// let BASE_API_URL = "https://raw.freshyo.in"; // let BASE_API_URL = "https://freshyo.technocracy.ovh"; // let BASE_API_URL = 'http://192.168.100.108:4000'; ->>>>>>> main // let BASE_API_URL = 'http://192.168.29.176:4000'; // if(isDevMode) { diff --git a/sqlite.db b/sqlite.db new file mode 100644 index 0000000..d296495 Binary files /dev/null and b/sqlite.db differ