@lti-tool/postgresql 1.0.0
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 +13 -0
- package/docker-compose.yml +18 -0
- package/drizzle/0000_black_mentallo.sql +42 -0
- package/drizzle/meta/0000_snapshot.json +330 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +11 -0
- package/package.json +59 -0
- package/src/cacheConfig.ts +23 -0
- package/src/db/schema/clients.schema.ts +18 -0
- package/src/db/schema/deployments.schema.ts +20 -0
- package/src/db/schema/index.ts +5 -0
- package/src/db/schema/nonces.schema.ts +6 -0
- package/src/db/schema/registrationSessions.schema.ts +14 -0
- package/src/db/schema/sessions.schema.ts +12 -0
- package/src/index.ts +2 -0
- package/src/interfaces/postgresStorageConfig.ts +35 -0
- package/src/postgresStorage.ts +631 -0
- package/test/postgresStorage.integration.test.ts +207 -0
- package/tsconfig.json +8 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:16
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_USER: lti_user
|
|
6
|
+
POSTGRES_PASSWORD: lti_password
|
|
7
|
+
POSTGRES_DB: lti_test
|
|
8
|
+
ports:
|
|
9
|
+
- '5432:5432'
|
|
10
|
+
volumes:
|
|
11
|
+
- postgres_data:/var/lib/postgresql/data
|
|
12
|
+
healthcheck:
|
|
13
|
+
test: ['CMD-SHELL', 'pg_isready -U lti_user -d lti_test']
|
|
14
|
+
timeout: 5s
|
|
15
|
+
retries: 10
|
|
16
|
+
|
|
17
|
+
volumes:
|
|
18
|
+
postgres_data:
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
CREATE TABLE "clients" (
|
|
2
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
3
|
+
"name" varchar(255) NOT NULL,
|
|
4
|
+
"iss" varchar(255) NOT NULL,
|
|
5
|
+
"client_id" varchar(255) NOT NULL,
|
|
6
|
+
"auth_url" text NOT NULL,
|
|
7
|
+
"token_url" text NOT NULL,
|
|
8
|
+
"jwks_url" text NOT NULL
|
|
9
|
+
);
|
|
10
|
+
--> statement-breakpoint
|
|
11
|
+
CREATE TABLE "deployments" (
|
|
12
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
13
|
+
"deployment_id" varchar(255) NOT NULL,
|
|
14
|
+
"name" varchar(255),
|
|
15
|
+
"description" text,
|
|
16
|
+
"client_id" uuid NOT NULL
|
|
17
|
+
);
|
|
18
|
+
--> statement-breakpoint
|
|
19
|
+
CREATE TABLE "nonces" (
|
|
20
|
+
"nonce" varchar(255) PRIMARY KEY NOT NULL,
|
|
21
|
+
"expires_at" timestamp with time zone NOT NULL
|
|
22
|
+
);
|
|
23
|
+
--> statement-breakpoint
|
|
24
|
+
CREATE TABLE "registration_sessions" (
|
|
25
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
26
|
+
"data" jsonb NOT NULL,
|
|
27
|
+
"expires_at" timestamp with time zone NOT NULL
|
|
28
|
+
);
|
|
29
|
+
--> statement-breakpoint
|
|
30
|
+
CREATE TABLE "sessions" (
|
|
31
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
32
|
+
"data" jsonb NOT NULL,
|
|
33
|
+
"expires_at" timestamp with time zone NOT NULL
|
|
34
|
+
);
|
|
35
|
+
--> statement-breakpoint
|
|
36
|
+
ALTER TABLE "deployments" ADD CONSTRAINT "deployments_client_id_clients_id_fk" FOREIGN KEY ("client_id") REFERENCES "public"."clients"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
37
|
+
CREATE INDEX "issuer_client_idx" ON "clients" USING btree ("client_id","iss");--> statement-breakpoint
|
|
38
|
+
CREATE UNIQUE INDEX "iss_client_id_unique" ON "clients" USING btree ("iss","client_id");--> statement-breakpoint
|
|
39
|
+
CREATE INDEX "deployment_id_idx" ON "deployments" USING btree ("deployment_id");--> statement-breakpoint
|
|
40
|
+
CREATE UNIQUE INDEX "client_deployment_unique" ON "deployments" USING btree ("client_id","deployment_id");--> statement-breakpoint
|
|
41
|
+
CREATE INDEX "reg_sessions_expires_at_idx" ON "registration_sessions" USING btree ("expires_at");--> statement-breakpoint
|
|
42
|
+
CREATE INDEX "sessions_expires_at_idx" ON "sessions" USING btree ("expires_at");
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "7e071465-0b94-4f8d-a120-45ddb5575592",
|
|
3
|
+
"prevId": "00000000-0000-0000-0000-000000000000",
|
|
4
|
+
"version": "7",
|
|
5
|
+
"dialect": "postgresql",
|
|
6
|
+
"tables": {
|
|
7
|
+
"public.clients": {
|
|
8
|
+
"name": "clients",
|
|
9
|
+
"schema": "",
|
|
10
|
+
"columns": {
|
|
11
|
+
"id": {
|
|
12
|
+
"name": "id",
|
|
13
|
+
"type": "uuid",
|
|
14
|
+
"primaryKey": true,
|
|
15
|
+
"notNull": true,
|
|
16
|
+
"default": "gen_random_uuid()"
|
|
17
|
+
},
|
|
18
|
+
"name": {
|
|
19
|
+
"name": "name",
|
|
20
|
+
"type": "varchar(255)",
|
|
21
|
+
"primaryKey": false,
|
|
22
|
+
"notNull": true
|
|
23
|
+
},
|
|
24
|
+
"iss": {
|
|
25
|
+
"name": "iss",
|
|
26
|
+
"type": "varchar(255)",
|
|
27
|
+
"primaryKey": false,
|
|
28
|
+
"notNull": true
|
|
29
|
+
},
|
|
30
|
+
"client_id": {
|
|
31
|
+
"name": "client_id",
|
|
32
|
+
"type": "varchar(255)",
|
|
33
|
+
"primaryKey": false,
|
|
34
|
+
"notNull": true
|
|
35
|
+
},
|
|
36
|
+
"auth_url": {
|
|
37
|
+
"name": "auth_url",
|
|
38
|
+
"type": "text",
|
|
39
|
+
"primaryKey": false,
|
|
40
|
+
"notNull": true
|
|
41
|
+
},
|
|
42
|
+
"token_url": {
|
|
43
|
+
"name": "token_url",
|
|
44
|
+
"type": "text",
|
|
45
|
+
"primaryKey": false,
|
|
46
|
+
"notNull": true
|
|
47
|
+
},
|
|
48
|
+
"jwks_url": {
|
|
49
|
+
"name": "jwks_url",
|
|
50
|
+
"type": "text",
|
|
51
|
+
"primaryKey": false,
|
|
52
|
+
"notNull": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"indexes": {
|
|
56
|
+
"issuer_client_idx": {
|
|
57
|
+
"name": "issuer_client_idx",
|
|
58
|
+
"columns": [
|
|
59
|
+
{
|
|
60
|
+
"expression": "client_id",
|
|
61
|
+
"isExpression": false,
|
|
62
|
+
"asc": true,
|
|
63
|
+
"nulls": "last"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"expression": "iss",
|
|
67
|
+
"isExpression": false,
|
|
68
|
+
"asc": true,
|
|
69
|
+
"nulls": "last"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"isUnique": false,
|
|
73
|
+
"concurrently": false,
|
|
74
|
+
"method": "btree",
|
|
75
|
+
"with": {}
|
|
76
|
+
},
|
|
77
|
+
"iss_client_id_unique": {
|
|
78
|
+
"name": "iss_client_id_unique",
|
|
79
|
+
"columns": [
|
|
80
|
+
{
|
|
81
|
+
"expression": "iss",
|
|
82
|
+
"isExpression": false,
|
|
83
|
+
"asc": true,
|
|
84
|
+
"nulls": "last"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"expression": "client_id",
|
|
88
|
+
"isExpression": false,
|
|
89
|
+
"asc": true,
|
|
90
|
+
"nulls": "last"
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
"isUnique": true,
|
|
94
|
+
"concurrently": false,
|
|
95
|
+
"method": "btree",
|
|
96
|
+
"with": {}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"foreignKeys": {},
|
|
100
|
+
"compositePrimaryKeys": {},
|
|
101
|
+
"uniqueConstraints": {},
|
|
102
|
+
"policies": {},
|
|
103
|
+
"checkConstraints": {},
|
|
104
|
+
"isRLSEnabled": false
|
|
105
|
+
},
|
|
106
|
+
"public.deployments": {
|
|
107
|
+
"name": "deployments",
|
|
108
|
+
"schema": "",
|
|
109
|
+
"columns": {
|
|
110
|
+
"id": {
|
|
111
|
+
"name": "id",
|
|
112
|
+
"type": "uuid",
|
|
113
|
+
"primaryKey": true,
|
|
114
|
+
"notNull": true,
|
|
115
|
+
"default": "gen_random_uuid()"
|
|
116
|
+
},
|
|
117
|
+
"deployment_id": {
|
|
118
|
+
"name": "deployment_id",
|
|
119
|
+
"type": "varchar(255)",
|
|
120
|
+
"primaryKey": false,
|
|
121
|
+
"notNull": true
|
|
122
|
+
},
|
|
123
|
+
"name": {
|
|
124
|
+
"name": "name",
|
|
125
|
+
"type": "varchar(255)",
|
|
126
|
+
"primaryKey": false,
|
|
127
|
+
"notNull": false
|
|
128
|
+
},
|
|
129
|
+
"description": {
|
|
130
|
+
"name": "description",
|
|
131
|
+
"type": "text",
|
|
132
|
+
"primaryKey": false,
|
|
133
|
+
"notNull": false
|
|
134
|
+
},
|
|
135
|
+
"client_id": {
|
|
136
|
+
"name": "client_id",
|
|
137
|
+
"type": "uuid",
|
|
138
|
+
"primaryKey": false,
|
|
139
|
+
"notNull": true
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"indexes": {
|
|
143
|
+
"deployment_id_idx": {
|
|
144
|
+
"name": "deployment_id_idx",
|
|
145
|
+
"columns": [
|
|
146
|
+
{
|
|
147
|
+
"expression": "deployment_id",
|
|
148
|
+
"isExpression": false,
|
|
149
|
+
"asc": true,
|
|
150
|
+
"nulls": "last"
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
"isUnique": false,
|
|
154
|
+
"concurrently": false,
|
|
155
|
+
"method": "btree",
|
|
156
|
+
"with": {}
|
|
157
|
+
},
|
|
158
|
+
"client_deployment_unique": {
|
|
159
|
+
"name": "client_deployment_unique",
|
|
160
|
+
"columns": [
|
|
161
|
+
{
|
|
162
|
+
"expression": "client_id",
|
|
163
|
+
"isExpression": false,
|
|
164
|
+
"asc": true,
|
|
165
|
+
"nulls": "last"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"expression": "deployment_id",
|
|
169
|
+
"isExpression": false,
|
|
170
|
+
"asc": true,
|
|
171
|
+
"nulls": "last"
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"isUnique": true,
|
|
175
|
+
"concurrently": false,
|
|
176
|
+
"method": "btree",
|
|
177
|
+
"with": {}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"foreignKeys": {
|
|
181
|
+
"deployments_client_id_clients_id_fk": {
|
|
182
|
+
"name": "deployments_client_id_clients_id_fk",
|
|
183
|
+
"tableFrom": "deployments",
|
|
184
|
+
"tableTo": "clients",
|
|
185
|
+
"columnsFrom": ["client_id"],
|
|
186
|
+
"columnsTo": ["id"],
|
|
187
|
+
"onDelete": "no action",
|
|
188
|
+
"onUpdate": "no action"
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"compositePrimaryKeys": {},
|
|
192
|
+
"uniqueConstraints": {},
|
|
193
|
+
"policies": {},
|
|
194
|
+
"checkConstraints": {},
|
|
195
|
+
"isRLSEnabled": false
|
|
196
|
+
},
|
|
197
|
+
"public.nonces": {
|
|
198
|
+
"name": "nonces",
|
|
199
|
+
"schema": "",
|
|
200
|
+
"columns": {
|
|
201
|
+
"nonce": {
|
|
202
|
+
"name": "nonce",
|
|
203
|
+
"type": "varchar(255)",
|
|
204
|
+
"primaryKey": true,
|
|
205
|
+
"notNull": true
|
|
206
|
+
},
|
|
207
|
+
"expires_at": {
|
|
208
|
+
"name": "expires_at",
|
|
209
|
+
"type": "timestamp with time zone",
|
|
210
|
+
"primaryKey": false,
|
|
211
|
+
"notNull": true
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
"indexes": {},
|
|
215
|
+
"foreignKeys": {},
|
|
216
|
+
"compositePrimaryKeys": {},
|
|
217
|
+
"uniqueConstraints": {},
|
|
218
|
+
"policies": {},
|
|
219
|
+
"checkConstraints": {},
|
|
220
|
+
"isRLSEnabled": false
|
|
221
|
+
},
|
|
222
|
+
"public.registration_sessions": {
|
|
223
|
+
"name": "registration_sessions",
|
|
224
|
+
"schema": "",
|
|
225
|
+
"columns": {
|
|
226
|
+
"id": {
|
|
227
|
+
"name": "id",
|
|
228
|
+
"type": "uuid",
|
|
229
|
+
"primaryKey": true,
|
|
230
|
+
"notNull": true,
|
|
231
|
+
"default": "gen_random_uuid()"
|
|
232
|
+
},
|
|
233
|
+
"data": {
|
|
234
|
+
"name": "data",
|
|
235
|
+
"type": "jsonb",
|
|
236
|
+
"primaryKey": false,
|
|
237
|
+
"notNull": true
|
|
238
|
+
},
|
|
239
|
+
"expires_at": {
|
|
240
|
+
"name": "expires_at",
|
|
241
|
+
"type": "timestamp with time zone",
|
|
242
|
+
"primaryKey": false,
|
|
243
|
+
"notNull": true
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"indexes": {
|
|
247
|
+
"reg_sessions_expires_at_idx": {
|
|
248
|
+
"name": "reg_sessions_expires_at_idx",
|
|
249
|
+
"columns": [
|
|
250
|
+
{
|
|
251
|
+
"expression": "expires_at",
|
|
252
|
+
"isExpression": false,
|
|
253
|
+
"asc": true,
|
|
254
|
+
"nulls": "last"
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
"isUnique": false,
|
|
258
|
+
"concurrently": false,
|
|
259
|
+
"method": "btree",
|
|
260
|
+
"with": {}
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"foreignKeys": {},
|
|
264
|
+
"compositePrimaryKeys": {},
|
|
265
|
+
"uniqueConstraints": {},
|
|
266
|
+
"policies": {},
|
|
267
|
+
"checkConstraints": {},
|
|
268
|
+
"isRLSEnabled": false
|
|
269
|
+
},
|
|
270
|
+
"public.sessions": {
|
|
271
|
+
"name": "sessions",
|
|
272
|
+
"schema": "",
|
|
273
|
+
"columns": {
|
|
274
|
+
"id": {
|
|
275
|
+
"name": "id",
|
|
276
|
+
"type": "uuid",
|
|
277
|
+
"primaryKey": true,
|
|
278
|
+
"notNull": true,
|
|
279
|
+
"default": "gen_random_uuid()"
|
|
280
|
+
},
|
|
281
|
+
"data": {
|
|
282
|
+
"name": "data",
|
|
283
|
+
"type": "jsonb",
|
|
284
|
+
"primaryKey": false,
|
|
285
|
+
"notNull": true
|
|
286
|
+
},
|
|
287
|
+
"expires_at": {
|
|
288
|
+
"name": "expires_at",
|
|
289
|
+
"type": "timestamp with time zone",
|
|
290
|
+
"primaryKey": false,
|
|
291
|
+
"notNull": true
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"indexes": {
|
|
295
|
+
"sessions_expires_at_idx": {
|
|
296
|
+
"name": "sessions_expires_at_idx",
|
|
297
|
+
"columns": [
|
|
298
|
+
{
|
|
299
|
+
"expression": "expires_at",
|
|
300
|
+
"isExpression": false,
|
|
301
|
+
"asc": true,
|
|
302
|
+
"nulls": "last"
|
|
303
|
+
}
|
|
304
|
+
],
|
|
305
|
+
"isUnique": false,
|
|
306
|
+
"concurrently": false,
|
|
307
|
+
"method": "btree",
|
|
308
|
+
"with": {}
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
"foreignKeys": {},
|
|
312
|
+
"compositePrimaryKeys": {},
|
|
313
|
+
"uniqueConstraints": {},
|
|
314
|
+
"policies": {},
|
|
315
|
+
"checkConstraints": {},
|
|
316
|
+
"isRLSEnabled": false
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"enums": {},
|
|
320
|
+
"schemas": {},
|
|
321
|
+
"sequences": {},
|
|
322
|
+
"roles": {},
|
|
323
|
+
"policies": {},
|
|
324
|
+
"views": {},
|
|
325
|
+
"_meta": {
|
|
326
|
+
"columns": {},
|
|
327
|
+
"schemas": {},
|
|
328
|
+
"tables": {}
|
|
329
|
+
}
|
|
330
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lti-tool/postgresql",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "PostgreSQL storage for LTI 1.3 @lti-tool",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"lti",
|
|
7
|
+
"lti13",
|
|
8
|
+
"education",
|
|
9
|
+
"storage",
|
|
10
|
+
"postgresql",
|
|
11
|
+
"postgres",
|
|
12
|
+
"edtech",
|
|
13
|
+
"serverless"
|
|
14
|
+
],
|
|
15
|
+
"author": "jamesjoplin",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/lti-tool/lti-tool.git",
|
|
20
|
+
"directory": "packages/postgresql"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/lti-tool/lti-tool#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/lti-tool/lti-tool/issues"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"dev": "tsc --watch",
|
|
38
|
+
"test": "vitest"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@lti-tool/core": "*",
|
|
42
|
+
"drizzle-orm": "^0.45.1",
|
|
43
|
+
"lru-cache": "^11.2.5",
|
|
44
|
+
"postgres": "^3.4.8"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"pino": "^9.9.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"pino": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"dotenv": "^17.2.3",
|
|
56
|
+
"drizzle-kit": "^0.31.8",
|
|
57
|
+
"pino": "^10.3.0"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LTILaunchConfig, LTISession } from '@lti-tool/core';
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
3
|
+
|
|
4
|
+
export const LAUNCH_CONFIG_CACHE = new LRUCache<
|
|
5
|
+
string,
|
|
6
|
+
LTILaunchConfig | undefinedLaunchConfig
|
|
7
|
+
>({
|
|
8
|
+
max: 1000,
|
|
9
|
+
ttl: 1000 * 60 * 15, // 15 minutes
|
|
10
|
+
});
|
|
11
|
+
export const SESSION_CACHE = new LRUCache<string, LTISession | undefinedSession>({
|
|
12
|
+
max: 1000,
|
|
13
|
+
ttl: 1000 * 60 * 5, // 5 minutes (shorter than clients)
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const SESSION_TTL = 60 * 60 * 24; // session ttl is one day
|
|
17
|
+
export const NONCE_TTL = 60 * 15; // nonce ttl is fifteen minutes
|
|
18
|
+
|
|
19
|
+
// we need an undefined value to handle cache misses and cache them
|
|
20
|
+
export const undefinedLaunchConfigValue = Symbol('undefinedLaunchConfig');
|
|
21
|
+
export type undefinedLaunchConfig = typeof undefinedLaunchConfigValue;
|
|
22
|
+
export const undefinedSessionValue = Symbol('undefinedSession');
|
|
23
|
+
export type undefinedSession = typeof undefinedSessionValue;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { index, pgTable, text, uniqueIndex, uuid, varchar } from 'drizzle-orm/pg-core';
|
|
2
|
+
|
|
3
|
+
export const clientsTable = pgTable(
|
|
4
|
+
'clients',
|
|
5
|
+
{
|
|
6
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
7
|
+
name: varchar('name', { length: 255 }).notNull(),
|
|
8
|
+
iss: varchar('iss', { length: 255 }).notNull(),
|
|
9
|
+
clientId: varchar('client_id', { length: 255 }).notNull(),
|
|
10
|
+
authUrl: text('auth_url').notNull(),
|
|
11
|
+
tokenUrl: text('token_url').notNull(),
|
|
12
|
+
jwksUrl: text('jwks_url').notNull(),
|
|
13
|
+
},
|
|
14
|
+
(table) => [
|
|
15
|
+
index('issuer_client_idx').on(table.clientId, table.iss),
|
|
16
|
+
uniqueIndex('iss_client_id_unique').on(table.iss, table.clientId),
|
|
17
|
+
],
|
|
18
|
+
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { index, pgTable, text, uniqueIndex, uuid, varchar } from 'drizzle-orm/pg-core';
|
|
2
|
+
|
|
3
|
+
import { clientsTable } from './clients.schema';
|
|
4
|
+
|
|
5
|
+
export const deploymentsTable = pgTable(
|
|
6
|
+
'deployments',
|
|
7
|
+
{
|
|
8
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
9
|
+
deploymentId: varchar('deployment_id', { length: 255 }).notNull(),
|
|
10
|
+
name: varchar('name', { length: 255 }),
|
|
11
|
+
description: text('description'),
|
|
12
|
+
clientId: uuid('client_id')
|
|
13
|
+
.notNull()
|
|
14
|
+
.references(() => clientsTable.id),
|
|
15
|
+
},
|
|
16
|
+
(table) => [
|
|
17
|
+
index('deployment_id_idx').on(table.deploymentId),
|
|
18
|
+
uniqueIndex('client_deployment_unique').on(table.clientId, table.deploymentId),
|
|
19
|
+
],
|
|
20
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LTIDynamicRegistrationSession } from '@lti-tool/core';
|
|
2
|
+
import { index, jsonb, pgTable, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
3
|
+
|
|
4
|
+
export const registrationSessionsTable = pgTable(
|
|
5
|
+
'registration_sessions',
|
|
6
|
+
{
|
|
7
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
8
|
+
data: jsonb('data')
|
|
9
|
+
.$type<Omit<LTIDynamicRegistrationSession, 'sessionId'>>()
|
|
10
|
+
.notNull(),
|
|
11
|
+
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
|
|
12
|
+
},
|
|
13
|
+
(table) => [index('reg_sessions_expires_at_idx').on(table.expiresAt)],
|
|
14
|
+
);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { LTISession } from '@lti-tool/core';
|
|
2
|
+
import { index, jsonb, pgTable, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
3
|
+
|
|
4
|
+
export const sessionsTable = pgTable(
|
|
5
|
+
'sessions',
|
|
6
|
+
{
|
|
7
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
8
|
+
data: jsonb('data').$type<Omit<LTISession, 'id'>>().notNull(),
|
|
9
|
+
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
|
|
10
|
+
},
|
|
11
|
+
(table) => [index('sessions_expires_at_idx').on(table.expiresAt)],
|
|
12
|
+
);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
export interface PostgresStorageConfig {
|
|
4
|
+
logger?: Logger;
|
|
5
|
+
/**
|
|
6
|
+
* PostgreSQL connection URL in format: postgresql://user:password@host:port/database
|
|
7
|
+
* Compatible with DATABASE_URL environment variable used by most ORMs
|
|
8
|
+
*/
|
|
9
|
+
connectionUrl: string;
|
|
10
|
+
/**
|
|
11
|
+
* Optional postgres.js connection options
|
|
12
|
+
*/
|
|
13
|
+
poolOptions?: {
|
|
14
|
+
/**
|
|
15
|
+
* Maximum number of connections in the pool.
|
|
16
|
+
* Defaults to 1 in serverless environments, 10 otherwise.
|
|
17
|
+
*
|
|
18
|
+
* Recommended values:
|
|
19
|
+
* - Serverless (Lambda, Cloud Functions): 1
|
|
20
|
+
* - Low traffic servers: 5-10
|
|
21
|
+
* - Medium traffic servers: 10-20
|
|
22
|
+
* - High traffic servers: 20-50
|
|
23
|
+
*/
|
|
24
|
+
max?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Idle timeout in seconds before a connection is closed.
|
|
27
|
+
* Defaults to 20 seconds.
|
|
28
|
+
*/
|
|
29
|
+
idleTimeout?: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Nonce expiration time in seconds (defaults to 600 = 10 minutes)
|
|
33
|
+
*/
|
|
34
|
+
nonceExpirationSeconds?: number;
|
|
35
|
+
}
|