@arcote.tech/arc-host 0.1.4 → 0.1.6
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/dist/host.d.ts +5 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/index.js +126 -12
- package/host.ts +133 -10
- package/package.json +1 -1
package/dist/host.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ declare class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
15
15
|
* Convert JWT payload to AuthContext
|
|
16
16
|
*/
|
|
17
17
|
private tokenToAuthContext;
|
|
18
|
+
/**
|
|
19
|
+
* Extract client IP address from request headers
|
|
20
|
+
*/
|
|
21
|
+
private getClientIpAddress;
|
|
18
22
|
/**
|
|
19
23
|
* Get default auth context for anonymous users
|
|
20
24
|
*/
|
|
@@ -29,6 +33,7 @@ declare class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
29
33
|
private setNestedProperty;
|
|
30
34
|
private handleCommand;
|
|
31
35
|
private handleQuery;
|
|
36
|
+
private handleRoute;
|
|
32
37
|
private setupServer;
|
|
33
38
|
private isPublicEndpoint;
|
|
34
39
|
private handleSync;
|
package/dist/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../host.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,aAAa,EAElB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAGvB,KAAK,4BAA4B,EAClC,MAAM,kBAAkB,CAAC;AAI1B,cAAM,OAAQ,YAAW,4BAA4B;IAMjD,OAAO,CAAC,OAAO;IALjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,KAAK,CAAuB;gBAG1B,OAAO,EAAE,aAAa,EAC9B,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC;IASrC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,IAAI;IAI5C,IAAI,CACR,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,IAAI,GACL,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,IAAI,GACT,OAAO,CAAC,IAAI,CAAC;YAIF,eAAe;
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../host.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,aAAa,EAElB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAGvB,KAAK,4BAA4B,EAClC,MAAM,kBAAkB,CAAC;AAI1B,cAAM,OAAQ,YAAW,4BAA4B;IAMjD,OAAO,CAAC,OAAO;IALjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,KAAK,CAAuB;gBAG1B,OAAO,EAAE,aAAa,EAC9B,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC;IASrC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,IAAI;IAI5C,IAAI,CACR,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,IAAI,GACL,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,IAAI,GACT,OAAO,CAAC,IAAI,CAAC;YAIF,eAAe;IAS7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;OAEG;YACW,qBAAqB;IAWnC;;OAEG;IACH,OAAO,CAAC,iBAAiB;YAwCX,aAAa;YAoDb,WAAW;YAiEX,WAAW;IAgEzB,OAAO,CAAC,WAAW;IAmEnB,OAAO,CAAC,gBAAgB;YA4BV,UAAU;YA+CV,SAAS;IAevB,OAAO,CAAC,cAAc;CAGvB;AAED,eAAO,MAAM,cAAc,YACf,aAAa,aAAa,OAAO,CAAC,eAAe,CAAC,kBAE3D,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4904,6 +4904,7 @@ var constructorString = Object.prototype.constructor.toString();
|
|
|
4904
4904
|
class ArcContextElement {
|
|
4905
4905
|
$event;
|
|
4906
4906
|
name;
|
|
4907
|
+
queryBuilder;
|
|
4907
4908
|
commandContext;
|
|
4908
4909
|
commandClient;
|
|
4909
4910
|
observer;
|
|
@@ -5632,7 +5633,7 @@ class ArcFindQuery extends ArcCollectionQuery {
|
|
|
5632
5633
|
}
|
|
5633
5634
|
onChange(change) {
|
|
5634
5635
|
const lastResult = this.lastResult;
|
|
5635
|
-
const lastResultAsArray = lastResult
|
|
5636
|
+
const lastResultAsArray = lastResult || [];
|
|
5636
5637
|
const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.id));
|
|
5637
5638
|
const isInLastResult = index !== -1;
|
|
5638
5639
|
const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
|
|
@@ -6654,6 +6655,40 @@ class Model extends ModelBase {
|
|
|
6654
6655
|
}
|
|
6655
6656
|
});
|
|
6656
6657
|
}
|
|
6658
|
+
routes(authContext) {
|
|
6659
|
+
return new Proxy({}, {
|
|
6660
|
+
get: (_, name) => {
|
|
6661
|
+
const element3 = this.context.elements.find((element4) => element4.name === name);
|
|
6662
|
+
if (!element3) {
|
|
6663
|
+
throw new Error(`Route element "${String(name)}" not found`);
|
|
6664
|
+
}
|
|
6665
|
+
if (typeof element3.getHandler !== "function") {
|
|
6666
|
+
throw new Error(`Element "${String(name)}" does not have route handlers`);
|
|
6667
|
+
}
|
|
6668
|
+
return async (method, req, routeParams, url) => {
|
|
6669
|
+
const handler = element3.getHandler(method);
|
|
6670
|
+
if (!handler) {
|
|
6671
|
+
throw new Error(`Method ${method} not allowed for route ${String(name)}`);
|
|
6672
|
+
}
|
|
6673
|
+
const forkedDataStorage = this.dataStorage.fork();
|
|
6674
|
+
const eventPublisher = new EventPublisher(this.context, this.dataStorage, authContext);
|
|
6675
|
+
const publishEvent = async (event3) => {
|
|
6676
|
+
await eventPublisher.publishEvent(event3, forkedDataStorage);
|
|
6677
|
+
};
|
|
6678
|
+
const commandContext = this.context.commandContext(forkedDataStorage, publishEvent, authContext);
|
|
6679
|
+
try {
|
|
6680
|
+
const result = await handler(commandContext, req, routeParams, url);
|
|
6681
|
+
await forkedDataStorage.merge();
|
|
6682
|
+
eventPublisher.runAsyncListeners();
|
|
6683
|
+
return result;
|
|
6684
|
+
} catch (error) {
|
|
6685
|
+
this.catchErrorCallback(error);
|
|
6686
|
+
throw error;
|
|
6687
|
+
}
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
});
|
|
6691
|
+
}
|
|
6657
6692
|
get $debug() {
|
|
6658
6693
|
return {};
|
|
6659
6694
|
}
|
|
@@ -6719,20 +6754,36 @@ class RTCHost {
|
|
|
6719
6754
|
const payload = verifyToken(token);
|
|
6720
6755
|
return payload;
|
|
6721
6756
|
} catch (error) {
|
|
6722
|
-
console.error("Token verification failed:", error);
|
|
6723
6757
|
return null;
|
|
6724
6758
|
}
|
|
6725
6759
|
}
|
|
6726
|
-
tokenToAuthContext(payload) {
|
|
6760
|
+
tokenToAuthContext(payload, ipAddress) {
|
|
6727
6761
|
return {
|
|
6728
6762
|
userId: payload.userId,
|
|
6729
|
-
roles: []
|
|
6763
|
+
roles: [],
|
|
6764
|
+
ipAddress
|
|
6730
6765
|
};
|
|
6731
6766
|
}
|
|
6732
|
-
|
|
6767
|
+
getClientIpAddress(req) {
|
|
6768
|
+
const xForwardedFor = req.headers.get("x-forwarded-for");
|
|
6769
|
+
const xRealIp = req.headers.get("x-real-ip");
|
|
6770
|
+
const cfConnectingIp = req.headers.get("cf-connecting-ip");
|
|
6771
|
+
if (xForwardedFor) {
|
|
6772
|
+
return xForwardedFor.split(",")[0].trim();
|
|
6773
|
+
}
|
|
6774
|
+
if (xRealIp) {
|
|
6775
|
+
return xRealIp;
|
|
6776
|
+
}
|
|
6777
|
+
if (cfConnectingIp) {
|
|
6778
|
+
return cfConnectingIp;
|
|
6779
|
+
}
|
|
6780
|
+
return;
|
|
6781
|
+
}
|
|
6782
|
+
getDefaultAuthContext(ipAddress) {
|
|
6733
6783
|
return {
|
|
6734
|
-
userId:
|
|
6735
|
-
roles: []
|
|
6784
|
+
userId: undefined,
|
|
6785
|
+
roles: [],
|
|
6786
|
+
ipAddress
|
|
6736
6787
|
};
|
|
6737
6788
|
}
|
|
6738
6789
|
async parseFormDataToObject(formData) {
|
|
@@ -6784,15 +6835,16 @@ class RTCHost {
|
|
|
6784
6835
|
try {
|
|
6785
6836
|
const authHeader = req.headers.get("Authorization");
|
|
6786
6837
|
const token = authHeader?.replace("Bearer ", "");
|
|
6838
|
+
const clientIp = this.getClientIpAddress(req);
|
|
6787
6839
|
let authContext;
|
|
6788
6840
|
if (token && !this.isPublicEndpoint(url.pathname)) {
|
|
6789
6841
|
const payload = await this.verifyAuthToken(token);
|
|
6790
6842
|
if (!payload) {
|
|
6791
6843
|
return new Response("Invalid or expired token", { status: 401 });
|
|
6792
6844
|
}
|
|
6793
|
-
authContext = this.tokenToAuthContext(payload);
|
|
6845
|
+
authContext = this.tokenToAuthContext(payload, clientIp);
|
|
6794
6846
|
} else {
|
|
6795
|
-
authContext = this.getDefaultAuthContext();
|
|
6847
|
+
authContext = this.getDefaultAuthContext(clientIp);
|
|
6796
6848
|
}
|
|
6797
6849
|
let argument;
|
|
6798
6850
|
const contentType = req.headers.get("Content-Type");
|
|
@@ -6817,13 +6869,14 @@ class RTCHost {
|
|
|
6817
6869
|
try {
|
|
6818
6870
|
const authHeader = req.headers.get("Authorization");
|
|
6819
6871
|
const token = authHeader?.replace("Bearer ", "");
|
|
6872
|
+
const clientIp = this.getClientIpAddress(req);
|
|
6820
6873
|
let authContext;
|
|
6821
6874
|
if (token) {
|
|
6822
6875
|
const payload = await this.verifyAuthToken(token);
|
|
6823
6876
|
if (!payload) {
|
|
6824
6877
|
return new Response("Invalid or expired token", { status: 401 });
|
|
6825
6878
|
}
|
|
6826
|
-
authContext = this.tokenToAuthContext(payload);
|
|
6879
|
+
authContext = this.tokenToAuthContext(payload, clientIp);
|
|
6827
6880
|
} else {
|
|
6828
6881
|
return new Response("Authorization token required", { status: 401 });
|
|
6829
6882
|
}
|
|
@@ -6856,6 +6909,50 @@ class RTCHost {
|
|
|
6856
6909
|
});
|
|
6857
6910
|
}
|
|
6858
6911
|
}
|
|
6912
|
+
async handleRoute(req) {
|
|
6913
|
+
const url = new URL(req.url);
|
|
6914
|
+
const method = req.method;
|
|
6915
|
+
let matchedRoute = null;
|
|
6916
|
+
let routeParams = {};
|
|
6917
|
+
for (const element of this.context.elements) {
|
|
6918
|
+
if (typeof element.matchesRoutePath === "function") {
|
|
6919
|
+
const { matches, params } = element.matchesRoutePath(url.pathname);
|
|
6920
|
+
if (matches) {
|
|
6921
|
+
matchedRoute = element;
|
|
6922
|
+
routeParams = params || {};
|
|
6923
|
+
break;
|
|
6924
|
+
}
|
|
6925
|
+
}
|
|
6926
|
+
}
|
|
6927
|
+
if (!matchedRoute) {
|
|
6928
|
+
return new Response("Route not found", { status: 404 });
|
|
6929
|
+
}
|
|
6930
|
+
const handler = matchedRoute.getHandler(method);
|
|
6931
|
+
if (!handler) {
|
|
6932
|
+
return new Response(`Method ${method} not allowed`, { status: 405 });
|
|
6933
|
+
}
|
|
6934
|
+
try {
|
|
6935
|
+
const authHeader = req.headers.get("Authorization");
|
|
6936
|
+
const token = authHeader?.replace("Bearer ", "");
|
|
6937
|
+
const clientIp = this.getClientIpAddress(req);
|
|
6938
|
+
let authContext;
|
|
6939
|
+
if (token && !matchedRoute.isPublic) {
|
|
6940
|
+
const payload = await this.verifyAuthToken(token);
|
|
6941
|
+
if (!payload) {
|
|
6942
|
+
return new Response("Invalid or expired token", { status: 401 });
|
|
6943
|
+
}
|
|
6944
|
+
authContext = this.tokenToAuthContext(payload, clientIp);
|
|
6945
|
+
} else {
|
|
6946
|
+
authContext = this.getDefaultAuthContext(clientIp);
|
|
6947
|
+
}
|
|
6948
|
+
const routes = this.model.routes(authContext);
|
|
6949
|
+
const response = await routes[matchedRoute.name](method, req, routeParams, url);
|
|
6950
|
+
return response;
|
|
6951
|
+
} catch (error) {
|
|
6952
|
+
console.error(`Error executing route ${matchedRoute.name}:`, error);
|
|
6953
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
6954
|
+
}
|
|
6955
|
+
}
|
|
6859
6956
|
setupServer() {
|
|
6860
6957
|
this.server = Bun.serve({
|
|
6861
6958
|
fetch: async (req, server) => {
|
|
@@ -6886,6 +6983,10 @@ class RTCHost {
|
|
|
6886
6983
|
if (url.pathname === "/query" && req.method === "POST") {
|
|
6887
6984
|
return await this.handleQuery(req);
|
|
6888
6985
|
}
|
|
6986
|
+
const routeResponse = await this.handleRoute(req);
|
|
6987
|
+
if (routeResponse.status !== 404) {
|
|
6988
|
+
return routeResponse;
|
|
6989
|
+
}
|
|
6889
6990
|
return new Response("Not Found", { status: 404 });
|
|
6890
6991
|
},
|
|
6891
6992
|
websocket: {
|
|
@@ -6901,8 +7002,21 @@ class RTCHost {
|
|
|
6901
7002
|
});
|
|
6902
7003
|
}
|
|
6903
7004
|
isPublicEndpoint(pathname) {
|
|
6904
|
-
const
|
|
6905
|
-
|
|
7005
|
+
for (const element of this.context.elements) {
|
|
7006
|
+
if (typeof element.matchesCommandPath === "function") {
|
|
7007
|
+
const { matches, isPublic } = element.matchesCommandPath(pathname);
|
|
7008
|
+
if (matches) {
|
|
7009
|
+
return isPublic;
|
|
7010
|
+
}
|
|
7011
|
+
}
|
|
7012
|
+
if (typeof element.matchesRoutePath === "function") {
|
|
7013
|
+
const { matches, isPublic } = element.matchesRoutePath(pathname);
|
|
7014
|
+
if (matches) {
|
|
7015
|
+
return isPublic;
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
7018
|
+
}
|
|
7019
|
+
return false;
|
|
6906
7020
|
}
|
|
6907
7021
|
async handleSync(lastDate) {
|
|
6908
7022
|
const syncDate = new Date;
|
package/host.ts
CHANGED
|
@@ -53,7 +53,6 @@ class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
53
53
|
const payload = verifyToken(token);
|
|
54
54
|
return payload;
|
|
55
55
|
} catch (error) {
|
|
56
|
-
console.error("Token verification failed:", error);
|
|
57
56
|
return null;
|
|
58
57
|
}
|
|
59
58
|
}
|
|
@@ -61,20 +60,51 @@ class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
61
60
|
/**
|
|
62
61
|
* Convert JWT payload to AuthContext
|
|
63
62
|
*/
|
|
64
|
-
private tokenToAuthContext(
|
|
63
|
+
private tokenToAuthContext(
|
|
64
|
+
payload: TokenPayload,
|
|
65
|
+
ipAddress?: string,
|
|
66
|
+
): AuthContext {
|
|
65
67
|
return {
|
|
66
68
|
userId: payload.userId,
|
|
67
69
|
roles: [], // Default to no roles, you may want to extend TokenPayload to include roles
|
|
70
|
+
ipAddress,
|
|
68
71
|
};
|
|
69
72
|
}
|
|
70
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Extract client IP address from request headers
|
|
76
|
+
*/
|
|
77
|
+
private getClientIpAddress(req: Request): string | undefined {
|
|
78
|
+
// Check common headers for client IP
|
|
79
|
+
const xForwardedFor = req.headers.get("x-forwarded-for");
|
|
80
|
+
const xRealIp = req.headers.get("x-real-ip");
|
|
81
|
+
const cfConnectingIp = req.headers.get("cf-connecting-ip");
|
|
82
|
+
|
|
83
|
+
if (xForwardedFor) {
|
|
84
|
+
// x-forwarded-for can contain multiple IPs, take the first one
|
|
85
|
+
return xForwardedFor.split(",")[0].trim();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (xRealIp) {
|
|
89
|
+
return xRealIp;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (cfConnectingIp) {
|
|
93
|
+
return cfConnectingIp;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Fallback - this might not work in all environments
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
71
100
|
/**
|
|
72
101
|
* Get default auth context for anonymous users
|
|
73
102
|
*/
|
|
74
|
-
private getDefaultAuthContext(): AuthContext {
|
|
103
|
+
private getDefaultAuthContext(ipAddress?: string): AuthContext {
|
|
75
104
|
return {
|
|
76
|
-
userId:
|
|
105
|
+
userId: undefined as any,
|
|
77
106
|
roles: [],
|
|
107
|
+
ipAddress,
|
|
78
108
|
};
|
|
79
109
|
}
|
|
80
110
|
|
|
@@ -148,15 +178,16 @@ class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
148
178
|
const authHeader = req.headers.get("Authorization");
|
|
149
179
|
const token = authHeader?.replace("Bearer ", "");
|
|
150
180
|
|
|
181
|
+
const clientIp = this.getClientIpAddress(req);
|
|
151
182
|
let authContext: AuthContext;
|
|
152
183
|
if (token && !this.isPublicEndpoint(url.pathname)) {
|
|
153
184
|
const payload = await this.verifyAuthToken(token);
|
|
154
185
|
if (!payload) {
|
|
155
186
|
return new Response("Invalid or expired token", { status: 401 });
|
|
156
187
|
}
|
|
157
|
-
authContext = this.tokenToAuthContext(payload);
|
|
188
|
+
authContext = this.tokenToAuthContext(payload, clientIp);
|
|
158
189
|
} else {
|
|
159
|
-
authContext = this.getDefaultAuthContext();
|
|
190
|
+
authContext = this.getDefaultAuthContext(clientIp);
|
|
160
191
|
}
|
|
161
192
|
|
|
162
193
|
let argument: any;
|
|
@@ -192,13 +223,14 @@ class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
192
223
|
const authHeader = req.headers.get("Authorization");
|
|
193
224
|
const token = authHeader?.replace("Bearer ", "");
|
|
194
225
|
|
|
226
|
+
const clientIp = this.getClientIpAddress(req);
|
|
195
227
|
let authContext: AuthContext;
|
|
196
228
|
if (token) {
|
|
197
229
|
const payload = await this.verifyAuthToken(token);
|
|
198
230
|
if (!payload) {
|
|
199
231
|
return new Response("Invalid or expired token", { status: 401 });
|
|
200
232
|
}
|
|
201
|
-
authContext = this.tokenToAuthContext(payload);
|
|
233
|
+
authContext = this.tokenToAuthContext(payload, clientIp);
|
|
202
234
|
} else {
|
|
203
235
|
return new Response("Authorization token required", { status: 401 });
|
|
204
236
|
}
|
|
@@ -250,6 +282,70 @@ class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
250
282
|
}
|
|
251
283
|
}
|
|
252
284
|
|
|
285
|
+
private async handleRoute(req: Request) {
|
|
286
|
+
const url = new URL(req.url);
|
|
287
|
+
const method = req.method;
|
|
288
|
+
|
|
289
|
+
// Find matching route
|
|
290
|
+
let matchedRoute: any = null;
|
|
291
|
+
let routeParams: Record<string, string> = {};
|
|
292
|
+
|
|
293
|
+
for (const element of this.context.elements) {
|
|
294
|
+
// Check if element has matchesRoutePath method (ArcRoute)
|
|
295
|
+
if (typeof (element as any).matchesRoutePath === "function") {
|
|
296
|
+
const { matches, params } = (element as any).matchesRoutePath(
|
|
297
|
+
url.pathname,
|
|
298
|
+
);
|
|
299
|
+
if (matches) {
|
|
300
|
+
matchedRoute = element;
|
|
301
|
+
routeParams = params || {};
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!matchedRoute) {
|
|
308
|
+
return new Response("Route not found", { status: 404 });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const handler = matchedRoute.getHandler(method);
|
|
312
|
+
if (!handler) {
|
|
313
|
+
return new Response(`Method ${method} not allowed`, { status: 405 });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
// Extract token from Authorization header
|
|
318
|
+
const authHeader = req.headers.get("Authorization");
|
|
319
|
+
const token = authHeader?.replace("Bearer ", "");
|
|
320
|
+
|
|
321
|
+
const clientIp = this.getClientIpAddress(req);
|
|
322
|
+
let authContext: AuthContext;
|
|
323
|
+
|
|
324
|
+
if (token && !matchedRoute.isPublic) {
|
|
325
|
+
const payload = await this.verifyAuthToken(token);
|
|
326
|
+
if (!payload) {
|
|
327
|
+
return new Response("Invalid or expired token", { status: 401 });
|
|
328
|
+
}
|
|
329
|
+
authContext = this.tokenToAuthContext(payload, clientIp);
|
|
330
|
+
} else {
|
|
331
|
+
authContext = this.getDefaultAuthContext(clientIp);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Use the model's routes method to properly handle event publishing
|
|
335
|
+
const routes = this.model.routes(authContext);
|
|
336
|
+
const response = await routes[matchedRoute.name](
|
|
337
|
+
method,
|
|
338
|
+
req,
|
|
339
|
+
routeParams,
|
|
340
|
+
url,
|
|
341
|
+
);
|
|
342
|
+
return response;
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error(`Error executing route ${matchedRoute.name}:`, error);
|
|
345
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
253
349
|
private setupServer() {
|
|
254
350
|
this.server = Bun.serve({
|
|
255
351
|
fetch: async (req, server) => {
|
|
@@ -296,6 +392,12 @@ class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
296
392
|
return await this.handleQuery(req);
|
|
297
393
|
}
|
|
298
394
|
|
|
395
|
+
// Try to handle as a route
|
|
396
|
+
const routeResponse = await this.handleRoute(req);
|
|
397
|
+
if (routeResponse.status !== 404) {
|
|
398
|
+
return routeResponse;
|
|
399
|
+
}
|
|
400
|
+
|
|
299
401
|
return new Response("Not Found", { status: 404 });
|
|
300
402
|
},
|
|
301
403
|
websocket: {
|
|
@@ -312,10 +414,31 @@ class RTCHost implements RealTimeCommunicationAdapter {
|
|
|
312
414
|
}
|
|
313
415
|
|
|
314
416
|
private isPublicEndpoint(pathname: string): boolean {
|
|
315
|
-
//
|
|
316
|
-
const
|
|
417
|
+
// Iterate through all context elements and check if any match and are public
|
|
418
|
+
for (const element of this.context.elements) {
|
|
419
|
+
// Check if element has matchesCommandPath method (ArcCommand)
|
|
420
|
+
if (typeof (element as any).matchesCommandPath === "function") {
|
|
421
|
+
const { matches, isPublic } = (element as any).matchesCommandPath(
|
|
422
|
+
pathname,
|
|
423
|
+
);
|
|
424
|
+
if (matches) {
|
|
425
|
+
return isPublic;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Check if element has matchesRoutePath method (ArcRoute)
|
|
430
|
+
if (typeof (element as any).matchesRoutePath === "function") {
|
|
431
|
+
const { matches, isPublic } = (element as any).matchesRoutePath(
|
|
432
|
+
pathname,
|
|
433
|
+
);
|
|
434
|
+
if (matches) {
|
|
435
|
+
return isPublic;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
317
439
|
|
|
318
|
-
|
|
440
|
+
// Default to non-public if no matching element found
|
|
441
|
+
return false;
|
|
319
442
|
}
|
|
320
443
|
|
|
321
444
|
private async handleSync(lastDate: string | null) {
|