@signe/room 2.9.4 → 3.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/dist/chunk-EUXUH3YW.js +15 -0
- package/dist/chunk-EUXUH3YW.js.map +1 -0
- package/dist/cloudflare/index.d.ts +71 -0
- package/dist/cloudflare/index.js +320 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/index.d.ts +65 -188
- package/dist/index.js +742 -146
- package/dist/index.js.map +1 -1
- package/dist/node/index.d.ts +164 -0
- package/dist/node/index.js +786 -0
- package/dist/node/index.js.map +1 -0
- package/dist/party-dNs-hqkq.d.ts +175 -0
- package/examples/cloudflare/README.md +62 -0
- package/examples/cloudflare/node_modules/.bin/tsc +17 -0
- package/examples/cloudflare/node_modules/.bin/tsserver +17 -0
- package/examples/cloudflare/node_modules/.bin/wrangler +17 -0
- package/examples/cloudflare/node_modules/.bin/wrangler2 +17 -0
- package/examples/cloudflare/package.json +24 -0
- package/examples/cloudflare/public/index.html +443 -0
- package/examples/cloudflare/src/index.ts +28 -0
- package/examples/cloudflare/src/room.ts +44 -0
- package/examples/cloudflare/tsconfig.json +10 -0
- package/examples/cloudflare/wrangler.jsonc +25 -0
- package/examples/node/README.md +57 -0
- package/examples/node/node_modules/.bin/tsc +17 -0
- package/examples/node/node_modules/.bin/tsserver +17 -0
- package/examples/node/node_modules/.bin/tsx +17 -0
- package/examples/node/package.json +23 -0
- package/examples/node/public/index.html +443 -0
- package/examples/node/room.ts +44 -0
- package/examples/node/server.sqlite.ts +52 -0
- package/examples/node/server.ts +51 -0
- package/examples/node/tsconfig.json +10 -0
- package/examples/node-game/README.md +66 -0
- package/examples/node-game/package.json +23 -0
- package/examples/node-game/public/index.html +705 -0
- package/examples/node-game/room.ts +145 -0
- package/examples/node-game/server.sqlite.ts +54 -0
- package/examples/node-game/server.ts +53 -0
- package/examples/node-game/tsconfig.json +10 -0
- package/examples/node-shard/README.md +32 -0
- package/examples/node-shard/dev.ts +39 -0
- package/examples/node-shard/package.json +24 -0
- package/examples/node-shard/public/index.html +777 -0
- package/examples/node-shard/room-server.ts +68 -0
- package/examples/node-shard/room.ts +105 -0
- package/examples/node-shard/shared.ts +6 -0
- package/examples/node-shard/tsconfig.json +14 -0
- package/examples/node-shard/world-server.ts +169 -0
- package/package.json +14 -5
- package/readme.md +377 -11
- package/src/cloudflare/index.ts +474 -0
- package/src/mock.ts +29 -7
- package/src/node/index.ts +1112 -0
- package/src/server.ts +626 -90
- package/src/session.guard.ts +6 -2
- package/src/shard.ts +91 -23
- package/src/storage.ts +29 -5
- package/src/testing.ts +4 -3
- package/src/types/party.ts +4 -1
- package/src/world.guard.ts +23 -4
- package/src/world.ts +170 -79
- package/examples/game/.vscode/launch.json +0 -11
- package/examples/game/.vscode/settings.json +0 -11
- package/examples/game/README.md +0 -40
- package/examples/game/app/client.tsx +0 -15
- package/examples/game/app/components/Admin.tsx +0 -1089
- package/examples/game/app/components/Room.tsx +0 -162
- package/examples/game/app/styles.css +0 -31
- package/examples/game/package-lock.json +0 -225
- package/examples/game/package.json +0 -20
- package/examples/game/party/game.room.ts +0 -32
- package/examples/game/party/server.ts +0 -10
- package/examples/game/party/shard.ts +0 -5
- package/examples/game/partykit.json +0 -14
- package/examples/game/public/favicon.ico +0 -0
- package/examples/game/public/index.html +0 -27
- package/examples/game/public/normalize.css +0 -351
- package/examples/game/shared/room.schema.ts +0 -14
- package/examples/game/tsconfig.json +0 -109
package/src/session.guard.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type * as Party from "./types/party";
|
|
2
2
|
|
|
3
|
+
function getPrivateId(sender: Party.Connection) {
|
|
4
|
+
return sender.sessionId || sender.id;
|
|
5
|
+
}
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* @description Factory function that creates a session guard with access to room storage
|
|
5
9
|
* @param {Party.Storage} storage - The room storage instance
|
|
@@ -29,7 +33,7 @@ export function createRequireSessionGuard(storage: Party.Storage) {
|
|
|
29
33
|
|
|
30
34
|
try {
|
|
31
35
|
// Check if session exists in storage
|
|
32
|
-
const session = await storage.get(`session:${sender
|
|
36
|
+
const session = await storage.get(`session:${getPrivateId(sender)}`);
|
|
33
37
|
|
|
34
38
|
// Return false if no session found
|
|
35
39
|
if (!session) {
|
|
@@ -88,7 +92,7 @@ export const requireSession = async (sender: Party.Connection, value: any, room:
|
|
|
88
92
|
|
|
89
93
|
try {
|
|
90
94
|
// Check if session exists in storage
|
|
91
|
-
const session = await room.storage.get(`session:${sender
|
|
95
|
+
const session = await room.storage.get(`session:${getPrivateId(sender)}`);
|
|
92
96
|
|
|
93
97
|
// Return false if no session found
|
|
94
98
|
if (!session) {
|
package/src/shard.ts
CHANGED
|
@@ -15,18 +15,44 @@ export interface ShardOptions {
|
|
|
15
15
|
|
|
16
16
|
export class Shard {
|
|
17
17
|
ws: PartyWebSocket;
|
|
18
|
-
connectionMap = new Map<string, Party.Connection
|
|
18
|
+
connectionMap = new Map<string, Set<Party.Connection>>(); // Map privateId -> active connections
|
|
19
19
|
mainServerStub: any;
|
|
20
20
|
worldUrl: string | null = null;
|
|
21
|
-
worldId: string
|
|
21
|
+
worldId: string;
|
|
22
22
|
lastReportedConnections: number = 0;
|
|
23
23
|
statsInterval: number = 30000;
|
|
24
24
|
statsIntervalId: any = null;
|
|
25
25
|
|
|
26
|
-
constructor(private room: Party.Room) {
|
|
26
|
+
constructor(private room: Party.Room, options: ShardOptions = {}) {
|
|
27
|
+
this.worldUrl = options.worldUrl ?? null;
|
|
28
|
+
this.worldId = options.worldId
|
|
29
|
+
?? this.getWorldIdFromShardId(room.id)
|
|
30
|
+
?? this.getEnvString('WORLD_ID')
|
|
31
|
+
?? this.getEnvString('SIGNE_WORLD_ID')
|
|
32
|
+
?? 'world-default';
|
|
33
|
+
this.statsInterval = options.statsInterval ?? this.statsInterval;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private getPrivateId(conn: Party.Connection) {
|
|
37
|
+
return conn.sessionId || conn.id;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private getEnvString(key: string) {
|
|
41
|
+
const value = this.room.env?.[key];
|
|
42
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private getRoomIdFromShardId(shardId: string) {
|
|
46
|
+
return shardId.split(':')[0];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private getWorldIdFromShardId(shardId: string) {
|
|
50
|
+
const parts = shardId.split(':');
|
|
51
|
+
return parts.length >= 3 ? parts[1] : undefined;
|
|
52
|
+
}
|
|
27
53
|
|
|
28
54
|
async onStart() {
|
|
29
|
-
const roomId = this.room.id
|
|
55
|
+
const roomId = this.getRoomIdFromShardId(this.room.id);
|
|
30
56
|
const roomStub = this.room.context.parties.main.get(roomId);
|
|
31
57
|
if (!roomStub) {
|
|
32
58
|
console.warn('No room room stub found in main party context');
|
|
@@ -36,7 +62,9 @@ export class Shard {
|
|
|
36
62
|
this.mainServerStub = roomStub;
|
|
37
63
|
this.ws = await roomStub.socket({
|
|
38
64
|
headers: {
|
|
39
|
-
'x-shard-id': this.room.id
|
|
65
|
+
'x-shard-id': this.room.id,
|
|
66
|
+
'x-shard-world-id': this.worldId,
|
|
67
|
+
'x-access-shard': this.room.env.SHARD_SECRET as string
|
|
40
68
|
}
|
|
41
69
|
}) as unknown as PartyWebSocket;
|
|
42
70
|
|
|
@@ -45,13 +73,25 @@ export class Shard {
|
|
|
45
73
|
try {
|
|
46
74
|
const message = JSON.parse(event.data);
|
|
47
75
|
|
|
76
|
+
if (message.type === 'shard.closeClient' && message.privateId) {
|
|
77
|
+
const clientConnections = this.connectionMap.get(message.privateId);
|
|
78
|
+
if (clientConnections?.size) {
|
|
79
|
+
for (const clientConn of [...clientConnections]) {
|
|
80
|
+
clientConn.close();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
48
86
|
// If the message is directed to a specific client, forward it
|
|
49
87
|
if (message.targetClientId) {
|
|
50
|
-
const
|
|
51
|
-
if (
|
|
88
|
+
const clientConnections = this.connectionMap.get(message.targetClientId);
|
|
89
|
+
if (clientConnections?.size) {
|
|
52
90
|
// Remove the routing information before forwarding
|
|
53
91
|
delete message.targetClientId;
|
|
54
|
-
clientConn
|
|
92
|
+
for (const clientConn of clientConnections) {
|
|
93
|
+
clientConn.send(message.data);
|
|
94
|
+
}
|
|
55
95
|
}
|
|
56
96
|
} else {
|
|
57
97
|
// Broadcast to all clients if no specific target
|
|
@@ -62,12 +102,12 @@ export class Shard {
|
|
|
62
102
|
}
|
|
63
103
|
});
|
|
64
104
|
|
|
65
|
-
await this.updateWorldStats();
|
|
105
|
+
await this.updateWorldStats(true);
|
|
66
106
|
this.startPeriodicStatsUpdates();
|
|
67
107
|
}
|
|
68
108
|
|
|
69
109
|
private startPeriodicStatsUpdates() {
|
|
70
|
-
if (!this.
|
|
110
|
+
if (this.statsInterval <= 0 || !this.room.context.parties.world) {
|
|
71
111
|
return;
|
|
72
112
|
}
|
|
73
113
|
|
|
@@ -76,10 +116,11 @@ export class Shard {
|
|
|
76
116
|
}
|
|
77
117
|
|
|
78
118
|
this.statsIntervalId = setInterval(() => {
|
|
79
|
-
this.updateWorldStats().catch(error => {
|
|
119
|
+
this.updateWorldStats(true).catch(error => {
|
|
80
120
|
console.error('Error in periodic stats update:', error);
|
|
81
121
|
});
|
|
82
122
|
}, this.statsInterval);
|
|
123
|
+
this.statsIntervalId?.unref?.();
|
|
83
124
|
}
|
|
84
125
|
|
|
85
126
|
private stopPeriodicStatsUpdates() {
|
|
@@ -90,8 +131,12 @@ export class Shard {
|
|
|
90
131
|
}
|
|
91
132
|
|
|
92
133
|
onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {
|
|
134
|
+
const privateId = this.getPrivateId(conn);
|
|
135
|
+
|
|
93
136
|
// Store connection mapping
|
|
94
|
-
this.connectionMap.
|
|
137
|
+
const connections = this.connectionMap.get(privateId) ?? new Set<Party.Connection>();
|
|
138
|
+
connections.add(conn);
|
|
139
|
+
this.connectionMap.set(privateId, connections);
|
|
95
140
|
|
|
96
141
|
// Capture all headers and request information
|
|
97
142
|
const headers: Record<string, string> = {};
|
|
@@ -111,7 +156,7 @@ export class Shard {
|
|
|
111
156
|
// Notify the main server about the new connection with complete connection metadata
|
|
112
157
|
this.ws.send(JSON.stringify({
|
|
113
158
|
type: 'shard.clientConnected',
|
|
114
|
-
privateId
|
|
159
|
+
privateId,
|
|
115
160
|
requestInfo
|
|
116
161
|
}));
|
|
117
162
|
|
|
@@ -126,7 +171,7 @@ export class Shard {
|
|
|
126
171
|
// Wrap the original message with sender information
|
|
127
172
|
const wrappedMessage = JSON.stringify({
|
|
128
173
|
type: 'shard.clientMessage',
|
|
129
|
-
privateId: sender
|
|
174
|
+
privateId: this.getPrivateId(sender),
|
|
130
175
|
publicId: (sender.state as any)?.publicId,
|
|
131
176
|
payload: parsedMessage
|
|
132
177
|
});
|
|
@@ -139,28 +184,48 @@ export class Shard {
|
|
|
139
184
|
}
|
|
140
185
|
|
|
141
186
|
onClose(conn: Party.Connection) {
|
|
187
|
+
const privateId = this.getPrivateId(conn);
|
|
188
|
+
|
|
142
189
|
// Remove connection from the map
|
|
143
|
-
this.connectionMap.
|
|
190
|
+
const connections = this.connectionMap.get(privateId);
|
|
191
|
+
connections?.delete(conn);
|
|
192
|
+
|
|
193
|
+
if (connections?.size) {
|
|
194
|
+
this.updateWorldStats();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.connectionMap.delete(privateId);
|
|
144
199
|
|
|
145
200
|
// Notify main server about disconnection
|
|
146
201
|
this.ws.send(JSON.stringify({
|
|
147
202
|
type: 'shard.clientDisconnected',
|
|
148
|
-
privateId
|
|
203
|
+
privateId,
|
|
149
204
|
publicId: (conn.state as any)?.publicId
|
|
150
205
|
}));
|
|
151
206
|
|
|
152
207
|
this.updateWorldStats();
|
|
153
208
|
}
|
|
154
209
|
|
|
155
|
-
async updateWorldStats(): Promise<boolean> {
|
|
156
|
-
const currentConnections = this.connectionMap.
|
|
210
|
+
async updateWorldStats(force = false): Promise<boolean> {
|
|
211
|
+
const currentConnections = Array.from(this.connectionMap.values())
|
|
212
|
+
.reduce((total, connections) => total + connections.size, 0);
|
|
157
213
|
|
|
158
|
-
if (currentConnections === this.lastReportedConnections) {
|
|
214
|
+
if (!force && currentConnections === this.lastReportedConnections) {
|
|
159
215
|
return true;
|
|
160
216
|
}
|
|
161
217
|
|
|
162
218
|
try {
|
|
163
|
-
const
|
|
219
|
+
const worldParty = this.room.context.parties.world;
|
|
220
|
+
if (!worldParty) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const worldRoom = worldParty.get(this.worldId);
|
|
225
|
+
if (!worldRoom?.fetch) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
164
229
|
const response = await worldRoom.fetch('/update-shard', {
|
|
165
230
|
method: 'POST',
|
|
166
231
|
headers: {
|
|
@@ -169,6 +234,7 @@ export class Shard {
|
|
|
169
234
|
},
|
|
170
235
|
body: JSON.stringify({
|
|
171
236
|
shardId: this.room.id,
|
|
237
|
+
worldId: this.worldId,
|
|
172
238
|
connections: currentConnections
|
|
173
239
|
})
|
|
174
240
|
});
|
|
@@ -219,7 +285,9 @@ export class Shard {
|
|
|
219
285
|
|
|
220
286
|
// Add shard identification
|
|
221
287
|
headers.set('x-shard-id', this.room.id);
|
|
288
|
+
headers.set('x-shard-world-id', this.worldId);
|
|
222
289
|
headers.set('x-forwarded-by-shard', 'true');
|
|
290
|
+
headers.set('x-access-shard', this.room.env.SHARD_SECRET as string);
|
|
223
291
|
|
|
224
292
|
// Client IP tracking for the main server
|
|
225
293
|
const clientIp = req.headers.get('x-forwarded-for') || 'unknown';
|
|
@@ -234,7 +302,7 @@ export class Shard {
|
|
|
234
302
|
body
|
|
235
303
|
};
|
|
236
304
|
// Forward the request to the main server
|
|
237
|
-
const response = await this.mainServerStub.fetch(path, requestInit);
|
|
305
|
+
const response = await this.mainServerStub.fetch(path + url.search, requestInit);
|
|
238
306
|
return response;
|
|
239
307
|
} catch (error) {
|
|
240
308
|
return response(500, { error: 'Error forwarding request' });
|
|
@@ -247,8 +315,8 @@ export class Shard {
|
|
|
247
315
|
* @description Executed periodically, used to perform maintenance tasks
|
|
248
316
|
*/
|
|
249
317
|
async onAlarm() {
|
|
250
|
-
await this.updateWorldStats();
|
|
318
|
+
await this.updateWorldStats(true);
|
|
251
319
|
}
|
|
252
320
|
}
|
|
253
321
|
|
|
254
|
-
Shard satisfies Party.Worker;
|
|
322
|
+
Shard satisfies Party.Worker;
|
package/src/storage.ts
CHANGED
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
export class Storage {
|
|
2
2
|
private memory = new Map();
|
|
3
|
-
async put(key, value) {
|
|
4
|
-
|
|
3
|
+
async put(key, value?) {
|
|
4
|
+
if (typeof key === "string") {
|
|
5
|
+
this.memory.set(key, value);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
for (const [entryKey, entryValue] of Object.entries(key)) {
|
|
10
|
+
this.memory.set(entryKey, entryValue);
|
|
11
|
+
}
|
|
5
12
|
}
|
|
6
13
|
async get(key) {
|
|
7
14
|
return this.memory.get(key);
|
|
8
15
|
}
|
|
9
16
|
async delete(key) {
|
|
10
|
-
|
|
17
|
+
if (Array.isArray(key)) {
|
|
18
|
+
let deleted = 0;
|
|
19
|
+
for (const entryKey of key) {
|
|
20
|
+
if (this.memory.delete(entryKey)) {
|
|
21
|
+
deleted += 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return deleted;
|
|
25
|
+
}
|
|
26
|
+
return this.memory.delete(key);
|
|
11
27
|
}
|
|
12
|
-
async list() {
|
|
13
|
-
|
|
28
|
+
async list(options?: { prefix?: string }) {
|
|
29
|
+
if (!options?.prefix) {
|
|
30
|
+
return this.memory;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return new Map(
|
|
34
|
+
Array.from(this.memory.entries()).filter(([key]) =>
|
|
35
|
+
String(key).startsWith(options.prefix!)
|
|
36
|
+
)
|
|
37
|
+
);
|
|
14
38
|
}
|
|
15
39
|
}
|
package/src/testing.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { Shard } from "./shard"
|
|
|
30
30
|
export async function testRoom(Room, options: {
|
|
31
31
|
hibernate?: boolean,
|
|
32
32
|
shard?: boolean,
|
|
33
|
+
id?: string,
|
|
33
34
|
env?: Record<string, string>,
|
|
34
35
|
parties?: Record<string, (io: any) => any>,
|
|
35
36
|
partyFn?: (io: any) => any
|
|
@@ -42,7 +43,7 @@ export async function testRoom(Room, options: {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
const isShard = options.shard || false
|
|
45
|
-
const io = new ServerIo(Room.path, isShard ? {
|
|
46
|
+
const io = new ServerIo(options.id ?? Room.path, isShard ? {
|
|
46
47
|
parties: {
|
|
47
48
|
game: createServer,
|
|
48
49
|
...(options.parties || {})
|
|
@@ -92,7 +93,7 @@ export async function testRoom(Room, options: {
|
|
|
92
93
|
return client
|
|
93
94
|
},
|
|
94
95
|
getServerUser: async (client: any, prop = 'users') => {
|
|
95
|
-
const privateId = client.conn.id;
|
|
96
|
+
const privateId = client.conn.sessionId || client.conn.id;
|
|
96
97
|
const session = await (server as Server).getSession(privateId);
|
|
97
98
|
return (server as any).subRoom[prop]()[session?.publicId];
|
|
98
99
|
}
|
|
@@ -114,4 +115,4 @@ export async function request(room: Server | Shard, path: string, options: {
|
|
|
114
115
|
|
|
115
116
|
export function tick(ms: number = 0) {
|
|
116
117
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
117
|
-
}
|
|
118
|
+
}
|
package/src/types/party.ts
CHANGED
|
@@ -150,6 +150,9 @@ import type {
|
|
|
150
150
|
export type Connection<TState = unknown> = WebSocket & {
|
|
151
151
|
/** Connection identifier */
|
|
152
152
|
id: string;
|
|
153
|
+
|
|
154
|
+
/** Stable private session identifier shared by reconnects or multiple tabs. */
|
|
155
|
+
sessionId?: string;
|
|
153
156
|
|
|
154
157
|
/** @deprecated You can access the socket properties directly on the connection*/
|
|
155
158
|
socket: WebSocket;
|
|
@@ -548,4 +551,4 @@ import type {
|
|
|
548
551
|
export type PartyKitConnection = Connection;
|
|
549
552
|
|
|
550
553
|
/** @deprecated Use `Party.ServerOptions` instead */
|
|
551
|
-
export type PartyServerOptions = ServerOptions;
|
|
554
|
+
export type PartyServerOptions = ServerOptions;
|
package/src/world.guard.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as Party from "./types/party";
|
|
2
2
|
import { JWTAuth } from "./jwt";
|
|
3
|
-
import { response } from "./utils";
|
|
4
3
|
|
|
5
|
-
export const guardManageWorld = async (_, req: Party.Request, room: Party.Room): Promise<boolean> => {
|
|
4
|
+
export const guardManageWorld = async (_: unknown, req: Party.Request, room: Party.Room): Promise<boolean> => {
|
|
6
5
|
const tokenShard = req.headers.get("x-access-shard");
|
|
7
6
|
if (tokenShard) {
|
|
8
7
|
if (tokenShard !== room.env.SHARD_SECRET) {
|
|
@@ -11,7 +10,7 @@ export const guardManageWorld = async (_, req: Party.Request, room: Party.Room):
|
|
|
11
10
|
return true
|
|
12
11
|
}
|
|
13
12
|
const url = new URL(req.url);
|
|
14
|
-
const token = req
|
|
13
|
+
const token = getAuthToken(req, url);
|
|
15
14
|
if (!token) {
|
|
16
15
|
return false;
|
|
17
16
|
}
|
|
@@ -21,8 +20,28 @@ export const guardManageWorld = async (_, req: Party.Request, room: Party.Room):
|
|
|
21
20
|
if (!payload) {
|
|
22
21
|
return false;
|
|
23
22
|
}
|
|
23
|
+
if (!canAccessWorld(payload, room.id)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
24
26
|
} catch (error) {
|
|
25
27
|
return false;
|
|
26
28
|
}
|
|
27
29
|
return true;
|
|
28
|
-
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getAuthToken(req: Party.Request, url: URL) {
|
|
33
|
+
const authorization = req.headers.get("Authorization");
|
|
34
|
+
if (authorization?.startsWith("Bearer ")) {
|
|
35
|
+
return authorization.slice("Bearer ".length).trim();
|
|
36
|
+
}
|
|
37
|
+
return authorization ?? url.searchParams.get("world-auth-token");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function canAccessWorld(payload: Record<string, unknown>, worldId: string) {
|
|
41
|
+
const worlds = payload.worlds;
|
|
42
|
+
if (!Array.isArray(worlds)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return worlds.some((world) => world === "*" || world === worldId);
|
|
47
|
+
}
|