@anton.andrusenko/shopify-mcp-admin 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -52
- package/package.json +1 -1
- package/dist/chunk-3Y4P67GZ.js +0 -124
- package/dist/chunk-6YECVENJ.js +0 -208
- package/dist/chunk-GKGHMPEC.js +0 -333
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -18111
- package/dist/mcp-auth-UJ6MADL4.js +0 -15
- package/dist/setup-wizard-PVLOC3DU.js +0 -697
- package/dist/store-MQK3GUUB.js +0 -9
package/README.md
CHANGED
|
@@ -129,6 +129,13 @@ Tokens are automatically refreshed every 24 hours.
|
|
|
129
129
|
| `LOG_LEVEL` | No | `info` | Log level: `debug`, `info`, `warn`, `error` |
|
|
130
130
|
| `SHOPIFY_MCP_LAZY_LOADING` | No | `false` | Enable modular lazy loading (set to `true` for on-demand module loading) |
|
|
131
131
|
| `SHOPIFY_MCP_ROLE` | No | — | Role preset for automatic module loading (see [Role Presets](#-role-presets)) |
|
|
132
|
+
| `ALLOWED_HOSTS` | ✅ (remote mode) | — | Comma-separated list of allowed hostnames (e.g., `shopify-mcp.com,api.shopify-mcp.com`) |
|
|
133
|
+
| `ALLOWED_ORIGINS` | ✅ (remote mode) | — | Comma-separated list of allowed CORS origins (e.g., `https://shopify-mcp.com`) |
|
|
134
|
+
| `METRICS_ENDPOINT_ENABLED` | No | `false` | Enable Prometheus metrics endpoint at `/metrics` |
|
|
135
|
+
| `ENABLE_HSTS` | No | `false` | Enable HTTP Strict Transport Security headers |
|
|
136
|
+
| `SHUTDOWN_DRAIN_SECONDS` | No | `30` | Graceful shutdown drain timeout in seconds (1-300) |
|
|
137
|
+
| `LOG_FORMAT` | No | `json` | Log format: `json` (production) or `pretty` (development) |
|
|
138
|
+
| `SENTRY_DSN` | No | — | Sentry error tracking DSN (recommended for production) |
|
|
132
139
|
|
|
133
140
|
### Server Modes
|
|
134
141
|
|
|
@@ -987,9 +994,9 @@ shopify-mcp-admin uses a modular architecture to optimize AI agent performance.
|
|
|
987
994
|
|
|
988
995
|
## 🔄 Upgrading from Previous Versions
|
|
989
996
|
|
|
990
|
-
### What's New in
|
|
997
|
+
### What's New in v2.0.0+: Modular Tool Loading
|
|
991
998
|
|
|
992
|
-
Starting with
|
|
999
|
+
Starting with v2.0.0, shopify-mcp-admin supports **modular lazy loading** to optimize AI agent performance. Research shows that presenting more than 30 tools to an AI agent can degrade performance by up to 85%.
|
|
993
1000
|
|
|
994
1001
|
**Default Behavior (All Tools Loaded):**
|
|
995
1002
|
- All 79 domain tools are loaded at startup for maximum compatibility
|
|
@@ -1227,56 +1234,6 @@ Enable these settings for the `main` branch in **Settings → Branches**:
|
|
|
1227
1234
|
|
|
1228
1235
|
---
|
|
1229
1236
|
|
|
1230
|
-
## 🚀 Deployment
|
|
1231
|
-
|
|
1232
|
-
shopify-mcp-admin supports multiple deployment options for different environments:
|
|
1233
|
-
|
|
1234
|
-
### Railway (Recommended for Quick Start)
|
|
1235
|
-
|
|
1236
|
-
Deploy to [Railway](https://railway.app) with the included `railway.json` configuration:
|
|
1237
|
-
|
|
1238
|
-
1. Connect your GitHub repository to Railway
|
|
1239
|
-
2. Add environment variables (DATABASE_URL, ENCRYPTION_KEY, etc.)
|
|
1240
|
-
3. Deploy — Railway auto-detects the Dockerfile
|
|
1241
|
-
|
|
1242
|
-
### Kubernetes
|
|
1243
|
-
|
|
1244
|
-
For self-hosted or cloud Kubernetes clusters, see the [Kubernetes Deployment Guide](docs/kubernetes.md).
|
|
1245
|
-
|
|
1246
|
-
Quick start:
|
|
1247
|
-
```bash
|
|
1248
|
-
# Build and push image
|
|
1249
|
-
docker build -t yourregistry/shopify-mcp-admin:v1.0.0 .
|
|
1250
|
-
docker push yourregistry/shopify-mcp-admin:v1.0.0
|
|
1251
|
-
|
|
1252
|
-
# Configure and deploy
|
|
1253
|
-
cd k8s
|
|
1254
|
-
kustomize edit set image shopify-mcp-admin=yourregistry/shopify-mcp-admin:v1.0.0
|
|
1255
|
-
kubectl apply -k .
|
|
1256
|
-
```
|
|
1257
|
-
|
|
1258
|
-
### Docker (Local/Development)
|
|
1259
|
-
|
|
1260
|
-
```bash
|
|
1261
|
-
# Build image
|
|
1262
|
-
docker build -t shopify-mcp-admin .
|
|
1263
|
-
|
|
1264
|
-
# Run container
|
|
1265
|
-
docker run -p 3000:3000 \
|
|
1266
|
-
-e DATABASE_URL="postgresql://..." \
|
|
1267
|
-
-e ENCRYPTION_KEY="..." \
|
|
1268
|
-
shopify-mcp-admin
|
|
1269
|
-
```
|
|
1270
|
-
|
|
1271
|
-
### Image Size
|
|
1272
|
-
|
|
1273
|
-
The production Docker image is optimized for minimal size:
|
|
1274
|
-
- Multi-stage build with Alpine Linux
|
|
1275
|
-
- Production dependencies only (`npm prune --production`)
|
|
1276
|
-
- Target size: <150MB
|
|
1277
|
-
|
|
1278
|
-
---
|
|
1279
|
-
|
|
1280
1237
|
## 🎨 Tenant Dashboard (Remote Mode)
|
|
1281
1238
|
|
|
1282
1239
|
When running in remote mode (`SERVER_MODE=remote`), shopify-mcp-admin includes a web-based dashboard for tenant management. The dashboard provides a modern, responsive UI for:
|
|
@@ -1383,6 +1340,8 @@ This project is licensed under the **MIT License** — see the [LICENSE](LICENSE
|
|
|
1383
1340
|
|
|
1384
1341
|
## 🚀 Deployment
|
|
1385
1342
|
|
|
1343
|
+
shopify-mcp-admin supports multiple deployment options for different environments.
|
|
1344
|
+
|
|
1386
1345
|
### Production Deployment
|
|
1387
1346
|
|
|
1388
1347
|
For production deployment on Railway with Cloudflare, Sentry, and uptime monitoring, see the comprehensive deployment guide:
|
|
@@ -1396,6 +1355,79 @@ The guide includes:
|
|
|
1396
1355
|
- **Staging Environment**: Separate staging deployment configuration
|
|
1397
1356
|
- **Monitoring**: Sentry error tracking and uptime monitoring setup
|
|
1398
1357
|
|
|
1358
|
+
### Production URLs
|
|
1359
|
+
|
|
1360
|
+
The production service is available at:
|
|
1361
|
+
|
|
1362
|
+
**Root Domain:**
|
|
1363
|
+
- **Dashboard:** `https://shopify-mcp.com/` → redirects to `/app`
|
|
1364
|
+
- **Dashboard UI:** `https://shopify-mcp.com/app`
|
|
1365
|
+
- **Health Check:** `https://shopify-mcp.com/health`
|
|
1366
|
+
- **Metrics:** `https://shopify-mcp.com/metrics`
|
|
1367
|
+
- **MCP Endpoint:** `https://shopify-mcp.com/mcp`
|
|
1368
|
+
|
|
1369
|
+
**API Subdomain:**
|
|
1370
|
+
- **Dashboard:** `https://api.shopify-mcp.com/app`
|
|
1371
|
+
- **Health Check:** `https://api.shopify-mcp.com/health`
|
|
1372
|
+
- **Metrics:** `https://api.shopify-mcp.com/metrics`
|
|
1373
|
+
- **MCP Endpoint:** `https://api.shopify-mcp.com/mcp`
|
|
1374
|
+
|
|
1375
|
+
**Available Endpoints:**
|
|
1376
|
+
|
|
1377
|
+
| Endpoint | Description | Authentication |
|
|
1378
|
+
|----------|-------------|----------------|
|
|
1379
|
+
| `/` | Root path - redirects to `/app` (dashboard) | None |
|
|
1380
|
+
| `/app` | Dashboard UI (React SPA) | Session cookie |
|
|
1381
|
+
| `/health` | Health check endpoint | None |
|
|
1382
|
+
| `/metrics` | Prometheus metrics | None |
|
|
1383
|
+
| `/mcp` | MCP protocol endpoint | API key or session |
|
|
1384
|
+
| `/api/tenants` | Tenant management API | Session cookie |
|
|
1385
|
+
| `/api/shops` | Shop connection API | Session cookie |
|
|
1386
|
+
| `/api/keys` | API key management | Session cookie |
|
|
1387
|
+
|
|
1388
|
+
**Note:** The root domain (`shopify-mcp.com`) redirects to the dashboard (`/app`) for a better user experience. All API endpoints are also available on the root domain.
|
|
1389
|
+
|
|
1390
|
+
### Deployment Options
|
|
1391
|
+
|
|
1392
|
+
#### Railway (Recommended for Quick Start)
|
|
1393
|
+
|
|
1394
|
+
Deploy to [Railway](https://railway.app) with the included `railway.json` configuration:
|
|
1395
|
+
|
|
1396
|
+
1. Connect your GitHub repository to Railway
|
|
1397
|
+
2. Add environment variables (DATABASE_URL, ENCRYPTION_KEY, etc.)
|
|
1398
|
+
3. Deploy — Railway auto-detects the Dockerfile
|
|
1399
|
+
|
|
1400
|
+
#### Kubernetes
|
|
1401
|
+
|
|
1402
|
+
For self-hosted or cloud Kubernetes clusters, see the [Kubernetes Deployment Guide](docs/kubernetes.md).
|
|
1403
|
+
|
|
1404
|
+
Quick start:
|
|
1405
|
+
```bash
|
|
1406
|
+
# Build and push image
|
|
1407
|
+
docker build -t yourregistry/shopify-mcp-admin:v2.0.0 .
|
|
1408
|
+
docker push yourregistry/shopify-mcp-admin:v2.0.0
|
|
1409
|
+
|
|
1410
|
+
# Configure and deploy
|
|
1411
|
+
cd k8s
|
|
1412
|
+
kustomize edit set image shopify-mcp-admin=yourregistry/shopify-mcp-admin:v2.0.0
|
|
1413
|
+
kubectl apply -k .
|
|
1414
|
+
```
|
|
1415
|
+
|
|
1416
|
+
#### Docker (Local/Development)
|
|
1417
|
+
|
|
1418
|
+
```bash
|
|
1419
|
+
# Build image
|
|
1420
|
+
docker build -t shopify-mcp-admin .
|
|
1421
|
+
|
|
1422
|
+
# Run container
|
|
1423
|
+
docker run -p 3000:3000 \
|
|
1424
|
+
-e DATABASE_URL="postgresql://..." \
|
|
1425
|
+
-e ENCRYPTION_KEY="..." \
|
|
1426
|
+
shopify-mcp-admin
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
**Image Size:** The production Docker image is optimized for minimal size (<150MB) with multi-stage Alpine Linux build.
|
|
1430
|
+
|
|
1399
1431
|
### Environment Variables
|
|
1400
1432
|
|
|
1401
1433
|
For production environment variables, see:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anton.andrusenko/shopify-mcp-admin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "MCP server for Shopify Admin API - enables AI agents to manage Shopify stores with 79 tools for products, inventory, collections, content, SEO, metafields, markets & translations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/dist/chunk-3Y4P67GZ.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
log
|
|
3
|
-
} from "./chunk-GKGHMPEC.js";
|
|
4
|
-
|
|
5
|
-
// src/middleware/mcp-auth.ts
|
|
6
|
-
var MCP_AUTH_ERROR_CODES = {
|
|
7
|
-
AUTH_REQUIRED: -32001,
|
|
8
|
-
// Missing authentication
|
|
9
|
-
AUTH_INVALID: -32002,
|
|
10
|
-
// Invalid API key format or key
|
|
11
|
-
AUTH_REVOKED: -32003,
|
|
12
|
-
// API key has been revoked
|
|
13
|
-
AUTH_FORBIDDEN: -32004
|
|
14
|
-
// Tenant suspended or no access
|
|
15
|
-
};
|
|
16
|
-
function createJsonRpcError(code, message, hint) {
|
|
17
|
-
const error = {
|
|
18
|
-
jsonrpc: "2.0",
|
|
19
|
-
error: {
|
|
20
|
-
code,
|
|
21
|
-
message
|
|
22
|
-
},
|
|
23
|
-
id: null
|
|
24
|
-
};
|
|
25
|
-
if (hint) {
|
|
26
|
-
error.error.data = { hint };
|
|
27
|
-
}
|
|
28
|
-
return error;
|
|
29
|
-
}
|
|
30
|
-
function parseBearerToken(authHeader) {
|
|
31
|
-
if (!authHeader) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
const parts = authHeader.split(" ");
|
|
35
|
-
if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
const token = parts[1];
|
|
39
|
-
if (!token || token.trim() === "") {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return token;
|
|
43
|
-
}
|
|
44
|
-
async function validateMcpApiKey(authHeader, apiKeyService, prisma) {
|
|
45
|
-
const token = parseBearerToken(authHeader);
|
|
46
|
-
if (!token) {
|
|
47
|
-
return {
|
|
48
|
-
valid: false,
|
|
49
|
-
error: "Authentication required",
|
|
50
|
-
httpStatus: 401
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const validationResult = await apiKeyService.validate(token);
|
|
54
|
-
if (!validationResult.valid) {
|
|
55
|
-
const isRevoked = validationResult.error?.includes("revoked");
|
|
56
|
-
return {
|
|
57
|
-
valid: false,
|
|
58
|
-
error: validationResult.error || "Invalid API key",
|
|
59
|
-
httpStatus: isRevoked ? 403 : 401
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
const tenant = validationResult.tenant;
|
|
63
|
-
const tenantShops = await prisma.tenantShop.findMany({
|
|
64
|
-
where: {
|
|
65
|
-
tenantId: tenant.tenantId,
|
|
66
|
-
uninstalledAt: null
|
|
67
|
-
// Only active shops
|
|
68
|
-
},
|
|
69
|
-
select: {
|
|
70
|
-
shopDomain: true,
|
|
71
|
-
scopes: true
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
const allowedShops = tenantShops.map((shop) => shop.shopDomain);
|
|
75
|
-
const mcpTenantContext = {
|
|
76
|
-
tenantId: tenant.tenantId,
|
|
77
|
-
email: tenant.email,
|
|
78
|
-
defaultShop: validationResult.shop,
|
|
79
|
-
allowedShops
|
|
80
|
-
};
|
|
81
|
-
return {
|
|
82
|
-
valid: true,
|
|
83
|
-
tenant: mcpTenantContext
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function createMcpAuthMiddleware(options) {
|
|
87
|
-
const { apiKeyService, prisma, isRemote } = options;
|
|
88
|
-
return async (req, res, next) => {
|
|
89
|
-
if (!isRemote) {
|
|
90
|
-
log.debug("[mcp-auth] Local mode: skipping API key validation");
|
|
91
|
-
next();
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const authResult = await validateMcpApiKey(req.headers.authorization, apiKeyService, prisma);
|
|
95
|
-
if (!authResult.valid) {
|
|
96
|
-
log.debug(`[mcp-auth] Auth failed: ${authResult.error}`);
|
|
97
|
-
let errorCode;
|
|
98
|
-
let hint;
|
|
99
|
-
if (authResult.httpStatus === 401) {
|
|
100
|
-
errorCode = authResult.error === "Authentication required" ? MCP_AUTH_ERROR_CODES.AUTH_REQUIRED : MCP_AUTH_ERROR_CODES.AUTH_INVALID;
|
|
101
|
-
hint = "Include Authorization: Bearer sk_live_xxx header";
|
|
102
|
-
} else {
|
|
103
|
-
errorCode = authResult.error?.includes("revoked") ? MCP_AUTH_ERROR_CODES.AUTH_REVOKED : MCP_AUTH_ERROR_CODES.AUTH_FORBIDDEN;
|
|
104
|
-
hint = "API key has been revoked or tenant is inactive";
|
|
105
|
-
}
|
|
106
|
-
res.status(authResult.httpStatus || 401).json(createJsonRpcError(errorCode, authResult.error || "Authentication failed", hint));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
req.mcpTenantContext = authResult.tenant;
|
|
110
|
-
log.debug(
|
|
111
|
-
`[mcp-auth] Auth successful for tenant: ${authResult.tenant?.tenantId?.substring(0, 8)}...`
|
|
112
|
-
);
|
|
113
|
-
next();
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
var MCP_AUTH_ERRORS = MCP_AUTH_ERROR_CODES;
|
|
117
|
-
|
|
118
|
-
export {
|
|
119
|
-
createJsonRpcError,
|
|
120
|
-
parseBearerToken,
|
|
121
|
-
validateMcpApiKey,
|
|
122
|
-
createMcpAuthMiddleware,
|
|
123
|
-
MCP_AUTH_ERRORS
|
|
124
|
-
};
|
package/dist/chunk-6YECVENJ.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
log
|
|
3
|
-
} from "./chunk-GKGHMPEC.js";
|
|
4
|
-
|
|
5
|
-
// src/db/client.ts
|
|
6
|
-
import { PrismaClient } from "@prisma/client";
|
|
7
|
-
import { PrismaClient as PrismaClient2 } from "@prisma/client";
|
|
8
|
-
var prismaInstance = null;
|
|
9
|
-
function getPrismaClient() {
|
|
10
|
-
if (!prismaInstance) {
|
|
11
|
-
log.debug("Creating new Prisma client instance");
|
|
12
|
-
prismaInstance = new PrismaClient({
|
|
13
|
-
log: process.env.DEBUG === "true" ? ["query", "info", "warn", "error"] : ["error"]
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return prismaInstance;
|
|
17
|
-
}
|
|
18
|
-
async function disconnectPrisma() {
|
|
19
|
-
if (prismaInstance) {
|
|
20
|
-
log.debug("Disconnecting Prisma client");
|
|
21
|
-
await prismaInstance.$disconnect();
|
|
22
|
-
prismaInstance = null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
var prisma = getPrismaClient();
|
|
26
|
-
|
|
27
|
-
// src/session/store.ts
|
|
28
|
-
var SessionStore = class {
|
|
29
|
-
cleanupInterval = null;
|
|
30
|
-
prisma = getPrismaClient();
|
|
31
|
-
SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
32
|
-
// 24 hours
|
|
33
|
-
/**
|
|
34
|
-
* Retrieve session data by session ID
|
|
35
|
-
* @param sessionId - Unique session identifier
|
|
36
|
-
* @returns SessionData if valid and not expired, null otherwise
|
|
37
|
-
*/
|
|
38
|
-
async get(sessionId) {
|
|
39
|
-
try {
|
|
40
|
-
const session = await this.prisma.tenantSession.findUnique({
|
|
41
|
-
where: { id: sessionId },
|
|
42
|
-
select: {
|
|
43
|
-
tenantId: true,
|
|
44
|
-
expiresAt: true
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
if (!session) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const expiresAt = session.expiresAt.getTime();
|
|
51
|
-
if (expiresAt < Date.now()) {
|
|
52
|
-
await this.prisma.tenantSession.delete({
|
|
53
|
-
where: { id: sessionId }
|
|
54
|
-
}).catch(() => {
|
|
55
|
-
});
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
tenantId: session.tenantId,
|
|
60
|
-
expiresAt
|
|
61
|
-
};
|
|
62
|
-
} catch (error) {
|
|
63
|
-
log.error("Failed to retrieve session", error instanceof Error ? error : void 0);
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Store session data with automatic 24-hour expiration
|
|
69
|
-
* @param sessionId - Unique session identifier
|
|
70
|
-
* @param data - Session data (tenantId)
|
|
71
|
-
*/
|
|
72
|
-
async set(sessionId, data) {
|
|
73
|
-
const expiresAt = new Date(Date.now() + this.SESSION_TTL_MS);
|
|
74
|
-
try {
|
|
75
|
-
await this.prisma.tenantSession.upsert({
|
|
76
|
-
where: { id: sessionId },
|
|
77
|
-
update: {
|
|
78
|
-
expiresAt
|
|
79
|
-
},
|
|
80
|
-
create: {
|
|
81
|
-
id: sessionId,
|
|
82
|
-
tenantId: data.tenantId,
|
|
83
|
-
expiresAt
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
} catch (error) {
|
|
87
|
-
log.error("Failed to store session", error instanceof Error ? error : void 0);
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Delete a specific session
|
|
93
|
-
* @param sessionId - Session identifier to delete
|
|
94
|
-
*/
|
|
95
|
-
async delete(sessionId) {
|
|
96
|
-
try {
|
|
97
|
-
await this.prisma.tenantSession.delete({
|
|
98
|
-
where: { id: sessionId }
|
|
99
|
-
});
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (error && typeof error === "object" && "code" in error && error.code !== "P2025") {
|
|
102
|
-
log.error("Failed to delete session", error instanceof Error ? error : void 0);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Delete all sessions for a specific tenant
|
|
108
|
-
* Used when a tenant changes their password for security
|
|
109
|
-
* @param tenantId - Tenant identifier
|
|
110
|
-
*/
|
|
111
|
-
async deleteByTenantId(tenantId) {
|
|
112
|
-
try {
|
|
113
|
-
await this.prisma.tenantSession.deleteMany({
|
|
114
|
-
where: { tenantId }
|
|
115
|
-
});
|
|
116
|
-
log.debug(`Deleted all sessions for tenant ${tenantId}`);
|
|
117
|
-
} catch (error) {
|
|
118
|
-
log.error("Failed to delete tenant sessions", error instanceof Error ? error : void 0);
|
|
119
|
-
throw error;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Remove all expired sessions from the store
|
|
124
|
-
*/
|
|
125
|
-
async cleanup() {
|
|
126
|
-
try {
|
|
127
|
-
const result = await this.prisma.tenantSession.deleteMany({
|
|
128
|
-
where: {
|
|
129
|
-
expiresAt: {
|
|
130
|
-
lt: /* @__PURE__ */ new Date()
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
if (result.count > 0) {
|
|
135
|
-
log.debug(`Cleaned up ${result.count} expired sessions`);
|
|
136
|
-
}
|
|
137
|
-
} catch (error) {
|
|
138
|
-
log.error("Session cleanup failed", error instanceof Error ? error : void 0);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Start automatic cleanup task (runs every 10 minutes)
|
|
143
|
-
* @returns Timer handle for cleanup task
|
|
144
|
-
*/
|
|
145
|
-
startCleanup() {
|
|
146
|
-
if (this.cleanupInterval) {
|
|
147
|
-
return this.cleanupInterval;
|
|
148
|
-
}
|
|
149
|
-
this.cleanupInterval = setInterval(
|
|
150
|
-
() => {
|
|
151
|
-
this.cleanup().catch((error) => {
|
|
152
|
-
log.error("Background session cleanup error", error instanceof Error ? error : void 0);
|
|
153
|
-
});
|
|
154
|
-
},
|
|
155
|
-
10 * 60 * 1e3
|
|
156
|
-
);
|
|
157
|
-
this.cleanupInterval.unref();
|
|
158
|
-
log.info("Session cleanup background task started (runs every 10 minutes)");
|
|
159
|
-
return this.cleanupInterval;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Stop the automatic cleanup task
|
|
163
|
-
*/
|
|
164
|
-
stopCleanup() {
|
|
165
|
-
if (this.cleanupInterval) {
|
|
166
|
-
clearInterval(this.cleanupInterval);
|
|
167
|
-
this.cleanupInterval = null;
|
|
168
|
-
log.info("Session cleanup background task stopped");
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Get the number of active sessions (for testing/monitoring)
|
|
173
|
-
*/
|
|
174
|
-
async size() {
|
|
175
|
-
try {
|
|
176
|
-
return await this.prisma.tenantSession.count({
|
|
177
|
-
where: {
|
|
178
|
-
expiresAt: {
|
|
179
|
-
gte: /* @__PURE__ */ new Date()
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
} catch (error) {
|
|
184
|
-
log.error("Failed to count sessions", error instanceof Error ? error : void 0);
|
|
185
|
-
return 0;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Clear all sessions (for testing)
|
|
190
|
-
*/
|
|
191
|
-
async clear() {
|
|
192
|
-
try {
|
|
193
|
-
await this.prisma.tenantSession.deleteMany();
|
|
194
|
-
} catch (error) {
|
|
195
|
-
log.error("Failed to clear sessions", error instanceof Error ? error : void 0);
|
|
196
|
-
throw error;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
var sessionStore = new SessionStore();
|
|
201
|
-
|
|
202
|
-
export {
|
|
203
|
-
getPrismaClient,
|
|
204
|
-
disconnectPrisma,
|
|
205
|
-
prisma,
|
|
206
|
-
SessionStore,
|
|
207
|
-
sessionStore
|
|
208
|
-
};
|