Spaces:
Runtime error
Runtime error
import express, { Router } from "express"; | |
import serverless from "serverless-http"; | |
import Redis from "ioredis"; | |
import { initClient } from './utils.js'; | |
import { errorHandler, setJioCopilotSessionCookie } from './middleware.js'; | |
import swaggerJSDoc from 'swagger-jsdoc'; | |
import yaml from 'js-yaml' | |
import fs from 'fs'; | |
import { RedisListCache } from './cache.js'; | |
import { | |
getApplications, | |
createApplication, | |
getLocations, | |
createLocation, | |
updateLocation, | |
getBrands, | |
createBrand, | |
updateBrand, | |
createProduct, | |
createInventory, | |
getCompanyCreds, | |
createUpdateCompanyCreds | |
} from './functions.js' | |
import OpenAI from 'openai'; | |
import compression from 'compression'; | |
import cookieParser from 'cookie-parser'; | |
const listCache = new RedisListCache({ namespace: "fc" }) | |
const redis = new Redis({ | |
host: 'redis-12291.c305.ap-south-1-1.ec2.cloud.redislabs.com', | |
port: 12291, | |
password: 'KQCVapXXF2ioM4zF5krQFImzAYkKWY5l', | |
username: "default" | |
}); | |
const skills = { | |
getApplications, | |
createApplication, | |
getLocations, | |
createLocation, | |
updateLocation, | |
getBrands, | |
createBrand, | |
updateBrand, | |
createProduct, | |
createInventory, | |
getCompanyCreds, | |
createUpdateCompanyCreds | |
} | |
const app = express(); | |
const router = Router(); | |
api.use("/api/", router); | |
const port = process.env.PORT; | |
app.use(express.json({})); | |
const swaggerDefinition = { | |
openapi: '3.0.0', | |
info: { | |
title: 'Express API Documentation', | |
version: '1.0.0', | |
description: 'This is the API documentation for my Express application.' | |
}, | |
servers: [{ | |
url: "https://934a-45-119-30-178.ngrok-free.app" | |
}] | |
}; | |
const options = { | |
swaggerDefinition, | |
// Path to the API docs | |
apis: ['./index.js'], // Adjust the path according to your file structure | |
}; | |
app.use(compression()) | |
app.use(cookieParser()) | |
/** | |
* @swagger | |
* components: | |
* schemas: | |
* CompanyCreds: | |
* type: object | |
* properties: | |
* clientId: | |
* type: string | |
* description: Client ID for the company | |
* clientSecret: | |
* type: string | |
* description: Client secret for the company | |
* required: | |
* - clientId | |
* - clientSecret | |
* CreateUpdateLocation: | |
* type: object | |
* required: | |
* - code | |
* - name | |
* - gst | |
* - manager | |
* - address | |
* properties: | |
* code: | |
* type: string | |
* description: Unique code for the location | |
* name: | |
* type: string | |
* description: Name of the location | |
* gst: | |
* type: object | |
* required: | |
* - legal_name | |
* - value | |
* properties: | |
* legal_name: | |
* type: string | |
* description: Legal name for GST purposes | |
* value: | |
* type: string | |
* description: GST value | |
* manager: | |
* type: object | |
* required: | |
* - manager_name | |
* - number | |
* - country_code | |
* properties: | |
* manager_name: | |
* type: string | |
* description: Name of the manager | |
* email: | |
* type: string | |
* description: Email of the manager | |
* number: | |
* type: string | |
* description: Contact number of the manager | |
* country_code: | |
* type: string | |
* description: Country code for the manager's contact number | |
* address: | |
* type: object | |
* required: | |
* - address1 | |
* - country | |
* - pincode | |
* - city | |
* - state | |
* properties: | |
* address1: | |
* type: string | |
* description: Primary address line | |
* address2: | |
* type: string | |
* description: Secondary address line | |
* country: | |
* type: string | |
* description: Country of the location | |
* pincode: | |
* type: string | |
* description: Postal code of the location | |
* city: | |
* type: string | |
* description: City of the location | |
* state: | |
* type: string | |
* description: State of the location | |
* latitude: | |
* type: number | |
* description: Latitude for the location | |
* longitude: | |
* type: number | |
* description: Longitude for the location | |
* landmark: | |
* type: string | |
* description: Landmark near the location | |
* Brand: | |
* type: object | |
* properties: | |
* name: | |
* type: string | |
* logo: | |
* type: string | |
* id: | |
* type: string | |
* BrandCreation: | |
* type: object | |
* required: | |
* - name | |
* - logo | |
* - description | |
* properties: | |
* name: | |
* type: string | |
* logo: | |
* type: string | |
* description: | |
* type: string | |
* ErrorResponse: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* description: Error message | |
*/ | |
router.get(['/swagger.yaml', '/swagger.json'], (req, res) => { | |
// Initialize swagger-jsdoc | |
const swaggerSpec = swaggerJSDoc(options); | |
// Convert to YAML | |
const swaggerYAML = yaml.dump(swaggerSpec); | |
switch (req.path.split('.').pop()) { | |
case 'json': | |
res.setHeader('Content-Type', 'application/json'); | |
return res.send(JSON.stringify(swaggerSpec, null, 2)); | |
case 'yaml': | |
res.setHeader('Content-Type', 'text/yaml'); | |
return res.send(swaggerYAML); | |
} | |
}); | |
/** | |
* @swagger | |
* /_healthz: | |
* get: | |
* operationId: getHealthZ | |
* description: Check server health | |
* responses: | |
* 200: | |
* description: Server is healthy | |
* content: | |
* text/plain: | |
* schema: | |
* type: string | |
* example: Hello World! | |
*/ | |
router.get("/_healthz", (req, res) => { | |
res.send('Hello World!'); | |
}); | |
router.get("/privacy", (req, res) => { | |
res.send('This is the privacy policy page!'); | |
}); | |
/** | |
* @swagger | |
* /company/{companyId}: | |
* get: | |
* operationId: getOrVerifyCompanyCredentials | |
* description: Retrieve company credentials | |
* tags: [Company] | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The company ID | |
* responses: | |
* 200: | |
* description: Company credentials retrieved successfully | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/CompanyCreds' | |
* 404: | |
* description: Company credentials not found | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/ErrorResponse' | |
*/ | |
router.get("/company/:companyId", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const creds = await redis.get(`${companyId}:creds`); | |
if (!creds) { | |
throw { | |
message: "company creds not found" | |
} | |
} | |
res.json({ ...JSON.parse(creds), companyId }) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}: | |
* put: | |
* operationId: updateCompanyCredentials | |
* description: Update company credentials | |
* tags: [Company] | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The company ID | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/CompanyCreds' | |
* responses: | |
* 200: | |
* description: Company credentials updated successfully | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* 400: | |
* description: Invalid input | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/ErrorResponse' | |
*/ | |
router.put("/company/:companyId", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const { clientId, clientSecret } = req.body | |
const creds = await redis.set(`${companyId}:creds`, JSON.stringify({ clientId, clientSecret })); | |
res.json({ message: "company creds saved" }) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/applications: | |
* get: | |
* operationId: getSalesChannelByCompany | |
* description: Retrieve applications for a specific company | |
* tags: [Company] | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The ID of the company to retrieve applications for | |
* responses: | |
* 200: | |
* description: List of applications for the specified company | |
* content: | |
* application/json: | |
* schema: | |
* type: array | |
* items: | |
* type: object | |
* properties: | |
* name: | |
* type: string | |
* description: Name of the application | |
* id: | |
* type: string | |
* description: ID of the application | |
* token: | |
* type: string | |
* description: Token associated with the application | |
* domain: | |
* type: string | |
* description: Primary domain of the application | |
* logo: | |
* type: string | |
* description: Logo URL of the application | |
* 500: | |
* description: Server error | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/ErrorResponse' | |
*/ | |
router.get("/company/:companyId/applications", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const client = await initClient(companyId); | |
let applications = await client.configuration.getApplications({ pageSize: 100 }); | |
applications = applications.items.map(i => { | |
return { | |
name: i.name, | |
id: i.id, | |
token: i.token, | |
domain: i.domains?.find(i => i.is_primary)?.name, | |
logo: i.logo?.secure_url | |
} | |
}) | |
res.json(applications) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/brands: | |
* get: | |
* summary: Retrieves a list of brands for a specific company | |
* tags: [Brands] | |
* operationId: getCompanyBrands | |
* description: Fetches a list of brands associated with the given company ID. | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The unique identifier of the company | |
* responses: | |
* 200: | |
* description: A list of brands | |
* content: | |
* application/json: | |
* schema: | |
* type: array | |
* items: | |
* $ref: '#/components/schemas/Brand' | |
*/ | |
router.get("/company/:companyId/brands", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const client = await initClient(companyId); | |
let brands = await client.companyProfile.getBrands({ pageSize: 300 }); | |
brands = brands.items.map(i => { | |
return { | |
name: i?.brand?.name, | |
logo: i?.brand?.logo, | |
id: i?.brand?.uid, | |
} | |
}) | |
res.json(brands) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/brands: | |
* post: | |
* summary: Creates a new brand for a specific company | |
* tags: [Brands] | |
* operationId: createCompanyBrand | |
* description: Adds a new brand to the company profile. | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The unique identifier of the company | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/BrandCreation' | |
* responses: | |
* 200: | |
* description: Brand created successfully | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* id: | |
* type: string | |
* | |
* /company/{companyId}/brands/{brandId}: | |
* put: | |
* summary: Creates a new brand for a specific company | |
* tags: [Brands] | |
* operationId: updateCompanyBrand | |
* description: updated an existing brand. | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The unique identifier of the company | |
* - in: path | |
* name: brandId | |
* required: true | |
* schema: | |
* type: number | |
* description: The unique identifier of the brand | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/BrandCreation' | |
* responses: | |
* 200: | |
* description: Brand updated successfully | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* id: | |
* type: string | |
*/ | |
router.post("/company/:companyId/brands", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const client = await initClient(companyId); | |
const { | |
name, | |
logo = "https://cdn.pixelbin.io/v2/falling-surf-7c8bb8/fyprod/wrkr/platform/pictures/favicon/original/ZWTmgEoFQ-platform-favicon.png", | |
description | |
} = req.body | |
let brands = await client.companyProfile.createBrand({ | |
body: { | |
name, | |
description, | |
logo, | |
banner: { portrait: logo, landscape: logo } | |
} | |
}); | |
res.json({ message: "brand created", id: brands?.uid }) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
router.put("/company/:companyId/brands/:brandId", async (req, res, next) => { | |
try { | |
const { companyId, brandId } = req.params; | |
const client = await initClient(companyId); | |
const { | |
name, | |
logo = "https://cdn.pixelbin.io/v2/falling-surf-7c8bb8/fyprod/wrkr/platform/pictures/favicon/original/ZWTmgEoFQ-platform-favicon.png", | |
description | |
} = req.body | |
let brands = await client.companyProfile.editBrand({ | |
brandId, | |
body: { | |
name, | |
description, | |
logo, | |
banner: { portrait: logo, landscape: logo } | |
} | |
}); | |
res.json({ message: "brand updated", id: brands?.id }) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/locations: | |
* get: | |
* operationId: getLocationsByCompany | |
* description: get all company locations | |
* tags: [Company] | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The ID of the company to add a location for | |
* responses: | |
* 200: | |
* description: location list | |
* content: | |
* application/json: | |
* schema: | |
* type: array | |
* items: | |
* properties: | |
* id: | |
* type: number | |
* description: Location id | |
* code: | |
* type: string | |
* description: Location code | |
* name: | |
* type: string | |
* description: Location code | |
* documents: | |
* type: array | |
* description: Location gst documents | |
* items: | |
* type: object | |
* properties: | |
* type: | |
* type: string | |
* description: document type | |
* value: | |
* type: string | |
* description: document number | |
* verified: | |
* type: boolean | |
* description: document number verification status | |
* legal_name: | |
* type: boolean | |
* description: document owner | |
* 400: | |
* description: Invalid input | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/ErrorResponse' | |
*/ | |
router.get("/company/:companyId/locations", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const client = await initClient(companyId.toString()); | |
let locations = await client.companyProfile.getLocations({ pageSize: 100 }); | |
locations = locations.items.map(i => { | |
return { | |
name: i.name, | |
id: i.uid, | |
code: i.code, | |
documents: i.documents | |
} | |
}) | |
res.json(locations) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/locations: | |
* post: | |
* operationId: createLocationsForCompany | |
* description: Add a new location for a specific company | |
* tags: [Company] | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The ID of the company to add a location for | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/CreateUpdateLocation' | |
* responses: | |
* 200: | |
* description: Location created successfully | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* id: | |
* type: string | |
* description: Location id | |
* 400: | |
* description: Invalid input | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/ErrorResponse' | |
* /company/{companyId}/locations/{locationId}: | |
* put: | |
* operationId: updateLocationByCompany | |
* description: update a location for a specific company | |
* tags: [Company] | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The ID of the company to add a location for | |
* - in: path | |
* name: locationId | |
* required: true | |
* schema: | |
* type: string | |
* description: The ID of the location to be updated | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/CreateUpdateLocation' | |
* responses: | |
* 200: | |
* description: Location updated successfully | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* id: | |
* type: string | |
* description: Location id | |
* 400: | |
* description: Invalid input | |
* content: | |
* application/json: | |
* schema: | |
* $ref: '#/components/schemas/ErrorResponse' | |
*/ | |
router.post("/company/:companyId/locations", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const { code, | |
name, | |
gst: { | |
legal_name, | |
value | |
}, | |
manager: { | |
manager_name, email, number, country_code | |
}, | |
address: { | |
address1, | |
address2, | |
country, | |
pincode, | |
city, | |
state, | |
latitude = 19.2762702, | |
longitude = 72.8929, | |
landmark = "" | |
}, | |
} = req.body | |
const client = await initClient(companyId); | |
let locations = await client.companyProfile.createLocation({ | |
body: { | |
name, | |
display_name: name, | |
code, | |
company: parseInt(companyId), | |
documents: [{ | |
type: "gst", | |
legal_name: legal_name, | |
value: value, verified: true | |
}], | |
address: { | |
"address1": address1, | |
"address2": address2, | |
"country": country, | |
"pincode": pincode, | |
"city": city, | |
"state": state, | |
"latitude": latitude, | |
"longitude": longitude, | |
"landmark": landmark | |
}, | |
manager: { | |
"name": manager_name, | |
"email": email, | |
"mobile_no": { | |
"number": number, | |
"country_code": country_code | |
} | |
}, | |
contact_numbers: [ | |
{ | |
"number": number, | |
"country_code": country_code | |
} | |
], | |
store_type: "high_street" | |
} | |
}); | |
res.json({ message: "location added", id: locations?.uid }) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
router.put("/company/:companyId/locations/locationId", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const { code, | |
name, | |
gst: { | |
legal_name, | |
value | |
}, | |
manager: { | |
manager_name, email, number, country_code | |
}, | |
address: { | |
address1, | |
address2, | |
country, | |
pincode, | |
city, | |
state, | |
latitude = 19.2762702, | |
longitude = 72.8929, | |
landmark = "" | |
}, | |
} = req.body | |
const client = await initClient(companyId); | |
let locations = await client.companyProfile.createLocation({ | |
body: { | |
name, | |
display_name: name, | |
code, | |
company: parseInt(companyId), | |
documents: [{ | |
type: "gst", | |
legal_name: legal_name, | |
value: value, verified: true | |
}], | |
address: { | |
"address1": address1, | |
"address2": address2, | |
"country": country, | |
"pincode": pincode, | |
"city": city, | |
"state": state, | |
"latitude": latitude, | |
"longitude": longitude, | |
"landmark": landmark | |
}, | |
manager: { | |
"name": manager_name, | |
"email": email, | |
"mobile_no": { | |
"number": number, | |
"country_code": country_code | |
} | |
}, | |
contact_numbers: [ | |
{ | |
"number": number, | |
"country_code": country_code | |
} | |
], | |
store_type: "high_street" | |
} | |
}); | |
res.json({ message: "location updated", id: locations?.id }) | |
} catch (e) { | |
next(e) | |
} | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/products: | |
* post: | |
* summary: Creates a new product for a given company. | |
* description: This endpoint creates a new product with various attributes including name, slug, pricing, and more. | |
* operationId: createProduct | |
* tags: | |
* - Products | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: Unique identifier of the company. | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* required: | |
* - name | |
* - slug | |
* - seller_identifier | |
* - brand_id | |
* properties: | |
* name: | |
* type: string | |
* description: Name of the product. | |
* slug: | |
* type: string | |
* description: URL-friendly identifier for the product. | |
* seller_identifier: | |
* type: string | |
* description: Unique identifier for the seller. | |
* brand_id: | |
* type: string | |
* description: Unique identifier for the brand. | |
* location_id: | |
* type: string | |
* description: Location identifier for the product. | |
* mrp: | |
* type: number | |
* default: 999 | |
* description: Maximum retail price of the product. | |
* selling_price: | |
* type: number | |
* default: 499 | |
* description: Selling price of the product. | |
* responses: | |
* 200: | |
* description: Successful creation of the product. | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* id: | |
* type: string | |
* seller_identifier: | |
* type: string | |
*/ | |
router.post("/company/:companyId/products", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const { name, | |
slug, | |
seller_identifier, | |
brand_id, location_id, mrp = 999, selling_price = 499 | |
} = req.body; | |
const obj = { | |
"name": name, | |
"slug": slug, | |
"brand_uid": brand_id, | |
"item_code": seller_identifier, | |
"teaser_tag": {}, | |
"net_quantity": {}, | |
"tax_identifier": { | |
"reporting_hsn": "1202355241H1", | |
"hsn_code": "1202355241", | |
"hsn_code_id": "65769883ba99dcf407a2b1ed" | |
}, | |
"country_of_origin": "India", | |
"variants": {}, | |
"variant_media": {}, | |
"description": "PHA+WW91ciBwcm9kdWN0IGRlc2NyaXB0aW9uPC9wPg==", | |
"short_description": "Your product description", | |
"highlights": [], | |
"company_id": 10, | |
"template_tag": "c2-0-template", | |
"currency": "INR", | |
"media": [], | |
"is_set": false, | |
"sizes": [ | |
{ | |
"size": "OS", | |
"price": mrp, | |
"price_effective": selling_price, | |
"price_transfer": 0, | |
"currency": "INR", | |
"item_length": 1, | |
"item_width": 1, | |
"item_height": 1, | |
"item_weight": 1, | |
"item_dimensions_unit_of_measure": "cm", | |
"item_weight_unit_of_measure": "gram", | |
"track_inventory": true, | |
"identifiers": [ | |
{ | |
"gtin_value": seller_identifier, | |
"gtin_type": "ean", | |
"primary": true | |
} | |
], | |
"_custom_json": {}, | |
"name": "OS" | |
} | |
], | |
"_custom_json": {}, | |
"size_guide": "", | |
"product_group_tag": [], | |
"product_publish": { | |
"product_online_date": "2023-12-11T08:38:10.082Z", | |
"is_set": false | |
}, | |
"is_active": true, | |
"custom_order": { | |
"is_custom_order": false, | |
"manufacturing_time": 0, | |
"manufacturing_time_unit": "hours" | |
}, | |
"multi_size": false, | |
"no_of_boxes": 1, | |
"is_dependent": false, | |
"item_type": "digital", | |
"tags": [], | |
"departments": [ | |
19771 | |
], | |
return_config: { | |
"returnable": false | |
}, | |
"category_slug": "c2-0-cat", | |
"trader": [ | |
{ | |
"type": "Manufacturer", | |
"name": "Manufacturer", | |
"address": [ | |
"Manufacturer Address" | |
] | |
} | |
], | |
"return_config": { | |
"returnable": true, | |
time: 3, | |
unit: "days" | |
} | |
} | |
const client = await initClient(companyId); | |
let product = await client.catalog.createProduct({ | |
body: obj | |
}); | |
product = await client.catalog.getProduct({ | |
itemId: product.uid | |
}); | |
res.json({ | |
message: "product created", | |
id: product.data.uid, | |
seller_identifier: product?.data?.sizes?.[0]?.seller_identifier | |
}); | |
} catch (e) { next(e) } | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/products/{productId}/inventory: | |
* post: | |
* summary: Updates inventory for a specific product. | |
* description: This endpoint updates the inventory details for a given product, including location, pricing, and quantity. | |
* operationId: updateInventory | |
* tags: | |
* - Inventory | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: Unique identifier of the company. | |
* - in: path | |
* name: productId | |
* required: true | |
* schema: | |
* type: string | |
* description: Unique identifier of the product. | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* required: | |
* - location_id | |
* - seller_identifier | |
* properties: | |
* location_id: | |
* type: string | |
* description: Location identifier where the inventory is stored. | |
* mrp: | |
* type: number | |
* default: 999 | |
* description: Maximum retail price of the product. | |
* selling_price: | |
* type: number | |
* default: 499 | |
* description: Selling price of the product. | |
* seller_identifier: | |
* type: string | |
* description: Unique identifier for the seller. | |
* responses: | |
* 200: | |
* description: Successful update of inventory. | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
*/ | |
router.post("/company/:companyId/products/:productId/inventory", async (req, res, next) => { | |
try { | |
const { companyId, productId } = req.params; | |
const { location_id, mrp = 999, selling_price = 499, id, seller_identifier } = req.body; | |
const client = await initClient(companyId); | |
let product = await client.catalog.getProduct({ | |
itemId: productId | |
}); | |
let inv = await client.catalog.updateRealtimeInventory({ | |
itemId: product.data.uid, | |
sellerIdentifier: seller_identifier, | |
body: { | |
company_id: companyId, | |
payload: [{ | |
"seller_identifier": seller_identifier, | |
"store_id": location_id, | |
"price_marked": mrp, | |
"price_effective": selling_price, | |
"total_quantity": 10, | |
"tags": [] | |
}] | |
} | |
}) | |
res.json({ message: "inv created" }); | |
} catch (e) { next(e) } | |
}) | |
/** | |
* @swagger | |
* /company/{companyId}/sales_channel: | |
* post: | |
* operationId: addSalesChannel | |
* summary: Create a sales channel for a given company | |
* description: This endpoint creates a new sales channel for the specified company. | |
* tags: | |
* - Sales Channel | |
* parameters: | |
* - in: path | |
* name: companyId | |
* required: true | |
* schema: | |
* type: string | |
* description: The ID of the company | |
* requestBody: | |
* required: true | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* brand_ids: | |
* type: array | |
* items: | |
* type: integer | |
* description: Array of brand IDs to be associated with the sales channel | |
* name: | |
* type: string | |
* description: Name of the sales channel | |
* subdomain: | |
* type: string | |
* description: subdomain associated with the sales channel | |
* responses: | |
* 200: | |
* description: Sales channel successfully created | |
* content: | |
* application/json: | |
* schema: | |
* type: object | |
* properties: | |
* message: | |
* type: string | |
* app: | |
* type: object | |
* description: Details of the created sales channel | |
* 400: | |
* description: Bad request | |
* 500: | |
* description: Internal server error | |
*/ | |
app.post("/company/:companyId/sales_channel", async (req, res, next) => { | |
try { | |
const { companyId } = req.params; | |
const { brand_ids, name, subdomain } = req.body; | |
const client = await initClient(companyId); | |
let app = await client.configuration.createApplication({ | |
body: { | |
"app": { | |
"company_id": (1).toString(), | |
"channel_type": "website-and-mobile-apps", | |
"auth": { | |
"enabled": true | |
}, | |
"name": name, | |
"desc": "", | |
"mode": "live" | |
}, | |
"configuration": { | |
"inventory": { | |
"brand": { | |
"criteria": "all", | |
"brands": [] | |
}, | |
"store": { | |
"criteria": "filter", | |
"rules": [ | |
{ | |
"companies": [companyId], | |
"brands": brand_ids | |
} | |
], | |
"stores": [] | |
}, | |
"image": ["standard", "substandard", "default"], | |
"franchise_enabled": false, | |
"out_of_stock": true | |
}, | |
"payment": { | |
"mode_of_payment": "ECOMM", | |
"source": "ECOMM" | |
}, | |
"article_assignment": { | |
"post_order_reassignment": true, | |
"enforced_stores": [], | |
"rules": { | |
"store_priority": { | |
"enabled": false, | |
"storetype_order": [] | |
} | |
} | |
} | |
}, | |
"domain": { | |
"name": `${subdomain}.hostx5.de` | |
} | |
} | |
}) | |
res.json({ message: "inv created", app }); | |
} catch (e) { next(e) } | |
}) | |
const chatHtml = fs.readFileSync('./chat.html', 'utf-8'); | |
app.get('/', async (req, res, next) => { | |
try { | |
res.send(chatHtml); | |
} catch (error) { | |
console.error(error); | |
res.send('Something needs to be fixed!'); | |
} | |
}); | |
app.get('/talk', async (req, res, next) => { | |
try { | |
res.send(chatHtml); | |
} catch (error) { | |
console.error(error); | |
res.send('Something needs to be fixed!'); | |
} | |
}); | |
app.get('/history', setJioCopilotSessionCookie, async (req, res) => { | |
const session = req.jio_copilot_anonymous_session; | |
let messages = []; | |
if (session) { | |
messages = await listCache.getAll(session); | |
} | |
messages = messages.filter(message => message.role === 'user' || message.role === 'assistant'); | |
res.json([{ role: 'assistant', content: 'Greetings! I\'m Jio Copilot, your digital aid for Jio platforms like TiraBeauty, JioCinema, JioMart, and JioFiber. Here to help with shopping, product exploration, content streaming, and internet/data plan navigation. How may I assist you today?' }, ...messages]); | |
}); | |
app.delete('/history', setJioCopilotSessionCookie, async (req, res) => { | |
const session = req.jio_copilot_anonymous_session; | |
if (session) { | |
await listCache.clear(session); | |
} | |
res.json({ success: true }); | |
}); | |
router.post( | |
'/openai', setJioCopilotSessionCookie, | |
async (req, res, next) => { | |
const functions = [ | |
{ | |
"name": "getCompanyCreds", | |
"description": "Retrieves credentials for a specific company based on its ID.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
} | |
}, | |
"required": ["company_id"] | |
} | |
}, { | |
"name": "createUpdateCompanyCreds", | |
"description": "Creates or updates credentials for a company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"clientId": { | |
"type": "string", | |
"description": "Client ID for authentication." | |
}, | |
"clientSecret": { | |
"type": "string", | |
"description": "Client secret for authentication." | |
} | |
}, | |
"required": ["company_id", "clientId", "clientSecret"] | |
} | |
}, { | |
"name": "getApplications", | |
"description": "Retrieves a list of applications associated with a specific company ID.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
} | |
}, | |
"required": ["company_id"] | |
} | |
}, { | |
"name": "createBrand", | |
"description": "Creates a new brand under a specific company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"name": { | |
"type": "string", | |
"description": "Name of the new brand." | |
}, | |
"description": { | |
"type": "string", | |
"description": "Description of the new brand." | |
}, | |
"logo": { | |
"type": "string", | |
"description": "URL of the brand's logo." | |
} | |
}, | |
"required": ["company_id", "name", "description"] | |
} | |
}, { | |
"name": "updateBrand", | |
"description": "Updates the details of an existing brand within a company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"brand_id": { | |
"type": "string", | |
"description": "Unique identifier of the brand to be updated." | |
}, | |
"name": { | |
"type": "string", | |
"description": "New name for the brand." | |
}, | |
"description": { | |
"type": "string", | |
"description": "New description for the brand." | |
}, | |
"logo": { | |
"type": "string", | |
"description": "New URL for the brand's logo." | |
}, | |
}, | |
"required": ["company_id", "brand_id", "name", "description"] | |
} | |
}, { | |
"name": "getBrands", | |
"description": "Retrieves a list of all brands associated with a specific company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company for which brands are being retrieved." | |
} | |
}, | |
"required": ["company_id"] | |
} | |
}, { | |
"name": "getLocations", | |
"description": "Retrieves a list of all locations associated with a specific company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company for which locations are being retrieved." | |
} | |
}, | |
"required": ["company_id"] | |
} | |
}, { | |
"name": "createLocation", | |
"description": "Creates a new location for a specified company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"name": { | |
"type": "string", | |
"description": "name of the location." | |
}, | |
"code": { | |
"type": "string", | |
"description": "a unique code name." | |
}, | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"address1": { | |
"type": "string", | |
"description": "Primary address line of the new location." | |
}, | |
"address2": { | |
"type": "string", | |
"description": "Secondary address line of the new location." | |
}, | |
"pincode": { | |
"type": "number", | |
"description": "Postal code of the new location." | |
}, | |
"state": { | |
"type": "string", | |
"description": "State where the new location is situated." | |
}, | |
"country": { | |
"type": "string", | |
"description": "State where the new location is situated." | |
}, | |
"city": { | |
"type": "string", | |
"description": "City where the new location is situated." | |
}, | |
"number": { | |
"type": "string", | |
"description": "Contact number for the new location. 10 digits" | |
}, | |
"country_code": { | |
"type": "string", | |
"description": "Country code for the contact number eg 91" | |
}, | |
"gst_name": { | |
"type": "string", | |
"description": "GST registered name associated with the new location." | |
}, | |
"gst_no": { | |
"type": "string", | |
"description": "GST number associated with the new location." | |
}, | |
"latitude": { | |
"type": "number", | |
"description": "Latitude coordinate for the new location." | |
}, | |
"longitude": { | |
"type": "number", | |
"description": "Longitude coordinate for the new location." | |
}, | |
"manager_name": { | |
"type": "string", | |
"description": "name of the store manager." | |
}, | |
"email": { | |
"type": "string", | |
"description": "email of the store manager." | |
} | |
}, | |
"required": ["company_id", "address1", "city", "state", 'country', "pincode", "gst_name", "gst_no", 'manager_name', 'email', 'code', 'country_code', 'number'] | |
} | |
}, { | |
"name": "updateLocation", | |
"description": "update a existing location for a specified company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"location_id": { | |
"type": "number", | |
"description": "Unique identifier of the location." | |
}, | |
"address1": { | |
"type": "string", | |
"description": "Primary address line of the new location." | |
}, | |
"address2": { | |
"type": "string", | |
"description": "Secondary address line of the new location." | |
}, | |
"pincode": { | |
"type": "string", | |
"description": "Postal code of the new location." | |
}, | |
"state": { | |
"type": "string", | |
"description": "State where the new location is situated." | |
}, | |
"city": { | |
"type": "string", | |
"description": "City where the new location is situated." | |
}, | |
"number": { | |
"type": "string", | |
"description": "Contact number for the new location." | |
}, | |
"country_code": { | |
"type": "string", | |
"description": "Country code for the contact number." | |
}, | |
"gst_name": { | |
"type": "string", | |
"description": "GST registered name associated with the new location." | |
}, | |
"gst_no": { | |
"type": "string", | |
"description": "GST number associated with the new location." | |
}, | |
"latitude": { | |
"type": "number", | |
"description": "Latitude coordinate for the new location." | |
}, | |
"longitude": { | |
"type": "number", | |
"description": "Longitude coordinate for the new location." | |
} | |
}, | |
"required": ["company_id", 'brand_id', "address1", "city", "state", "pincode", "country_code", "gst_name", "gst_no"] | |
} | |
}, { | |
"name": "createProduct", | |
"description": "Creates a new product in the company's catalog.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"name": { | |
"type": "string", | |
"description": "Name of the new product." | |
}, | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"slug": { | |
"type": "string", | |
"description": "SEO-friendly URL segment for the product." | |
}, | |
"seller_identifier": { | |
"type": "string", | |
"description": "Unique identifier for the seller of the product." | |
}, | |
"brand_id": { | |
"type": "number", | |
"description": "Unique identifier of the brand associated with the product." | |
}, | |
"mrp": { | |
"type": "number", | |
"description": "Maximum Retail Price of the product." | |
}, | |
"selling_price": { | |
"type": "number", | |
"description": "Selling price of the product." | |
} | |
}, | |
"required": ["name", "company_id", "slug", "seller_identifier", "brand_id"] | |
} | |
}, { | |
"name": "createInventory", | |
"description": "Creates a new inventory record for a specific product in a company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"product_id": { | |
"type": "number", | |
"description": "Unique identifier of the product." | |
}, | |
"seller_identifier": { | |
"type": "string", | |
"description": "Unique identifier for the seller of the product." | |
}, | |
"location_id": { | |
"type": "number", | |
"description": "Unique identifier of the location where the inventory is stored." | |
}, | |
"mrp": { | |
"type": "number", | |
"description": "Maximum Retail Price of the product." | |
}, | |
"selling_price": { | |
"type": "number", | |
"description": "Selling price of the product." | |
} | |
}, | |
"required": ["company_id", "product_id", "seller_identifier", "location_id"] | |
} | |
}, { | |
"name": "createApplication", | |
"description": "Creates a new application for a specified company.", | |
"parameters": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "number", | |
"description": "Unique identifier of the company." | |
}, | |
"brand_ids": { | |
"type": "array", | |
"description": "Array of brand IDs associated with the application.", | |
"items": { | |
"type": "number" | |
} | |
}, | |
"name": { | |
"type": "string", | |
"description": "Name of the new application." | |
}, | |
"subdomain": { | |
"type": "string", | |
"description": "Subdomain for the application's URL." | |
} | |
}, | |
"required": ["company_id", "brand_ids", "name", "subdomain"] | |
} | |
} | |
]; | |
const messages = [{ | |
role: 'system', | |
content: `you are an onboarding buddy chatbot for fynd platform, you help logged in userd who have access to a company to clear their salechannels/ websites, always start by asking the same. when asking questions from the use ask one at a time, ask user for one input at a time. You must initiate the conversation always. | |
NOTEs FOR YOU: | |
always check if you have the credentials for a company_id | |
you always show the function name and the generated JSON args when functions are called | |
platform domain is platfrom.fyndx5.de | |
to get started yo need to get the companyId, clientId and secret from the user. always check if the credentials exist, if not save them | |
to create a sales channel you require at least one brand. fetch and show the brands | |
to create a product you need a brand. fetch and show the brand | |
once the product is created it takes some time reflect in the db before the inventory is added | |
for a product to be visible on your sales channel you must also add inventory for it | |
stores, location, warehouses mean the same, | |
seller_identifier, item_code are unique indentifier of the product, both may be the same | |
assume seller_identifier is a string separated by _ and in all caps always. | |
assume data if possible | |
UI Links: always give the ui link when performing actions | |
company credentials create link = https://platform.fyndx5.de/company/{companyId}/apis/oauthclient/create | |
company homepage url = https://platform.fyndx5.de/company/{companyId}/home | |
company profile url = https://platform.fyndx5.de/company/{companyId}/profile | |
company location url = https://platform.fyndx5.de/company/{companyId}/profile/edit-store/{locationId} | |
company brand url = https://platform.fyndx5.de/company/{companyId}/profile/edit-brand/{brandId} | |
company sales channels url = https://platform.fyndx5.de/company/{companyId}/application/{applicationId}/products | |
company product listing url= https://platform.fyndx5.de/company/{companyId}/products/list | |
company product url = https://platform.fyndx5.de/company/{companyId}/products/{itemId}/edit.` | |
}]; | |
const session = req.jio_copilot_anonymous_session; | |
if (session) { | |
messages.push(...(await listCache.getAll(session))); | |
} | |
const userMessage = { role: 'user', content: req.body.prompt }; | |
messages.push(userMessage); | |
await listCache.setItems(session, userMessage); | |
res.setHeader('Cache-Control', 'no-cache'); | |
res.setHeader('Content-Type', 'text/event-stream'); | |
// res.setHeader('Access-Control-Allow-Origin', http://); | |
res.setHeader('Connection', 'keep-alive'); | |
res.flushHeaders(); | |
try { | |
await nextStep(session, messages, skills, functions, res); | |
res.end(); | |
} catch (error) { | |
if (error.code === 'context_length_exceeded') { | |
res.write('You have exhausted the conversation limit, clear history and start again!'); | |
res.end(); | |
} | |
res.write('There is some issue is going on. Please try again later.'); | |
res.end(); | |
} | |
}); | |
app.use("/api", router); | |
app.use(errorHandler) | |
app.listen(port, async () => { | |
console.log(`Example app listening at http://localhost:${port}`); | |
const swaggerSpec = swaggerJSDoc(options); | |
// Convert to YAML | |
const swaggerYAML = yaml.dump(swaggerSpec); | |
await fs.writeFileSync("swagger.yaml", swaggerYAML, 'utf-8') | |
await fs.writeFileSync("swagger.json", JSON.stringify(swaggerSpec, null, 2), 'utf-8') | |
}); | |
async function nextStep(session, messages, skills, functions, res) { | |
const conn = new OpenAI({ apiKey: "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" }); | |
const streamResponse = await conn | |
.chat.completions.create({ | |
model: 'gpt-4', | |
messages, | |
functions, | |
function_call: 'auto', | |
temperature: 0.5, | |
stream: true | |
}).catch(async err => { | |
console.error(err); | |
const openAIError = new Error(err.error.message); | |
openAIError.code = err.error.code; | |
throw openAIError; | |
}); | |
let finalMessage = ''; | |
const functionCall = { name: null, arguments: '' }; | |
for await (const line of streamResponse) { | |
const choice = line.choices[0]; | |
if (choice.delta.function_call) { | |
const fc = choice.delta.function_call; | |
if (fc.name) { | |
// res.write(`Detected Function: ${fc.name}\n\n`); | |
// res.flush(); | |
functionCall.name = fc.name; | |
} | |
if (fc.arguments) { | |
functionCall.arguments += fc.arguments; | |
} | |
} else if (!choice.finish_reason && choice.delta.content) { | |
finalMessage += choice.delta.content; | |
res.write(`${choice.delta.content}`); | |
res.flush(); | |
} | |
} | |
if (!functionCall.name) { | |
const assistantMessage = { role: 'assistant', content: finalMessage }; | |
messages.push(assistantMessage); | |
await listCache.setItems(session, assistantMessage); | |
return; | |
} | |
const functionName = functionCall.name; | |
const args = JSON.parse(functionCall.arguments); | |
if (!skills[functionName]) { | |
throw new Error(`No skill implemented for this function: ${functionName}`); | |
} | |
// res.write(`Executing Function: ${functionName} with arguments ${JSON.stringify(args)} \n\n`); | |
// res.flush(); | |
const skillRes = await skills[functionName]({ ...args, headers: { 'openai-ephemeral-user-id': session } }).catch(e => { | |
return { message: e } | |
}); | |
if (skillRes) { | |
const functionMessage = { role: 'function', name: functionName, content: JSON.stringify(skillRes) }; | |
messages.push(functionMessage); | |
await listCache.setItems(session, functionMessage); | |
} | |
return nextStep(session, messages, skills, functions, res); | |
} | |
export const handler = serverless(api); | |