@webqit/webflo 1.0.34 → 1.0.36
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/package.json +1 -1
- package/src/runtime-pi/{WebfloCookieStorage.js → HttpCookies.js} +5 -6
- package/src/runtime-pi/HttpSession.js +11 -0
- package/src/runtime-pi/HttpUser.js +9 -10
- package/src/runtime-pi/WebfloStorage.js +33 -57
- package/src/runtime-pi/client/{CookieStorage.js → ClientSideCookies.js} +2 -2
- package/src/runtime-pi/client/WebfloClient.js +19 -12
- package/src/runtime-pi/client/worker/WebfloWorker.js +13 -6
- package/src/runtime-pi/{server/CookieStorage.js → client/worker/WorkerSideCookies.js} +2 -2
- package/src/runtime-pi/{client/worker/CookieStorage.js → server/ServerSideCookies.js} +2 -2
- package/src/runtime-pi/server/{SessionStorage.js → ServerSideSession.js} +16 -11
- package/src/runtime-pi/server/WebfloServer.js +58 -25
- package/src/runtime-pi/client/SessionStorage.js +0 -16
- package/src/runtime-pi/client/worker/SessionStorage.js +0 -11
package/package.json
CHANGED
|
@@ -2,15 +2,14 @@ import { _isObject } from '@webqit/util/js/index.js';
|
|
|
2
2
|
import { renderCookieObj } from './util-http.js';
|
|
3
3
|
import { WebfloStorage } from './WebfloStorage.js';
|
|
4
4
|
|
|
5
|
-
export class
|
|
5
|
+
export class HttpCookies extends WebfloStorage {
|
|
6
6
|
|
|
7
7
|
#originals;
|
|
8
8
|
|
|
9
9
|
constructor(request, iterable = []) {
|
|
10
10
|
iterable = [...iterable].map(([key, value]) => [key, !_isObject(value) ? { name: key, value } : value]);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
this.#originals = { ...iterable };
|
|
11
|
+
super(new Map(iterable), null, request);
|
|
12
|
+
this.#originals = new Map(iterable);
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
async set(key, value) {
|
|
@@ -24,8 +23,8 @@ export class WebfloCookieStorage extends WebfloStorage {
|
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
async render() {
|
|
27
|
-
const entries = await Promise.all((await this.keys()).concat(
|
|
28
|
-
const a =
|
|
26
|
+
const entries = await Promise.all((await this.keys()).concat(this.#originals.keys()).map(async (key) => {
|
|
27
|
+
const a = this.#originals.get(key);
|
|
29
28
|
const b = await this.get(key, true);
|
|
30
29
|
if (a === b || (_isObject(a) && _isObject(b) && _even(a, b))) {
|
|
31
30
|
// Same
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { WebfloStorage } from './WebfloStorage.js';
|
|
2
2
|
|
|
3
3
|
export class HttpUser extends WebfloStorage {
|
|
4
|
-
|
|
5
|
-
static create(request, session, client) {
|
|
6
|
-
return new this(request, session, client);
|
|
4
|
+
|
|
5
|
+
static create(store, request, session, client) {
|
|
6
|
+
return new this(store, request, session, client);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
#session;
|
|
10
9
|
#client;
|
|
11
10
|
|
|
12
|
-
constructor(request, session, client) {
|
|
13
|
-
super(
|
|
14
|
-
|
|
11
|
+
constructor(store, request, session, client) {
|
|
12
|
+
super(
|
|
13
|
+
store,
|
|
14
|
+
request,
|
|
15
|
+
session
|
|
16
|
+
);
|
|
15
17
|
this.#client = client;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
async isSignedIn() {
|
|
19
|
-
await this.#session.refresh();
|
|
20
21
|
return await this.has('id');
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
async signIn(...args) {
|
|
24
|
-
await this.#session.refresh();
|
|
25
25
|
return await this.require(
|
|
26
26
|
['id'].concat(typeof args[0] === 'string' || Array.isArray(args[0]) ? args.unshift() : []),
|
|
27
27
|
...args
|
|
@@ -30,7 +30,6 @@ export class HttpUser extends WebfloStorage {
|
|
|
30
30
|
|
|
31
31
|
async signOut() {
|
|
32
32
|
await this.clear();
|
|
33
|
-
await this.#session.commit();
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
async confirm(data, callback, options = {}) {
|
|
@@ -3,50 +3,51 @@ import { _even } from '@webqit/util/obj/index.js';
|
|
|
3
3
|
|
|
4
4
|
export class WebfloStorage {
|
|
5
5
|
|
|
6
|
+
#store;
|
|
6
7
|
#request;
|
|
7
8
|
#session;
|
|
8
|
-
#registry;
|
|
9
|
-
#key;
|
|
10
|
-
#store;
|
|
11
9
|
#modified = false;
|
|
12
10
|
|
|
13
|
-
constructor(
|
|
14
|
-
this.#
|
|
15
|
-
this.#key = key;
|
|
11
|
+
constructor(store, request, session = null) {
|
|
12
|
+
this.#store = store || new Map;
|
|
16
13
|
this.#request = request;
|
|
17
14
|
this.#session = session === true ? this : session;
|
|
18
15
|
}
|
|
19
|
-
|
|
20
|
-
async store()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
|
|
17
|
+
async has(key) { return await this.#store.has(key); }
|
|
18
|
+
|
|
19
|
+
async get(key) { return await this.#store.get(key); }
|
|
20
|
+
|
|
21
|
+
async set(key, value) {
|
|
22
|
+
await this.#store.set(key, value);
|
|
23
|
+
this.#modified = true;
|
|
24
|
+
await this.emit(key, value);
|
|
25
|
+
return this;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
async
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
async delete(key) {
|
|
29
|
+
await this.#store.delete(key);
|
|
30
|
+
this.#modified = true;
|
|
31
|
+
await this.emit(key);
|
|
32
|
+
return this;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
async
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this
|
|
35
|
+
async clear() {
|
|
36
|
+
await this.#store.clear();
|
|
37
|
+
this.#modified = true;
|
|
38
|
+
await this.emit();
|
|
39
|
+
return this;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
async keys() { return [...await this.#store.keys()]; }
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
async values() { return [...await this.#store.values()]; }
|
|
45
|
+
|
|
46
|
+
async entries() { return [...await this.#store.entries()]; }
|
|
46
47
|
|
|
47
48
|
async json(arg = null) {
|
|
48
49
|
if (!arguments.length || typeof arg === 'boolean') {
|
|
49
|
-
return
|
|
50
|
+
return Object.fromEntries(await this.#store.entries());
|
|
50
51
|
}
|
|
51
52
|
if (!_isObject(arg)) {
|
|
52
53
|
throw new Error(`Argument must be a valid JSON object`);
|
|
@@ -56,39 +57,14 @@ export class WebfloStorage {
|
|
|
56
57
|
}));
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
async
|
|
60
|
-
|
|
61
|
-
async has(key) { return Reflect.has(await this.store(), key); }
|
|
62
|
-
|
|
63
|
-
async keys() { return Object.keys(await this.store()); }
|
|
64
|
-
|
|
65
|
-
async values() { return Object.values(await this.store()); }
|
|
66
|
-
|
|
67
|
-
async entries() { return Object.entries(await this.store()); }
|
|
68
|
-
|
|
69
|
-
async forEach(callback) { (await this.entries()).forEach(callback); }
|
|
60
|
+
async forEach(callback) { (await this.entries()).forEach(([key, value], i) => callback(value, key, i)); }
|
|
70
61
|
|
|
71
|
-
|
|
72
|
-
Reflect.set(await this.store(), key, value);
|
|
73
|
-
this.#modified = true;
|
|
74
|
-
await this.emit(key, value);
|
|
75
|
-
return this;
|
|
76
|
-
}
|
|
62
|
+
[ Symbol.iterator ]() { return this.entries().then((entries) => entries[ Symbol.iterator ]()); }
|
|
77
63
|
|
|
78
|
-
|
|
79
|
-
Reflect.deleteProperty(await this.store(), key);
|
|
80
|
-
this.#modified = true;
|
|
81
|
-
await this.emit(key);
|
|
82
|
-
return this;
|
|
83
|
-
}
|
|
64
|
+
get size() { return this.#store.sizs; }
|
|
84
65
|
|
|
85
|
-
async
|
|
86
|
-
|
|
87
|
-
Reflect.deleteProperty(await this.store(), key);
|
|
88
|
-
}
|
|
89
|
-
this.#modified = true;
|
|
90
|
-
await this.emit();
|
|
91
|
-
return this;
|
|
66
|
+
async commit() {
|
|
67
|
+
this.#modified = false;
|
|
92
68
|
}
|
|
93
69
|
|
|
94
70
|
#listeners = new Set;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpCookies } from '../HttpCookies.js';
|
|
2
2
|
|
|
3
|
-
export class
|
|
3
|
+
export class ClientSideCookies extends HttpCookies {
|
|
4
4
|
static create(request) {
|
|
5
5
|
return new this(
|
|
6
6
|
request,
|
|
@@ -6,8 +6,8 @@ import { MessagingOverBroadcast } from '../MessagingOverBroadcast.js';
|
|
|
6
6
|
import { MessagingOverChannel } from '../MessagingOverChannel.js';
|
|
7
7
|
import { MessagingOverSocket } from '../MessagingOverSocket.js';
|
|
8
8
|
import { ClientMessaging } from './ClientMessaging.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { ClientSideCookies } from './ClientSideCookies.js';
|
|
10
|
+
import { HttpSession } from '../HttpSession.js';
|
|
11
11
|
import { HttpEvent } from '../HttpEvent.js';
|
|
12
12
|
import { HttpUser } from '../HttpUser.js';
|
|
13
13
|
import { Router } from './Router.js';
|
|
@@ -23,9 +23,9 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
23
23
|
|
|
24
24
|
static get HttpEvent() { return HttpEvent; }
|
|
25
25
|
|
|
26
|
-
static get
|
|
26
|
+
static get HttpCookies() { return ClientSideCookies; }
|
|
27
27
|
|
|
28
|
-
static get
|
|
28
|
+
static get HttpSession() { return HttpSession; }
|
|
29
29
|
|
|
30
30
|
static get HttpUser() { return HttpUser; }
|
|
31
31
|
|
|
@@ -48,11 +48,8 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
48
48
|
return document.querySelector('meta[name="webflo-viewtransitions"]')?.value;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
? this.cx.params.env[this.cx.params.mappings[key]]
|
|
54
|
-
: this.cx.params.env[key];
|
|
55
|
-
}
|
|
51
|
+
#sdk = {};
|
|
52
|
+
get sdk() { return this.#sdk; }
|
|
56
53
|
|
|
57
54
|
constructor(host) {
|
|
58
55
|
super();
|
|
@@ -74,6 +71,12 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
74
71
|
};
|
|
75
72
|
}
|
|
76
73
|
|
|
74
|
+
env(key) {
|
|
75
|
+
return key in this.cx.params.mappings
|
|
76
|
+
? this.cx.params.env[this.cx.params.mappings[key]]
|
|
77
|
+
: this.cx.params.env[key];
|
|
78
|
+
}
|
|
79
|
+
|
|
77
80
|
async initialize() {
|
|
78
81
|
this.#backgroundMessaging = new MultiportMessagingAPI(this, { runtime: this });
|
|
79
82
|
// Bind response and redirect handlers
|
|
@@ -329,12 +332,16 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
329
332
|
};
|
|
330
333
|
// Create and route request
|
|
331
334
|
scope.request = this.createRequest(scope.url, scope.init);
|
|
332
|
-
scope.cookies = this.constructor.
|
|
333
|
-
scope.session = this.constructor.
|
|
335
|
+
scope.cookies = this.constructor.HttpCookies.create(scope.request);
|
|
336
|
+
scope.session = this.constructor.HttpSession.create(
|
|
337
|
+
this.#sdk.storage?.('session'),
|
|
338
|
+
scope.request
|
|
339
|
+
);
|
|
334
340
|
const messageChannel = new MessageChannel;
|
|
335
341
|
this.backgroundMessaging.add(new MessagingOverChannel(null, messageChannel.port1));
|
|
336
342
|
scope.clientMessaging = new ClientMessaging(this, messageChannel.port2);
|
|
337
343
|
scope.user = this.constructor.HttpUser.create(
|
|
344
|
+
this.#sdk.storage?.('user'),
|
|
338
345
|
scope.request,
|
|
339
346
|
scope.session,
|
|
340
347
|
scope.clientMessaging
|
|
@@ -346,7 +353,7 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
346
353
|
session: scope.session,
|
|
347
354
|
user: scope.user,
|
|
348
355
|
client: scope.clientMessaging,
|
|
349
|
-
sdk:
|
|
356
|
+
sdk: this.#sdk
|
|
350
357
|
});
|
|
351
358
|
await this.setup(scope.httpEvent);
|
|
352
359
|
scope.httpEvent.onRequestClone = () => this.createRequest(scope.url, scope.init);
|
|
@@ -3,8 +3,8 @@ import { _isObject } from '@webqit/util/js/index.js';
|
|
|
3
3
|
import { pattern } from '../../util-url.js';
|
|
4
4
|
import { WebfloRuntime } from '../../WebfloRuntime.js';
|
|
5
5
|
import { ClientMessaging } from './ClientMessaging.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { WorkerSideCookies } from './WorkerSideCookies.js';
|
|
7
|
+
import { HttpSession } from '../../HttpSession.js';
|
|
8
8
|
import { HttpEvent } from '../../HttpEvent.js';
|
|
9
9
|
import { HttpUser } from '../../HttpUser.js';
|
|
10
10
|
import { Workport } from './Workport.js';
|
|
@@ -21,9 +21,9 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
21
21
|
|
|
22
22
|
static get HttpEvent() { return HttpEvent; }
|
|
23
23
|
|
|
24
|
-
static get
|
|
24
|
+
static get HttpCookies() { return WorkerSideCookies; }
|
|
25
25
|
|
|
26
|
-
static get
|
|
26
|
+
static get HttpSession() { return HttpSession; }
|
|
27
27
|
|
|
28
28
|
static get HttpUser() { return HttpUser; }
|
|
29
29
|
|
|
@@ -36,6 +36,9 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
36
36
|
#cx;
|
|
37
37
|
get cx() { return this.#cx; }
|
|
38
38
|
|
|
39
|
+
#sdk = {};
|
|
40
|
+
get sdk() { return this.#sdk; }
|
|
41
|
+
|
|
39
42
|
constructor(cx) {
|
|
40
43
|
super();
|
|
41
44
|
if (!(cx instanceof this.constructor.Context)) {
|
|
@@ -163,11 +166,15 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
163
166
|
};
|
|
164
167
|
// Create and route request
|
|
165
168
|
scope.request = this.createRequest(scope.url, scope.init);
|
|
166
|
-
scope.cookies = this.constructor.
|
|
167
|
-
scope.session = this.constructor.
|
|
169
|
+
scope.cookies = this.constructor.HttpCookies.create(scope.request);
|
|
170
|
+
scope.session = this.constructor.HttpSession.create(
|
|
171
|
+
this.#sdk.storage?.('session'),
|
|
172
|
+
scope.request
|
|
173
|
+
);
|
|
168
174
|
const portID = crypto.randomUUID();
|
|
169
175
|
scope.clientMessaging = new ClientMessaging(this, portID, { isPrimary: true });
|
|
170
176
|
scope.user = this.constructor.HttpUser.create(
|
|
177
|
+
this.#sdk.storage?.('user'),
|
|
171
178
|
scope.request,
|
|
172
179
|
scope.session,
|
|
173
180
|
scope.clientMessaging
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpCookies } from '../../HttpCookies.js';
|
|
2
2
|
|
|
3
|
-
export class
|
|
3
|
+
export class WorkerSideCookies extends HttpCookies {
|
|
4
4
|
static create(request) {
|
|
5
5
|
return new this(
|
|
6
6
|
request,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpCookies } from '../HttpCookies.js';
|
|
2
2
|
|
|
3
|
-
export class
|
|
3
|
+
export class ServerSideCookies extends HttpCookies {
|
|
4
4
|
static create(request) {
|
|
5
5
|
return new this(
|
|
6
6
|
request,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpSession } from '../HttpSession.js';
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
export class SessionStorage extends WebfloStorage {
|
|
4
|
+
export class ServerSideSession extends HttpSession {
|
|
6
5
|
|
|
7
|
-
static create(request, params = {}) {
|
|
6
|
+
static create(storageCallback, request, params = {}) {
|
|
8
7
|
let sessionID = request.headers.get('Cookie', true).find((c) => c.name === '__sessid')?.value;
|
|
9
8
|
if (sessionID?.includes('.')) {
|
|
10
9
|
if (params.secret) {
|
|
@@ -30,28 +29,34 @@ export class SessionStorage extends WebfloStorage {
|
|
|
30
29
|
sessionID = crypto.randomUUID();
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
|
-
return new this(
|
|
32
|
+
return new this(
|
|
33
|
+
storageCallback(sessionID),
|
|
34
|
+
request,
|
|
35
|
+
{ sessionID, ...params }
|
|
36
|
+
);
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
#params;
|
|
36
40
|
#sessionID;
|
|
37
41
|
get sessionID() { return this.#sessionID; }
|
|
38
|
-
#ttl;
|
|
39
42
|
|
|
40
|
-
constructor(
|
|
43
|
+
constructor(store, request, { sessionID, ...params } = {}) {
|
|
44
|
+
if (!sessionID) {
|
|
45
|
+
throw new Error(`sessionID is required`);
|
|
46
|
+
}
|
|
41
47
|
super(
|
|
42
|
-
|
|
43
|
-
`session/${sessionID}`,
|
|
48
|
+
store,
|
|
44
49
|
request,
|
|
45
50
|
true
|
|
46
51
|
);
|
|
52
|
+
this.#params = params;
|
|
47
53
|
this.#sessionID = sessionID;
|
|
48
|
-
this.#ttl = ttl;
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
async commit(response = null) {
|
|
52
57
|
if (response && !response.headers.get('Set-Cookie', true).find((c) => c.name === '__sessid')) {
|
|
53
58
|
// expires six months
|
|
54
|
-
response.headers.append('Set-Cookie', `__sessid=${this.#sessionID}; Path=/; Secure; HttpOnly; SameSite=Lax
|
|
59
|
+
response.headers.append('Set-Cookie', `__sessid=${this.#sessionID}; Path=/; Secure; HttpOnly; SameSite=Lax${this.#params.ttl ? `; Max-Age=${this.#params.ttl}` : ''}`);
|
|
55
60
|
}
|
|
56
61
|
await super.commit();
|
|
57
62
|
}
|
|
@@ -15,14 +15,14 @@ import { Readable as _ReadableStream } from 'stream';
|
|
|
15
15
|
import { WebfloRuntime } from '../WebfloRuntime.js';
|
|
16
16
|
import { createWindow } from '@webqit/oohtml-ssr';
|
|
17
17
|
import { Context } from './Context.js';
|
|
18
|
-
import { CookieStorage } from './CookieStorage.js';
|
|
19
|
-
import { SessionStorage } from './SessionStorage.js';
|
|
20
18
|
import { MessagingOverSocket } from '../MessagingOverSocket.js';
|
|
21
19
|
import { ClientMessagingRegistry } from './ClientMessagingRegistry.js';
|
|
20
|
+
import { ServerSideCookies } from './ServerSideCookies.js';
|
|
21
|
+
import { ServerSideSession } from './ServerSideSession.js';
|
|
22
22
|
import { HttpEvent } from '../HttpEvent.js';
|
|
23
23
|
import { HttpUser } from '../HttpUser.js';
|
|
24
24
|
import { Router } from './Router.js';
|
|
25
|
-
import {
|
|
25
|
+
import { pattern } from '../util-url.js';
|
|
26
26
|
import xfetch from '../xfetch.js';
|
|
27
27
|
import '../util-http.js';
|
|
28
28
|
|
|
@@ -37,9 +37,9 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
37
37
|
|
|
38
38
|
static get HttpEvent() { return HttpEvent; }
|
|
39
39
|
|
|
40
|
-
static get
|
|
40
|
+
static get HttpCookies() { return ServerSideCookies; }
|
|
41
41
|
|
|
42
|
-
static get
|
|
42
|
+
static get HttpSession() { return ServerSideSession; }
|
|
43
43
|
|
|
44
44
|
static get HttpUser() { return HttpUser; }
|
|
45
45
|
|
|
@@ -74,10 +74,10 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
74
74
|
|
|
75
75
|
async initialize() {
|
|
76
76
|
process.on('uncaughtException', (err) => {
|
|
77
|
-
console.error('Uncaught
|
|
77
|
+
console.error('Uncaught Exception:', err);
|
|
78
78
|
});
|
|
79
79
|
process.on('unhandledRejection', (reason, promise) => {
|
|
80
|
-
console.log('Unhandled
|
|
80
|
+
console.log('Unhandled Rejection', reason, promise);
|
|
81
81
|
});
|
|
82
82
|
const resolveContextObj = async (cx, force = false) => {
|
|
83
83
|
if (_isEmpty(cx.layout) || force) { cx.layout = await (new cx.config.deployment.Layout(cx)).read(); }
|
|
@@ -218,9 +218,39 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
218
218
|
}
|
|
219
219
|
if (this.#cx.server.capabilities?.redis) {
|
|
220
220
|
const { Redis } = await import('ioredis');
|
|
221
|
-
|
|
221
|
+
const redis = this.env('REDIS_URL')
|
|
222
222
|
? new Redis(this.env('REDIS_URL'))
|
|
223
223
|
: new Redis;
|
|
224
|
+
this.#sdk.redis = redis;
|
|
225
|
+
this.#sdk.storage = (namespace, ttl = null) => ({
|
|
226
|
+
async has(key) { return await redis.hexists(namespace, key); },
|
|
227
|
+
async get(key) {
|
|
228
|
+
const value = await redis.hget(namespace, key);
|
|
229
|
+
return typeof value === 'undefined' ? value : JSON.parse(value);
|
|
230
|
+
},
|
|
231
|
+
async set(key, value) {
|
|
232
|
+
const returnValue = await redis.hset(namespace, key, JSON.stringify(value));
|
|
233
|
+
if (!this.ttlApplied && ttl) {
|
|
234
|
+
await redis.expire(namespace, ttl);
|
|
235
|
+
this.ttlApplied = true;
|
|
236
|
+
}
|
|
237
|
+
return returnValue;
|
|
238
|
+
},
|
|
239
|
+
async delete(key) { return await redis.hdel(namespace, key); },
|
|
240
|
+
async clear() { return await redis.del(namespace); },
|
|
241
|
+
async keys() { return await redis.hkeys(namespace); },
|
|
242
|
+
async values() { return await redis.hvals(namespace); },
|
|
243
|
+
async entries() { return Object.entries(await redis.hgetall(namespace) || {}); },
|
|
244
|
+
get size() { return redis.hlen(namespace); },
|
|
245
|
+
});
|
|
246
|
+
} else {
|
|
247
|
+
const inmemSessionRegistry = new Map;
|
|
248
|
+
this.#sdk.storage = (namespace) => {
|
|
249
|
+
if (!inmemSessionRegistry.has(namespace)) {
|
|
250
|
+
inmemSessionRegistry.set(namespace, new Map);
|
|
251
|
+
}
|
|
252
|
+
return inmemSessionRegistry.get(namespace);
|
|
253
|
+
};
|
|
224
254
|
}
|
|
225
255
|
if (this.#cx.server.capabilities?.webpush) {
|
|
226
256
|
const { default: webpush } = await import('web-push');
|
|
@@ -271,14 +301,15 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
271
301
|
// Level 3 validation
|
|
272
302
|
// and actual processing
|
|
273
303
|
scope.request = this.createRequest(scope.url.href, requestInit);
|
|
274
|
-
scope.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
304
|
+
scope.sessionTTL = this.env('SESSION_TTL') || 2592000/*30days*/;
|
|
305
|
+
scope.session = this.constructor.HttpSession.create(
|
|
306
|
+
(sessionID) => this.#sdk.storage?.(`${scope.url.host}/session:${sessionID}`, scope.sessionTTL),
|
|
307
|
+
scope.request,
|
|
308
|
+
{
|
|
309
|
+
secret: this.env('SESSION_KEY'),
|
|
310
|
+
ttl: scope.sessionTTL,
|
|
311
|
+
}
|
|
312
|
+
);
|
|
282
313
|
if (!scope.error) {
|
|
283
314
|
if (!(scope.clientMessagingRegistry = this.#globalMessagingRegistry.get(scope.session.sessionID))) {
|
|
284
315
|
scope.error = `Lost or invalid clientID`;
|
|
@@ -576,15 +607,16 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
576
607
|
? ((await (new this.#cx.config.runtime.server.Headers(this.#cx)).read()).entries || []).filter(entry => pattern(entry.url, url.origin).exec(url.href))
|
|
577
608
|
: [];
|
|
578
609
|
scope.request = this.createRequest(scope.url.href, scope.init, scope.autoHeaders.filter((header) => header.type === 'request'));
|
|
579
|
-
scope.cookies = this.constructor.
|
|
580
|
-
scope.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
610
|
+
scope.cookies = this.constructor.HttpCookies.create(scope.request);
|
|
611
|
+
scope.sessionTTL = this.env('SESSION_TTL') || 2592000/*30days*/;
|
|
612
|
+
scope.session = this.constructor.HttpSession.create(
|
|
613
|
+
(sessionID) => this.#sdk.storage?.(`${scope.url.host}/session:${sessionID}`, scope.sessionTTL),
|
|
614
|
+
scope.request,
|
|
615
|
+
{
|
|
616
|
+
secret: this.env('SESSION_KEY'),
|
|
617
|
+
ttl: scope.sessionTTL,
|
|
618
|
+
}
|
|
619
|
+
);
|
|
588
620
|
const sessionID = scope.session.sessionID;
|
|
589
621
|
if (!this.#globalMessagingRegistry.has(sessionID)) {
|
|
590
622
|
this.#globalMessagingRegistry.set(sessionID, new ClientMessagingRegistry(this, sessionID));
|
|
@@ -592,6 +624,7 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
592
624
|
scope.clientMessagingRegistry = this.#globalMessagingRegistry.get(sessionID);
|
|
593
625
|
scope.clientMessaging = scope.clientMessagingRegistry.createPort();
|
|
594
626
|
scope.user = this.constructor.HttpUser.create(
|
|
627
|
+
this.#sdk.storage?.(`${scope.url.host}/user:${scope.session.sessionID}`, scope.sessionTTL),
|
|
595
628
|
scope.request,
|
|
596
629
|
scope.session,
|
|
597
630
|
scope.clientMessaging
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { WebfloStorage } from '../WebfloStorage.js';
|
|
2
|
-
|
|
3
|
-
export class SessionStorage extends WebfloStorage {
|
|
4
|
-
static create(request) {
|
|
5
|
-
const registry = {
|
|
6
|
-
async get(key) { return localStorage.getItem(key) },
|
|
7
|
-
async set(key, value) { return localStorage.setItem(key, value) },
|
|
8
|
-
};
|
|
9
|
-
return new this(
|
|
10
|
-
registry,
|
|
11
|
-
'session',
|
|
12
|
-
request,
|
|
13
|
-
true
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
}
|