@pikku/mongodb 0.12.2 → 0.12.4
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/CHANGELOG.md +38 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/mongodb-channel-store.d.ts +3 -4
- package/dist/src/mongodb-channel-store.js +5 -5
- package/dist/src/mongodb-deployment-service.d.ts +6 -3
- package/dist/src/mongodb-deployment-service.js +22 -13
- package/dist/src/mongodb-secret-service.js +1 -1
- package/dist/src/mongodb-session-store.d.ts +14 -0
- package/dist/src/mongodb-session-store.js +32 -0
- package/dist/src/mongodb-workflow-service.js +3 -0
- package/dist/src/pikku-mongodb.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/mongodb-channel-store.ts +8 -12
- package/src/mongodb-deployment-service.ts +37 -16
- package/src/mongodb-secret-service.ts +1 -1
- package/src/mongodb-services.test.ts +6 -0
- package/src/mongodb-session-store.ts +50 -0
- package/src/mongodb-workflow-service.ts +3 -0
- package/src/pikku-mongodb.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @pikku/mongodb
|
|
2
2
|
|
|
3
|
+
## 0.12.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 311c0c4: Unify session persistence through SessionStore, remove session blob from ChannelStore
|
|
8
|
+
|
|
9
|
+
- PikkuSessionService now persists sessions via SessionStore on set()/clear() instead of every function call
|
|
10
|
+
- ChannelStore no longer stores session data — maps channelId to pikkuUserId only
|
|
11
|
+
- ChannelStore API: setUserSession/getChannelAndSession replaced with setPikkuUserId/getChannel
|
|
12
|
+
- Serverless channel runner resolves sessions from SessionStore using pikkuUserId from ChannelStore
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [311c0c4]
|
|
15
|
+
- @pikku/core@0.12.18
|
|
16
|
+
|
|
17
|
+
## 0.12.3
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- a2ee6d0: Replace process.exit(1) with thrown error on MongoDB connection failure to allow graceful error handling.
|
|
22
|
+
- 87433f0: Validate state key names in updateRunState to prevent MongoDB operator injection.
|
|
23
|
+
- Updated dependencies [e412b4d]
|
|
24
|
+
- Updated dependencies [53dc8c8]
|
|
25
|
+
- Updated dependencies [0a1cc51]
|
|
26
|
+
- Updated dependencies [0a1cc51]
|
|
27
|
+
- Updated dependencies [0a1cc51]
|
|
28
|
+
- Updated dependencies [0a1cc51]
|
|
29
|
+
- Updated dependencies [0a1cc51]
|
|
30
|
+
- Updated dependencies [0a1cc51]
|
|
31
|
+
- Updated dependencies [0a1cc51]
|
|
32
|
+
- Updated dependencies [0a1cc51]
|
|
33
|
+
- Updated dependencies [0a1cc51]
|
|
34
|
+
- Updated dependencies [8b9b2e9]
|
|
35
|
+
- Updated dependencies [8b9b2e9]
|
|
36
|
+
- Updated dependencies [b973d44]
|
|
37
|
+
- Updated dependencies [8b9b2e9]
|
|
38
|
+
- Updated dependencies [8b9b2e9]
|
|
39
|
+
- @pikku/core@0.12.9
|
|
40
|
+
|
|
3
41
|
## 0.12.2
|
|
4
42
|
|
|
5
43
|
### Patch Changes
|
package/dist/src/index.d.ts
CHANGED
|
@@ -10,3 +10,4 @@ export { MongoDBSecretService } from './mongodb-secret-service.js';
|
|
|
10
10
|
export type { MongoDBSecretServiceConfig } from './mongodb-secret-service.js';
|
|
11
11
|
export type { WorkflowRunService } from '@pikku/core/workflow';
|
|
12
12
|
export type { AgentRunService, AgentRunRow } from '@pikku/core/ai-agent';
|
|
13
|
+
export { MongoDBSessionStore } from './mongodb-session-store.js';
|
package/dist/src/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { MongoDBDeploymentService } from './mongodb-deployment-service.js';
|
|
|
7
7
|
export { MongoDBAIStorageService } from './mongodb-ai-storage-service.js';
|
|
8
8
|
export { MongoDBAgentRunService } from './mongodb-agent-run-service.js';
|
|
9
9
|
export { MongoDBSecretService } from './mongodb-secret-service.js';
|
|
10
|
+
export { MongoDBSessionStore } from './mongodb-session-store.js';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { CoreUserSession } from '@pikku/core';
|
|
2
1
|
import type { Channel } from '@pikku/core/channel';
|
|
3
2
|
import { ChannelStore } from '@pikku/core/channel';
|
|
4
3
|
import type { Db } from 'mongodb';
|
|
@@ -11,9 +10,9 @@ export declare class MongoDBChannelStore extends ChannelStore {
|
|
|
11
10
|
init(): Promise<void>;
|
|
12
11
|
addChannel({ channelId, channelName, openingData, }: Channel): Promise<void>;
|
|
13
12
|
removeChannels(channelIds: string[]): Promise<void>;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
setPikkuUserId(channelId: string, pikkuUserId: string | null): Promise<void>;
|
|
14
|
+
getChannel(channelId: string): Promise<Channel & {
|
|
15
|
+
pikkuUserId?: string;
|
|
17
16
|
}>;
|
|
18
17
|
close(): Promise<void>;
|
|
19
18
|
}
|
|
@@ -22,7 +22,7 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
22
22
|
_id: channelId,
|
|
23
23
|
channelName,
|
|
24
24
|
openingData: openingData || {},
|
|
25
|
-
|
|
25
|
+
pikkuUserId: null,
|
|
26
26
|
createdAt: new Date(),
|
|
27
27
|
lastWire: new Date(),
|
|
28
28
|
});
|
|
@@ -33,10 +33,10 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
33
33
|
await this.channels.deleteMany({ _id: { $in: channelIds } });
|
|
34
34
|
await this.subscriptions.deleteMany({ channelId: { $in: channelIds } });
|
|
35
35
|
}
|
|
36
|
-
async
|
|
37
|
-
await this.channels.updateOne({ _id: channelId }, { $set: {
|
|
36
|
+
async setPikkuUserId(channelId, pikkuUserId) {
|
|
37
|
+
await this.channels.updateOne({ _id: channelId }, { $set: { pikkuUserId } });
|
|
38
38
|
}
|
|
39
|
-
async
|
|
39
|
+
async getChannel(channelId) {
|
|
40
40
|
const row = await this.channels.findOne({ _id: channelId });
|
|
41
41
|
if (!row) {
|
|
42
42
|
throw new Error(`Channel not found: ${channelId}`);
|
|
@@ -45,7 +45,7 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
45
45
|
channelId: row._id,
|
|
46
46
|
channelName: row.channelName,
|
|
47
47
|
openingData: row.openingData ?? {},
|
|
48
|
-
|
|
48
|
+
pikkuUserId: row.pikkuUserId ?? undefined,
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
async close() { }
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import type { DeploymentService, DeploymentServiceConfig, DeploymentConfig
|
|
1
|
+
import type { DeploymentService, DeploymentServiceConfig, DeploymentConfig } from '@pikku/core/services';
|
|
2
|
+
import type { JWTService, SecretService } from '@pikku/core/services';
|
|
2
3
|
import type { Db } from 'mongodb';
|
|
3
4
|
export declare class MongoDBDeploymentService implements DeploymentService {
|
|
4
5
|
private db;
|
|
6
|
+
private jwt?;
|
|
7
|
+
private secrets?;
|
|
5
8
|
private initialized;
|
|
6
9
|
private heartbeatTimer?;
|
|
7
10
|
private deploymentConfig?;
|
|
8
11
|
private heartbeatInterval;
|
|
9
12
|
private heartbeatTtl;
|
|
10
13
|
private deployments;
|
|
11
|
-
constructor(config: DeploymentServiceConfig, db: Db);
|
|
14
|
+
constructor(config: DeploymentServiceConfig, db: Db, jwt?: JWTService | undefined, secrets?: SecretService | undefined);
|
|
12
15
|
init(): Promise<void>;
|
|
13
16
|
start(config: DeploymentConfig): Promise<void>;
|
|
14
17
|
stop(): Promise<void>;
|
|
15
|
-
|
|
18
|
+
invoke(funcName: string, data: unknown, session?: unknown, traceId?: string): Promise<unknown>;
|
|
16
19
|
private sendHeartbeat;
|
|
17
20
|
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import { buildRemoteHeaders } from '@pikku/core/remote';
|
|
1
2
|
import { getAllFunctionNames } from '@pikku/core/function';
|
|
2
3
|
export class MongoDBDeploymentService {
|
|
3
4
|
db;
|
|
5
|
+
jwt;
|
|
6
|
+
secrets;
|
|
4
7
|
initialized = false;
|
|
5
8
|
heartbeatTimer;
|
|
6
9
|
deploymentConfig;
|
|
7
10
|
heartbeatInterval;
|
|
8
11
|
heartbeatTtl;
|
|
9
12
|
deployments;
|
|
10
|
-
constructor(config, db) {
|
|
13
|
+
constructor(config, db, jwt, secrets) {
|
|
11
14
|
this.db = db;
|
|
15
|
+
this.jwt = jwt;
|
|
16
|
+
this.secrets = secrets;
|
|
12
17
|
this.heartbeatInterval = config.heartbeatInterval ?? 10000;
|
|
13
18
|
this.heartbeatTtl = config.heartbeatTtl ?? 30000;
|
|
14
19
|
}
|
|
@@ -48,19 +53,23 @@ export class MongoDBDeploymentService {
|
|
|
48
53
|
});
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
|
-
async
|
|
56
|
+
async invoke(funcName, data, session, traceId) {
|
|
57
|
+
const headers = await buildRemoteHeaders(this.jwt, this.secrets, funcName, session, traceId);
|
|
52
58
|
const cutoff = new Date(Date.now() - this.heartbeatTtl);
|
|
53
|
-
const result = await this.deployments
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const result = await this.deployments.findOne({ functions: funcName, lastHeartbeat: { $gt: cutoff } }, { sort: { lastHeartbeat: -1 } });
|
|
60
|
+
if (!result) {
|
|
61
|
+
throw new Error(`No deployment found for function '${funcName}'`);
|
|
62
|
+
}
|
|
63
|
+
const url = `${result.endpoint}/remote/rpc/${encodeURIComponent(funcName)}`;
|
|
64
|
+
const response = await fetch(url, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
67
|
+
body: JSON.stringify({ data }),
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new Error(`Remote RPC call to '${funcName}' failed: ${response.status}`);
|
|
71
|
+
}
|
|
72
|
+
return response.json();
|
|
64
73
|
}
|
|
65
74
|
async sendHeartbeat() {
|
|
66
75
|
if (!this.deploymentConfig)
|
|
@@ -50,7 +50,7 @@ export class MongoDBSecretService {
|
|
|
50
50
|
async getSecret(key) {
|
|
51
51
|
const row = await this.secrets.findOne({ _id: key });
|
|
52
52
|
if (!row)
|
|
53
|
-
throw new Error(
|
|
53
|
+
throw new Error('Requested secret not found');
|
|
54
54
|
const kek = this.getKEK(row.keyVersion);
|
|
55
55
|
const result = await envelopeDecrypt(kek, row.ciphertext, row.wrappedDek);
|
|
56
56
|
await this.logAudit(key, 'read');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CoreUserSession } from '@pikku/core';
|
|
2
|
+
import type { SessionStore } from '@pikku/core/services';
|
|
3
|
+
import type { Db } from 'mongodb';
|
|
4
|
+
export declare class MongoDBSessionStore implements SessionStore {
|
|
5
|
+
private db;
|
|
6
|
+
private initialized;
|
|
7
|
+
private sessions;
|
|
8
|
+
constructor(db: Db);
|
|
9
|
+
init(): Promise<void>;
|
|
10
|
+
get(pikkuUserId: string): Promise<CoreUserSession | undefined>;
|
|
11
|
+
set(pikkuUserId: string, session: CoreUserSession): Promise<void>;
|
|
12
|
+
clear(pikkuUserId: string): Promise<void>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class MongoDBSessionStore {
|
|
2
|
+
db;
|
|
3
|
+
initialized = false;
|
|
4
|
+
sessions;
|
|
5
|
+
constructor(db) {
|
|
6
|
+
this.db = db;
|
|
7
|
+
}
|
|
8
|
+
async init() {
|
|
9
|
+
if (this.initialized)
|
|
10
|
+
return;
|
|
11
|
+
this.sessions = this.db.collection('pikkuUserSessions');
|
|
12
|
+
this.initialized = true;
|
|
13
|
+
}
|
|
14
|
+
async get(pikkuUserId) {
|
|
15
|
+
const doc = await this.sessions.findOne({ _id: pikkuUserId });
|
|
16
|
+
if (!doc) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return doc.session;
|
|
20
|
+
}
|
|
21
|
+
async set(pikkuUserId, session) {
|
|
22
|
+
const now = new Date();
|
|
23
|
+
await this.sessions.updateOne({ _id: pikkuUserId }, {
|
|
24
|
+
$set: { session, updatedAt: now },
|
|
25
|
+
$setOnInsert: { createdAt: now },
|
|
26
|
+
}, { upsert: true });
|
|
27
|
+
}
|
|
28
|
+
async clear(pikkuUserId) {
|
|
29
|
+
await this.sessions.deleteOne({ _id: pikkuUserId });
|
|
30
|
+
}
|
|
31
|
+
async close() { }
|
|
32
|
+
}
|
|
@@ -311,6 +311,9 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
311
311
|
await this.steps.updateOne({ _id: stepId }, { $set: { branchTaken: branchKey, updatedAt: new Date() } });
|
|
312
312
|
}
|
|
313
313
|
async updateRunState(runId, name, value) {
|
|
314
|
+
if (!/^[a-zA-Z0-9_]+$/.test(name)) {
|
|
315
|
+
throw new Error('Invalid state key name');
|
|
316
|
+
}
|
|
314
317
|
await this.runs.updateOne({ _id: runId }, {
|
|
315
318
|
$set: {
|
|
316
319
|
[`state.${name}`]: value,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts","../src/mongodb-agent-run-service.ts","../src/mongodb-ai-storage-service.ts","../src/mongodb-channel-store.ts","../src/mongodb-deployment-service.ts","../src/mongodb-eventhub-store.ts","../src/mongodb-secret-service.ts","../src/mongodb-workflow-run-service.ts","../src/mongodb-workflow-service.ts","../src/pikku-mongodb.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/index.ts","../src/mongodb-agent-run-service.ts","../src/mongodb-ai-storage-service.ts","../src/mongodb-channel-store.ts","../src/mongodb-deployment-service.ts","../src/mongodb-eventhub-store.ts","../src/mongodb-secret-service.ts","../src/mongodb-session-store.ts","../src/mongodb-workflow-run-service.ts","../src/mongodb-workflow-service.ts","../src/pikku-mongodb.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/mongodb",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.4",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/src/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"prepublishOnly": "yarn build"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@pikku/core": "^0.12.
|
|
20
|
+
"@pikku/core": "^0.12.18"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"mongodb": "^6.12.0"
|
package/src/index.ts
CHANGED
|
@@ -12,3 +12,4 @@ export type { MongoDBSecretServiceConfig } from './mongodb-secret-service.js'
|
|
|
12
12
|
|
|
13
13
|
export type { WorkflowRunService } from '@pikku/core/workflow'
|
|
14
14
|
export type { AgentRunService, AgentRunRow } from '@pikku/core/ai-agent'
|
|
15
|
+
export { MongoDBSessionStore } from './mongodb-session-store.js'
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { CoreUserSession } from '@pikku/core'
|
|
2
1
|
import type { Channel } from '@pikku/core/channel'
|
|
3
2
|
import { ChannelStore } from '@pikku/core/channel'
|
|
4
3
|
import type { Db, Collection } from 'mongodb'
|
|
@@ -8,7 +7,7 @@ interface ChannelDoc {
|
|
|
8
7
|
channelName: string
|
|
9
8
|
createdAt: Date
|
|
10
9
|
openingData: any
|
|
11
|
-
|
|
10
|
+
pikkuUserId: string | null
|
|
12
11
|
lastWire: Date
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -52,7 +51,7 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
52
51
|
_id: channelId,
|
|
53
52
|
channelName,
|
|
54
53
|
openingData: openingData || {},
|
|
55
|
-
|
|
54
|
+
pikkuUserId: null,
|
|
56
55
|
createdAt: new Date(),
|
|
57
56
|
lastWire: new Date(),
|
|
58
57
|
})
|
|
@@ -65,19 +64,16 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
65
64
|
await this.subscriptions.deleteMany({ channelId: { $in: channelIds } })
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
public async
|
|
67
|
+
public async setPikkuUserId(
|
|
69
68
|
channelId: string,
|
|
70
|
-
|
|
69
|
+
pikkuUserId: string | null
|
|
71
70
|
): Promise<void> {
|
|
72
|
-
await this.channels.updateOne(
|
|
73
|
-
{ _id: channelId },
|
|
74
|
-
{ $set: { userSession: session } }
|
|
75
|
-
)
|
|
71
|
+
await this.channels.updateOne({ _id: channelId }, { $set: { pikkuUserId } })
|
|
76
72
|
}
|
|
77
73
|
|
|
78
|
-
public async
|
|
74
|
+
public async getChannel(
|
|
79
75
|
channelId: string
|
|
80
|
-
): Promise<Channel & {
|
|
76
|
+
): Promise<Channel & { pikkuUserId?: string }> {
|
|
81
77
|
const row = await this.channels.findOne({ _id: channelId })
|
|
82
78
|
|
|
83
79
|
if (!row) {
|
|
@@ -88,7 +84,7 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
88
84
|
channelId: row._id,
|
|
89
85
|
channelName: row.channelName,
|
|
90
86
|
openingData: row.openingData ?? {},
|
|
91
|
-
|
|
87
|
+
pikkuUserId: row.pikkuUserId ?? undefined,
|
|
92
88
|
}
|
|
93
89
|
}
|
|
94
90
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { buildRemoteHeaders } from '@pikku/core/remote'
|
|
1
2
|
import type {
|
|
2
3
|
DeploymentService,
|
|
3
4
|
DeploymentServiceConfig,
|
|
4
5
|
DeploymentConfig,
|
|
5
|
-
DeploymentInfo,
|
|
6
6
|
} from '@pikku/core/services'
|
|
7
|
+
import type { JWTService, SecretService } from '@pikku/core/services'
|
|
7
8
|
import { getAllFunctionNames } from '@pikku/core/function'
|
|
8
9
|
import type { Db, Collection } from 'mongodb'
|
|
9
10
|
|
|
@@ -25,7 +26,9 @@ export class MongoDBDeploymentService implements DeploymentService {
|
|
|
25
26
|
|
|
26
27
|
constructor(
|
|
27
28
|
config: DeploymentServiceConfig,
|
|
28
|
-
private db: Db
|
|
29
|
+
private db: Db,
|
|
30
|
+
private jwt?: JWTService,
|
|
31
|
+
private secrets?: SecretService
|
|
29
32
|
) {
|
|
30
33
|
this.heartbeatInterval = config.heartbeatInterval ?? 10000
|
|
31
34
|
this.heartbeatTtl = config.heartbeatTtl ?? 30000
|
|
@@ -82,21 +85,39 @@ export class MongoDBDeploymentService implements DeploymentService {
|
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
async
|
|
88
|
+
async invoke(
|
|
89
|
+
funcName: string,
|
|
90
|
+
data: unknown,
|
|
91
|
+
session?: unknown,
|
|
92
|
+
traceId?: string
|
|
93
|
+
): Promise<unknown> {
|
|
94
|
+
const headers = await buildRemoteHeaders(
|
|
95
|
+
this.jwt,
|
|
96
|
+
this.secrets,
|
|
97
|
+
funcName,
|
|
98
|
+
session,
|
|
99
|
+
traceId
|
|
100
|
+
)
|
|
86
101
|
const cutoff = new Date(Date.now() - this.heartbeatTtl)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
})
|
|
102
|
+
const result = await this.deployments.findOne(
|
|
103
|
+
{ functions: funcName, lastHeartbeat: { $gt: cutoff } },
|
|
104
|
+
{ sort: { lastHeartbeat: -1 } }
|
|
105
|
+
)
|
|
106
|
+
if (!result) {
|
|
107
|
+
throw new Error(`No deployment found for function '${funcName}'`)
|
|
108
|
+
}
|
|
109
|
+
const url = `${result.endpoint}/remote/rpc/${encodeURIComponent(funcName)}`
|
|
110
|
+
const response = await fetch(url, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
113
|
+
body: JSON.stringify({ data }),
|
|
114
|
+
})
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Remote RPC call to '${funcName}' failed: ${response.status}`
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
return response.json()
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
private async sendHeartbeat(): Promise<void> {
|
|
@@ -89,7 +89,7 @@ export class MongoDBSecretService implements SecretService {
|
|
|
89
89
|
|
|
90
90
|
async getSecret(key: string): Promise<string> {
|
|
91
91
|
const row = await this.secrets.findOne({ _id: key })
|
|
92
|
-
if (!row) throw new Error(
|
|
92
|
+
if (!row) throw new Error('Requested secret not found')
|
|
93
93
|
|
|
94
94
|
const kek = this.getKEK(row.keyVersion)
|
|
95
95
|
const result = await envelopeDecrypt<string>(
|
|
@@ -12,6 +12,7 @@ import { MongoDBDeploymentService } from './mongodb-deployment-service.js'
|
|
|
12
12
|
import { MongoDBAIStorageService } from './mongodb-ai-storage-service.js'
|
|
13
13
|
import { MongoDBAgentRunService } from './mongodb-agent-run-service.js'
|
|
14
14
|
import { MongoDBSecretService } from './mongodb-secret-service.js'
|
|
15
|
+
import { MongoDBSessionStore } from './mongodb-session-store.js'
|
|
15
16
|
|
|
16
17
|
function registerTests(name: string, getDb: () => Db) {
|
|
17
18
|
defineServiceTests({
|
|
@@ -52,6 +53,11 @@ function registerTests(name: string, getDb: () => Db) {
|
|
|
52
53
|
await s.init()
|
|
53
54
|
return s
|
|
54
55
|
},
|
|
56
|
+
sessionStore: async () => {
|
|
57
|
+
const s = new MongoDBSessionStore(getDb())
|
|
58
|
+
await s.init()
|
|
59
|
+
return s
|
|
60
|
+
},
|
|
55
61
|
},
|
|
56
62
|
})
|
|
57
63
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CoreUserSession } from '@pikku/core'
|
|
2
|
+
import type { SessionStore } from '@pikku/core/services'
|
|
3
|
+
import type { Db, Collection } from 'mongodb'
|
|
4
|
+
|
|
5
|
+
interface SessionDoc {
|
|
6
|
+
_id: string
|
|
7
|
+
session: any
|
|
8
|
+
createdAt: Date
|
|
9
|
+
updatedAt: Date
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class MongoDBSessionStore implements SessionStore {
|
|
13
|
+
private initialized = false
|
|
14
|
+
private sessions!: Collection<SessionDoc>
|
|
15
|
+
|
|
16
|
+
constructor(private db: Db) {}
|
|
17
|
+
|
|
18
|
+
public async init(): Promise<void> {
|
|
19
|
+
if (this.initialized) return
|
|
20
|
+
|
|
21
|
+
this.sessions = this.db.collection<SessionDoc>('pikkuUserSessions')
|
|
22
|
+
this.initialized = true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async get(pikkuUserId: string): Promise<CoreUserSession | undefined> {
|
|
26
|
+
const doc = await this.sessions.findOne({ _id: pikkuUserId })
|
|
27
|
+
if (!doc) {
|
|
28
|
+
return undefined
|
|
29
|
+
}
|
|
30
|
+
return doc.session as CoreUserSession
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async set(pikkuUserId: string, session: CoreUserSession): Promise<void> {
|
|
34
|
+
const now = new Date()
|
|
35
|
+
await this.sessions.updateOne(
|
|
36
|
+
{ _id: pikkuUserId },
|
|
37
|
+
{
|
|
38
|
+
$set: { session, updatedAt: now },
|
|
39
|
+
$setOnInsert: { createdAt: now },
|
|
40
|
+
},
|
|
41
|
+
{ upsert: true }
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async clear(pikkuUserId: string): Promise<void> {
|
|
46
|
+
await this.sessions.deleteOne({ _id: pikkuUserId })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public async close(): Promise<void> {}
|
|
50
|
+
}
|
|
@@ -515,6 +515,9 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
515
515
|
name: string,
|
|
516
516
|
value: unknown
|
|
517
517
|
): Promise<void> {
|
|
518
|
+
if (!/^[a-zA-Z0-9_]+$/.test(name)) {
|
|
519
|
+
throw new Error('Invalid state key name')
|
|
520
|
+
}
|
|
518
521
|
await this.runs.updateOne(
|
|
519
522
|
{ _id: runId },
|
|
520
523
|
{
|