Fix API authentication and identity service updates
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
$content = Get-Content -Raw "c:\Users\Lenovo\Desktop\ai_pharma_frontend\src\services\api.ts"
|
||||
$target = "}, (error) => {`r`n return Promise.reject(error);`r`n});"
|
||||
$replacement = @"
|
||||
}, (error) => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// Response interceptor to handle global errors like 401 Unauthorized
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
// Token is invalid or expired
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('user');
|
||||
localStorage.removeItem('actor_id');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
"@
|
||||
|
||||
$content = $content.Replace($target, $replacement)
|
||||
Set-Content -Path "c:\Users\Lenovo\Desktop\ai_pharma_frontend\src\services\api.ts" -Value $content -NoNewline
|
||||
@@ -0,0 +1,176 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const collection = {
|
||||
info: {
|
||||
name: "Pharmacy Core API",
|
||||
description: "API collection for Pharmacy Core API",
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
variable: [
|
||||
{
|
||||
key: "baseUrl",
|
||||
value: "http://localhost:8080",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
key: "token",
|
||||
value: "",
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
auth: {
|
||||
type: "bearer",
|
||||
bearer: [
|
||||
{
|
||||
key: "token",
|
||||
value: "{{token}}",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
item: []
|
||||
};
|
||||
|
||||
function createFolder(name, endpoints, prefix) {
|
||||
const items = endpoints.map(ep => {
|
||||
const method = ep.method.toUpperCase();
|
||||
const fullPath = (prefix + ep.path).replace(/\/$/, "");
|
||||
const urlParts = fullPath.split('/').filter(p => p !== '');
|
||||
|
||||
const request = {
|
||||
method: method,
|
||||
header: [],
|
||||
url: {
|
||||
raw: `{{baseUrl}}${fullPath}`,
|
||||
host: ["{{baseUrl}}"],
|
||||
path: urlParts
|
||||
}
|
||||
};
|
||||
|
||||
if (['POST', 'PUT', 'PATCH'].includes(method)) {
|
||||
request.body = {
|
||||
mode: "raw",
|
||||
raw: "{}",
|
||||
options: {
|
||||
raw: {
|
||||
language: "json"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: ep.name || `${method} ${ep.path}`,
|
||||
request: request
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
name: name,
|
||||
item: items
|
||||
};
|
||||
}
|
||||
|
||||
const modules = [
|
||||
{
|
||||
name: "Auth / Identity",
|
||||
prefix: "/v1/auth",
|
||||
endpoints: [
|
||||
{ method: "GET", path: "/me", name: "Get Current User" },
|
||||
{ method: "PUT", path: "/me", name: "Update Current User" },
|
||||
{ method: "POST", path: "/register", name: "Register" },
|
||||
{ method: "POST", path: "/login", name: "Login" },
|
||||
{ method: "POST", path: "/logout", name: "Logout" },
|
||||
{ method: "POST", path: "/token", name: "Refresh Token" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Catalogue",
|
||||
prefix: "/v1",
|
||||
endpoints: [
|
||||
{ method: "GET", path: "/drugs", name: "Get Drugs" },
|
||||
{ method: "POST", path: "/drugs", name: "Create Drug" },
|
||||
{ method: "GET", path: "/drugs/search", name: "Search Drugs" },
|
||||
{ method: "GET", path: "/products/:product_id/batches", name: "Get Product Batches" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Inventory",
|
||||
prefix: "/v1/inventory",
|
||||
endpoints: [
|
||||
{ method: "POST", path: "/grn", name: "Create GRN" },
|
||||
{ method: "GET", path: "/expiring", name: "Get Expiring Stock" },
|
||||
{ method: "GET", path: "/low-stock", name: "Get Low Stock" },
|
||||
{ method: "POST", path: "/adjustments", name: "Stock Adjustment" },
|
||||
{ method: "GET", path: "/recall/:batch_no", name: "Recall Batch" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Dispensing (Orders)",
|
||||
prefix: "/v1/orders",
|
||||
endpoints: [
|
||||
{ method: "GET", path: "/history", name: "Get Order History" },
|
||||
{ method: "GET", path: "/h1-register", name: "Get H1 Register" },
|
||||
{ method: "GET", path: "/:id/invoice/pdf", name: "Get Invoice PDF" },
|
||||
{ method: "POST", path: "/", name: "Create Order" },
|
||||
{ method: "PATCH", path: "/:id/items", name: "Update Order Items" },
|
||||
{ method: "POST", path: "/:id/precheck", name: "Precheck Order" },
|
||||
{ method: "POST", path: "/:id/dispense", name: "Dispense Order" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Audit",
|
||||
prefix: "/v1/audit",
|
||||
endpoints: [
|
||||
{ method: "GET", path: "/ai-calls", name: "Get AI Calls" },
|
||||
{ method: "GET", path: "/overrides", name: "Get Overrides" },
|
||||
{ method: "GET", path: "/h1-register", name: "Get H1 Register Audit" },
|
||||
{ method: "GET", path: "/sales-register", name: "Get Sales Register" },
|
||||
{ method: "GET", path: "/dashboard-metrics", name: "Get Dashboard Metrics" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Prescription",
|
||||
prefix: "/v1/rx",
|
||||
endpoints: [
|
||||
{ method: "POST", path: "/", name: "Upload Prescription" },
|
||||
{ method: "GET", path: "/", name: "Get Prescriptions" },
|
||||
{ method: "GET", path: "/:id", name: "Get Prescription by ID" },
|
||||
{ method: "POST", path: "/:id/confirm", name: "Confirm Prescription" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "AI",
|
||||
prefix: "/v1/ai",
|
||||
endpoints: [
|
||||
{ method: "POST", path: "/voice", name: "Voice AI" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Integration",
|
||||
prefix: "/v1/integration",
|
||||
endpoints: [
|
||||
{ method: "POST", path: "/hospital/patient-sync", name: "Sync Hospital Patient" },
|
||||
{ method: "GET", path: "/hospital/patients", name: "Get Hospital Patients" },
|
||||
{ method: "POST", path: "/telegaruda/prescription", name: "Receive Telegaruda Rx" },
|
||||
{ method: "GET", path: "/telegaruda/patient/:patientId", name: "Fetch Telegaruda Patient" },
|
||||
{ method: "GET", path: "/hospital/prescriptions", name: "Get Hospital Prescriptions" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Upload",
|
||||
prefix: "/v1/upload",
|
||||
endpoints: [
|
||||
{ method: "POST", path: "/", name: "Upload File" },
|
||||
{ method: "GET", path: "/presigned", name: "Get Presigned URL" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
modules.forEach(mod => {
|
||||
collection.item.push(createFolder(mod.name, mod.endpoints, mod.prefix));
|
||||
});
|
||||
|
||||
// Write to file
|
||||
fs.writeFileSync('postman_collection.json', JSON.stringify(collection, null, 2));
|
||||
console.log('Collection generated at postman_collection.json');
|
||||
@@ -0,0 +1,948 @@
|
||||
{
|
||||
"info": {
|
||||
"name": "Pharmacy Core API",
|
||||
"description": "API collection for Pharmacy Core API",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"variable": [
|
||||
{
|
||||
"key": "baseUrl",
|
||||
"value": "http://localhost:8080",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "token",
|
||||
"value": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{token}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Auth / Identity",
|
||||
"item": [
|
||||
{
|
||||
"name": "Get Current User",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/auth/me",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"auth",
|
||||
"me"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Update Current User",
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/auth/me",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"auth",
|
||||
"me"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Register",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/auth/register",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"auth",
|
||||
"register"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Login",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/auth/login",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"auth",
|
||||
"login"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Logout",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/auth/logout",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"auth",
|
||||
"logout"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Refresh Token",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/auth/token",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"auth",
|
||||
"token"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Catalogue",
|
||||
"item": [
|
||||
{
|
||||
"name": "Get Drugs",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/drugs",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"drugs"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create Drug",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/drugs",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"drugs"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Search Drugs",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/drugs/search",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"drugs",
|
||||
"search"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Product Batches",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/products/:product_id/batches",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"products",
|
||||
":product_id",
|
||||
"batches"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Inventory",
|
||||
"item": [
|
||||
{
|
||||
"name": "Create GRN",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/inventory/grn",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"inventory",
|
||||
"grn"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Expiring Stock",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/inventory/expiring",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"inventory",
|
||||
"expiring"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Low Stock",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/inventory/low-stock",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"inventory",
|
||||
"low-stock"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Stock Adjustment",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/inventory/adjustments",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"inventory",
|
||||
"adjustments"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Recall Batch",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/inventory/recall/:batch_no",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"inventory",
|
||||
"recall",
|
||||
":batch_no"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Dispensing (Orders)",
|
||||
"item": [
|
||||
{
|
||||
"name": "Get Order History",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/orders/history",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"orders",
|
||||
"history"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get H1 Register",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/orders/h1-register",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"orders",
|
||||
"h1-register"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Invoice PDF",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/orders/:id/invoice/pdf",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"orders",
|
||||
":id",
|
||||
"invoice",
|
||||
"pdf"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create Order",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/orders",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"orders"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Update Order Items",
|
||||
"request": {
|
||||
"method": "PATCH",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/orders/:id/items",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"orders",
|
||||
":id",
|
||||
"items"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Precheck Order",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/orders/:id/precheck",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"orders",
|
||||
":id",
|
||||
"precheck"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Dispense Order",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/orders/:id/dispense",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"orders",
|
||||
":id",
|
||||
"dispense"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Audit",
|
||||
"item": [
|
||||
{
|
||||
"name": "Get AI Calls",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/audit/ai-calls",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"audit",
|
||||
"ai-calls"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Overrides",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/audit/overrides",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"audit",
|
||||
"overrides"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get H1 Register Audit",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/audit/h1-register",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"audit",
|
||||
"h1-register"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Sales Register",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/audit/sales-register",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"audit",
|
||||
"sales-register"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Dashboard Metrics",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/audit/dashboard-metrics",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"audit",
|
||||
"dashboard-metrics"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Prescription",
|
||||
"item": [
|
||||
{
|
||||
"name": "Upload Prescription",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/rx",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"rx"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Prescriptions",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/rx",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"rx"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Prescription by ID",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/rx/:id",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"rx",
|
||||
":id"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Confirm Prescription",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/rx/:id/confirm",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"rx",
|
||||
":id",
|
||||
"confirm"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AI",
|
||||
"item": [
|
||||
{
|
||||
"name": "Voice AI",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/ai/voice",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"ai",
|
||||
"voice"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Integration",
|
||||
"item": [
|
||||
{
|
||||
"name": "Sync Hospital Patient",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/integration/hospital/patient-sync",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"integration",
|
||||
"hospital",
|
||||
"patient-sync"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Hospital Patients",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/integration/hospital/patients",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"integration",
|
||||
"hospital",
|
||||
"patients"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Receive Telegaruda Rx",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/integration/telegaruda/prescription",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"integration",
|
||||
"telegaruda",
|
||||
"prescription"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Fetch Telegaruda Patient",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/integration/telegaruda/patient/:patientId",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"integration",
|
||||
"telegaruda",
|
||||
"patient",
|
||||
":patientId"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Hospital Prescriptions",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/integration/hospital/prescriptions",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"integration",
|
||||
"hospital",
|
||||
"prescriptions"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Upload",
|
||||
"item": [
|
||||
{
|
||||
"name": "Upload File",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/upload",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"upload"
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Presigned URL",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/v1/upload/presigned",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"v1",
|
||||
"upload",
|
||||
"presigned"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import express from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import { errorHandler } from './middleware/error';
|
||||
import { requireApiKey } from './middleware/auth';
|
||||
|
||||
// Import routers
|
||||
import identityRouter from './modules/identity/router';
|
||||
@@ -39,6 +40,9 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
// Global API Key Check
|
||||
app.use(requireApiKey);
|
||||
|
||||
// API Routes
|
||||
app.use('/v1/auth', identityRouter);
|
||||
app.use('/v1', catalogueRouter);
|
||||
@@ -61,3 +65,4 @@ app.use(errorHandler);
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Pharmacy Core API listening on port ${PORT}`);
|
||||
});
|
||||
|
||||
|
||||
+23
-2
@@ -1,7 +1,8 @@
|
||||
import { Request, Response, NextFunction, RequestHandler } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026';
|
||||
// JWT_SECRET will be evaluated at runtime to avoid loading order issues
|
||||
const API_KEY = process.env.API_KEY;
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
@@ -12,7 +13,26 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export const requireApiKey: RequestHandler = (req: Request, res: Response, next: NextFunction): void => {
|
||||
const apiKey = req.headers['x-api-key'];
|
||||
const expectedApiKey = process.env.API_KEY;
|
||||
|
||||
// If API_KEY is set in .env, enforce it
|
||||
if (expectedApiKey && apiKey !== expectedApiKey) {
|
||||
res.status(401).json({
|
||||
error: 'UNAUTHORIZED',
|
||||
message: 'Missing or invalid API Key.',
|
||||
details: {},
|
||||
retryable: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
export const requireAuth: RequestHandler = (req: Request, res: Response, next: NextFunction): void => {
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
const actorId = req.headers['x-actor-id'] as string;
|
||||
|
||||
@@ -29,8 +49,9 @@ export const requireAuth: RequestHandler = (req: Request, res: Response, next: N
|
||||
const token = authHeader.split(' ')[1];
|
||||
|
||||
try {
|
||||
const secret = process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026';
|
||||
// Cryptographically verify the token using our secret key
|
||||
const payload = jwt.verify(token, JWT_SECRET) as any;
|
||||
const payload = jwt.verify(token, secret) as any;
|
||||
|
||||
if (payload.userId) {
|
||||
// User JWT Token
|
||||
|
||||
@@ -9,7 +9,7 @@ const router = Router();
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
const s3Service = new S3Service();
|
||||
const identityService = new IdentityService();
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026';
|
||||
// JWT_SECRET evaluated at runtime
|
||||
|
||||
// GET /v1/auth/me (Get Profile)
|
||||
router.get('/me', requireAuth, async (req, res) => {
|
||||
@@ -123,7 +123,7 @@ router.post('/token', (req, res) => {
|
||||
tenantId: client_id,
|
||||
scopes: ['rx:read', 'rx:write', 'dispense:human', 'catalogue:read']
|
||||
},
|
||||
JWT_SECRET,
|
||||
process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026',
|
||||
{ expiresIn: '8h' }
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { pool } from '../../db/pool';
|
||||
import bcrypt from 'bcrypt';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026';
|
||||
// JWT_SECRET evaluated at runtime
|
||||
|
||||
export class IdentityService {
|
||||
async registerUser(payload: {
|
||||
@@ -63,7 +63,7 @@ export class IdentityService {
|
||||
role: user.role,
|
||||
tenant_id: user.tenant_id
|
||||
},
|
||||
JWT_SECRET,
|
||||
process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026',
|
||||
{ expiresIn: '12h' } // Usually 8-12 hours for a shift
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import os
|
||||
|
||||
filepath = r"c:\Users\Lenovo\Desktop\ai_pharma_frontend\src\services\api.ts"
|
||||
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Replacement 1: Add API_KEY to axios.create
|
||||
old_axios_create = """export const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});"""
|
||||
|
||||
new_axios_create = """export const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': import.meta.env.VITE_API_KEY || '',
|
||||
},
|
||||
});"""
|
||||
|
||||
content = content.replace(old_axios_create, new_axios_create)
|
||||
|
||||
# Replacement 2: update authenticate
|
||||
old_auth = """ const response = await axios.post(`${API_BASE_URL}/auth/token`, {
|
||||
grant_type: 'client_credentials',
|
||||
client_id: 'jayam_web_ai',
|
||||
client_secret: 'my_secret_key'
|
||||
});"""
|
||||
|
||||
new_auth = """ const response = await axios.post(`${API_BASE_URL}/auth/token`, {
|
||||
grant_type: 'client_credentials',
|
||||
client_id: 'jayam_web_ai',
|
||||
client_secret: 'my_secret_key'
|
||||
}, {
|
||||
headers: { 'x-api-key': import.meta.env.VITE_API_KEY || '' }
|
||||
});"""
|
||||
|
||||
content = content.replace(old_auth, new_auth)
|
||||
|
||||
# Replacement 3: update loginPharmacist
|
||||
old_login = """ const response = await axios.post(`${API_BASE_URL}/auth/login`, credentials);"""
|
||||
|
||||
new_login = """ const response = await axios.post(`${API_BASE_URL}/auth/login`, credentials, {
|
||||
headers: { 'x-api-key': import.meta.env.VITE_API_KEY || '' }
|
||||
});"""
|
||||
|
||||
content = content.replace(old_login, new_login)
|
||||
|
||||
# Replacement 4: update logoutPharmacist
|
||||
old_logout = """ const response = await axios.post(`${API_BASE_URL}/auth/logout`);"""
|
||||
new_logout = """ const response = await axios.post(`${API_BASE_URL}/auth/logout`, {}, {
|
||||
headers: { 'x-api-key': import.meta.env.VITE_API_KEY || '' }
|
||||
});"""
|
||||
|
||||
content = content.replace(old_logout, new_logout)
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
print("Updated api.ts")
|
||||
Reference in New Issue
Block a user