@jchat/runtime 1.0.0-alpha.23 → 1.0.0-alpha.25
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 +42 -4
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/index.d.mts +41 -58
- package/dist/index.mjs +1 -3
- package/package.json +2 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @jchat/runtime
|
|
2
2
|
|
|
3
|
+
## 1.0.0-alpha.25
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Update local development license verification endpoint port
|
|
8
|
+
|
|
9
|
+
- Update package metadata, documentation, and license links
|
|
10
|
+
|
|
11
|
+
- Bumped `@jchat/*` dependencies:
|
|
12
|
+
- [`shared@1.0.0-alpha.24`](https://github.com/jchatcloud/jchat/releases/tag/shared@v1.0.0-alpha.24)
|
|
13
|
+
|
|
14
|
+
## 1.0.0-alpha.24
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- Add feature stage and release channel verification with early access capability helpers
|
|
19
|
+
|
|
20
|
+
- Add annual term licensing, quota enforcement, and trial plan validation
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- Decouple application server environment schema from licensing runtime
|
|
25
|
+
|
|
26
|
+
- Update default license issuer URL
|
|
27
|
+
|
|
28
|
+
- Make license verification capability-driven and support dynamic plan names
|
|
29
|
+
|
|
30
|
+
- Update default service and documentation domains to jchatcloud.com
|
|
31
|
+
|
|
32
|
+
- Enforce license maxSites quota in runtime supervisor and Elysia plugin
|
|
33
|
+
|
|
34
|
+
- Update package repository URLs to jarvistack organization
|
|
35
|
+
|
|
36
|
+
- Improve code formatting and style consistency
|
|
37
|
+
|
|
38
|
+
- Bumped `@jchat/*` dependencies:
|
|
39
|
+
- [`shared@1.0.0-alpha.23`](https://github.com/jchatcloud/jchat/releases/tag/shared@v1.0.0-alpha.23)
|
|
40
|
+
|
|
3
41
|
## 1.0.0-alpha.23
|
|
4
42
|
|
|
5
43
|
No changes in this release.
|
|
@@ -11,28 +49,28 @@ No changes in this release.
|
|
|
11
49
|
- Update default service and documentation domains to jchatplatform.com
|
|
12
50
|
|
|
13
51
|
- Bumped `@jchat/*` dependencies:
|
|
14
|
-
- [`shared@1.0.0-alpha.22`](https://github.com/
|
|
52
|
+
- [`shared@1.0.0-alpha.22`](https://github.com/jchatcloud/jchat/releases/tag/shared@v1.0.0-alpha.22)
|
|
15
53
|
|
|
16
54
|
## 1.0.0-alpha.21
|
|
17
55
|
|
|
18
56
|
### Patch Changes
|
|
19
57
|
|
|
20
58
|
- Bumped `@jchat/*` dependencies:
|
|
21
|
-
- [`shared@1.0.0-alpha.21`](https://github.com/
|
|
59
|
+
- [`shared@1.0.0-alpha.21`](https://github.com/jchatcloud/jchat/releases/tag/shared@v1.0.0-alpha.21)
|
|
22
60
|
|
|
23
61
|
## 1.0.0-alpha.20
|
|
24
62
|
|
|
25
63
|
### Patch Changes
|
|
26
64
|
|
|
27
65
|
- Bumped `@jchat/*` dependencies:
|
|
28
|
-
- [`shared@1.0.0-alpha.20`](https://github.com/
|
|
66
|
+
- [`shared@1.0.0-alpha.20`](https://github.com/jchatcloud/jchat/releases/tag/shared@v1.0.0-alpha.20)
|
|
29
67
|
|
|
30
68
|
## 1.0.0-alpha.19
|
|
31
69
|
|
|
32
70
|
### Patch Changes
|
|
33
71
|
|
|
34
72
|
- Bumped `@jchat/*` dependencies:
|
|
35
|
-
- [`shared@1.0.0-alpha.19`](https://github.com/
|
|
73
|
+
- [`shared@1.0.0-alpha.19`](https://github.com/jchatcloud/jchat/releases/tag/shared@v1.0.0-alpha.19)
|
|
36
74
|
|
|
37
75
|
## 1.0.0-alpha.17
|
|
38
76
|
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -12,4 +12,4 @@ Core runtime environment and instance state management package for JChat platfor
|
|
|
12
12
|
|
|
13
13
|
## License
|
|
14
14
|
|
|
15
|
-
Proprietary — All rights reserved. See the [
|
|
15
|
+
Proprietary — All rights reserved. See the [Commercial EULA & Licensing Terms](https://jchatcloud.com/terms) for full details.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Elysia } from "elysia";
|
|
2
|
-
import * as z from "zod";
|
|
3
2
|
//#region src/domain.d.ts
|
|
4
3
|
export type Provider = "envato" | "jchat";
|
|
5
|
-
export type Plan =
|
|
4
|
+
export type Plan = string;
|
|
6
5
|
export type RoutingMode = "path" | "subdomain";
|
|
7
6
|
export type DomainScope = "current-host" | "parent-domain";
|
|
8
7
|
export type NormalizedDomain = {
|
|
@@ -17,7 +16,7 @@ export declare function isLocalhostDomain(value: string): boolean;
|
|
|
17
16
|
export declare function getRootDomain(value: string): string | null;
|
|
18
17
|
/** Checks if a request domain matches or is an authorized subdomain of the licensed domain. */
|
|
19
18
|
export declare function isMatchingDomain(requestHost: string, licensedDomain: string): boolean;
|
|
20
|
-
export declare function resolveActivationDomain(host: string,
|
|
19
|
+
export declare function resolveActivationDomain(host: string, isMultiSite: boolean, routingMode: RoutingMode, domainScope?: DomainScope): string;
|
|
21
20
|
//#endregion
|
|
22
21
|
//#region src/audit.d.ts
|
|
23
22
|
export type AuditEvent = "check_success" | "check_unreachable" | "check_rejected";
|
|
@@ -76,8 +75,8 @@ type CheckInput = {
|
|
|
76
75
|
};
|
|
77
76
|
//#endregion
|
|
78
77
|
//#region src/constants.d.ts
|
|
79
|
-
export declare const DEFAULT_LICENSE_SERVER_URL = "https://license.
|
|
80
|
-
export declare const DEFAULT_LICENSE_ISSUER = "https://license.
|
|
78
|
+
export declare const DEFAULT_LICENSE_SERVER_URL = "https://license.jchatcloud.com";
|
|
79
|
+
export declare const DEFAULT_LICENSE_ISSUER = "https://license.jchatcloud.com";
|
|
81
80
|
export declare const DEFAULT_LICENSE_AUDIENCE = "jchat-chat";
|
|
82
81
|
//#endregion
|
|
83
82
|
//#region src/state.d.ts
|
|
@@ -192,61 +191,32 @@ export declare function jchatRuntime(options?: RuntimePluginOptions): Elysia<"",
|
|
|
192
191
|
}>;
|
|
193
192
|
//#endregion
|
|
194
193
|
//#region src/server-context.d.ts
|
|
195
|
-
export
|
|
196
|
-
|
|
197
|
-
JCHAT_COOKIE_SECRET:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
JCHAT_CLOUDFLARE_ACCOUNT_ID: z.ZodOptional<z.ZodString>;
|
|
217
|
-
JCHAT_CLOUDFLARE_BUCKET_NAME: z.ZodOptional<z.ZodString>;
|
|
218
|
-
JCHAT_CLOUDFLARE_ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
|
|
219
|
-
JCHAT_CLOUDFLARE_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
|
|
220
|
-
JCHAT_CLOUDFLARE_PUBLIC_URL: z.ZodOptional<z.ZodString>;
|
|
221
|
-
JCHAT_GCS_BUCKET_NAME: z.ZodOptional<z.ZodString>;
|
|
222
|
-
JCHAT_GCS_PROJECT_ID: z.ZodOptional<z.ZodString>;
|
|
223
|
-
JCHAT_GCS_CLIENT_EMAIL: z.ZodOptional<z.ZodString>;
|
|
224
|
-
JCHAT_GCS_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
|
|
225
|
-
JCHAT_GCS_KEY_FILE: z.ZodOptional<z.ZodString>;
|
|
226
|
-
JCHAT_BOT_NAME: z.ZodString;
|
|
227
|
-
JCHAT_BOT_EMAIL: z.ZodEmail;
|
|
228
|
-
JCHAT_DOMAIN: z.ZodOptional<z.ZodString>;
|
|
229
|
-
JCHAT_SITE_URL: z.ZodOptional<z.ZodURL>;
|
|
230
|
-
JCHAT_ROUTING_MODE: z.ZodDefault<z.ZodEnum<{
|
|
231
|
-
path: "path";
|
|
232
|
-
subdomain: "subdomain";
|
|
233
|
-
}>>;
|
|
234
|
-
JCHAT_ADDON_ENCRYPTION_KEY: z.ZodString;
|
|
235
|
-
JCHAT_LICENSE_SERVER_URL: z.ZodOptional<z.ZodURL>;
|
|
236
|
-
JCHAT_LICENSE_CHECK_INTERVAL_DAYS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
|
|
237
|
-
JCHAT_STORE_SERVER_URL: z.ZodOptional<z.ZodURL>;
|
|
238
|
-
NODE_ENV: z.ZodDefault<z.ZodEnum<{
|
|
239
|
-
development: "development";
|
|
240
|
-
production: "production";
|
|
241
|
-
test: "test";
|
|
242
|
-
}>>;
|
|
243
|
-
SYSTEM_BOOTSTRAP_COMPLETE: z.ZodOptional<z.ZodBoolean>;
|
|
244
|
-
}, z.core.$strip>;
|
|
245
|
-
export type ServerConfig = z.infer<typeof ServerConfigSchema>;
|
|
246
|
-
export declare function loadServerContext(environment: Record<string, unknown>, distribution: "production" | "developer-sandbox" | undefined, publicKeyPem: string): ServerConfig;
|
|
194
|
+
export interface BaseServerContext {
|
|
195
|
+
JCHAT_DOMAIN?: string;
|
|
196
|
+
JCHAT_COOKIE_SECRET: string;
|
|
197
|
+
NODE_ENV?: string;
|
|
198
|
+
SYSTEM_BOOTSTRAP_COMPLETE?: boolean;
|
|
199
|
+
[key: string]: unknown;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Cryptographically verifies license state, validates production domain rules, salts
|
|
203
|
+
* JCHAT_COOKIE_SECRET with the HMAC license signature if verified, or randomizes it and flags
|
|
204
|
+
* SYSTEM_BOOTSTRAP_COMPLETE as false if invalid.
|
|
205
|
+
*/
|
|
206
|
+
export declare function sealServerContext<T extends Record<string, unknown>>(environmentOrConfig: T, distribution: "production" | "developer-sandbox" | undefined, publicKeyPem: string): T & {
|
|
207
|
+
JCHAT_COOKIE_SECRET: string;
|
|
208
|
+
SYSTEM_BOOTSTRAP_COMPLETE: boolean;
|
|
209
|
+
};
|
|
210
|
+
/** Backwards-compatible alias for `sealServerContext`. */
|
|
211
|
+
export declare function loadServerContext<T extends Record<string, unknown>>(environmentOrConfig: T, distribution: "production" | "developer-sandbox" | undefined, publicKeyPem: string): T & {
|
|
212
|
+
JCHAT_COOKIE_SECRET: string;
|
|
213
|
+
SYSTEM_BOOTSTRAP_COMPLETE: boolean;
|
|
214
|
+
};
|
|
247
215
|
//#endregion
|
|
248
216
|
//#region src/index.d.ts
|
|
249
217
|
export type RuntimeEnvironment = "production" | "development";
|
|
218
|
+
export type FeatureStage = "stable" | "beta" | "experimental";
|
|
219
|
+
export type ReleaseChannel = "stable" | "lts";
|
|
250
220
|
export type ConfigPayload = {
|
|
251
221
|
iss: string;
|
|
252
222
|
aud: string;
|
|
@@ -260,6 +230,14 @@ export type ConfigPayload = {
|
|
|
260
230
|
environment: RuntimeEnvironment;
|
|
261
231
|
routingMode: RoutingMode;
|
|
262
232
|
iat: number;
|
|
233
|
+
exp?: number;
|
|
234
|
+
isTrial?: boolean;
|
|
235
|
+
stages?: FeatureStage[];
|
|
236
|
+
channel?: ReleaseChannel;
|
|
237
|
+
limits?: {
|
|
238
|
+
maxSites: number;
|
|
239
|
+
maxRoomsPerSite?: number;
|
|
240
|
+
};
|
|
263
241
|
};
|
|
264
242
|
export type RuntimeOptions = {
|
|
265
243
|
publicKeyPem: string;
|
|
@@ -271,6 +249,11 @@ export declare function parsePayload(token: string): ConfigPayload | null;
|
|
|
271
249
|
export declare function verifySignature(token: string, currentDomain: string, options: RuntimeOptions): boolean;
|
|
272
250
|
export declare function verifyEntitlement(token: string, currentDomain: string, installationId: string, options?: RuntimeOptions): boolean;
|
|
273
251
|
export declare function hasEntitlement(token: string, currentDomain: string, entitlement: string, options: RuntimeOptions): boolean;
|
|
274
|
-
export declare function isMultiSiteAllowed(token: string | undefined | null, domain?: string): boolean;
|
|
252
|
+
export declare function isMultiSiteAllowed(token: string | undefined | null, domain?: string, options?: RuntimeOptions): boolean;
|
|
253
|
+
export declare function hasCapability(token: string | null | undefined, capability: string): boolean;
|
|
254
|
+
export declare function hasStageAccess(token: string | null | undefined, stage: FeatureStage): boolean;
|
|
255
|
+
export declare function getMaxSites(token: string | null | undefined): number;
|
|
256
|
+
export declare function getReleaseChannel(token: string | null | undefined): ReleaseChannel;
|
|
257
|
+
export declare function canCreateMultiSite(token: string | null | undefined): boolean;
|
|
275
258
|
//#endregion
|
|
276
259
|
export type { ActivationInput, ActivationResult, CheckInput, CheckResult, LocalState };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
import e from"node:crypto";import t from"node:net";import n from"node:fs/promises";import r from"node:path";import i from"node:fs";import{Elysia as a}from"elysia";import{replaceKeys as o}from"string-ts";import*as s from"zod";const c=`https://license.jchatplatform.com`,l=`https://license.jchatplatform.com`,u=`jchat-chat`,d=new Set([`localhost`,`127.0.0.1`,`::1`]),f=[`.local`,`.test`,`.invalid`,`.example`];function p(e){let t=e.trim();if(!t)throw Error(`Instance domain is required`);let n=new URL(t.includes(`://`)?t:`https://${t}`).hostname.toLowerCase().replace(/\.$/,``),r=n===`www.localhost`?`localhost`:n.replace(/^www\./,``);if(!r||r.includes(`@`)||/\s/.test(r))throw Error(`Invalid instance domain`);return{host:r,isLocalhost:d.has(r)}}function m(e){let{host:n,isLocalhost:r}=p(e);return!r&&t.isIP(n)===0&&n.includes(`.`)&&!f.some(e=>n.endsWith(e))}function h(e){try{return p(e).isLocalhost||m(e)}catch{return!1}}function g(e){try{return p(e).isLocalhost}catch{return!1}}function _(e){let{host:n,isLocalhost:r}=p(e);if(r||t.isIP(n)!==0)return null;let i=n.split(`.`);if(i.length<=2)return n;let a=new Set([`co`,`com`,`org`,`net`,`gov`,`edu`,`ac`,`sch`]),o=i.at(-1)??``,s=i.at(-2)??``;return a.has(s)&&o.length===2&&i.length>=3?i.slice(-3).join(`.`):i.slice(-2).join(`.`)}function ee(e,t){try{let n=p(e).host,r=p(t).host;if(n===r||n.endsWith(`.${r}`))return!0;let i=_(n),a=_(r);return!!(i&&a&&i===a)}catch{return!1}}function te(e,t,n,r=`current-host`){let i=p(e).host;return n===`subdomain`&&t===`extended`&&r===`parent-domain`?_(i)??i:i}function ne(e={}){let t=r.resolve(e.cwd??process.cwd()),n=e.env??Bun.env,i=v(t);return{repoRoot:i,runtimeRoot:t,uploadsDir:y(n.JCHAT_UPLOADS_DIR,t,i,`uploads`,`uploads`),addonBundlesDir:y(n.JCHAT_ADDONS_DIR,t,i,`addons`,`addons`),addonSourceDir:i?r.join(i,`addons`):r.join(t,`addons`),migrationsDir:y(n.JCHAT_MIGRATIONS_DIR,t,i,r.join(`packages`,`db`,`migrations`),`migrations`),dataDir:y(n.JCHAT_DATA_DIR,t,i,`data`,`data`)}}function re(e){return i.existsSync(r.join(e,`package.json`))&&i.existsSync(r.join(e,`apps`))&&i.existsSync(r.join(e,`packages`))}function v(e){let t=r.resolve(e);for(;;){if(re(t))return t;let e=r.dirname(t);if(e===t)return null;t=e}}function y(e,t,n,i,a){return e?r.resolve(e):n?r.join(n,i):r.join(t,a)}function b(){return ne().dataDir}const x=r.join(b(),`instance.json`);function S(){return{installationId:Bun.randomUUIDv7()}}function C(e=x){try{if(!i.existsSync(e))return``;let t=i.readFileSync(e,`utf8`);return JSON.parse(t)?.instanceToken??``}catch{return``}}async function w(e=x){try{let t=Bun.file(e);if(!await t.exists())return null;let n=JSON.parse(await t.text());if(!n||typeof n!=`object`)return null;let r=n;return r.version!==1||typeof r.installationId!=`string`||!r.installationId||typeof r.instanceToken!=`string`||!r.instanceToken?null:r}catch{return null}}async function T(e,t=x){let i=r.dirname(t);await n.mkdir(i,{recursive:!0});let a=`${t}.${process.pid}.${Bun.randomUUIDv7()}.tmp`;await Bun.write(a,`${JSON.stringify(e,null,2)}\n`),await n.rename(a,t)}async function ie(e,t=x){let n=await w(t);if(!n)throw Error(`Local license state is missing`);await T({...n,instanceToken:e,lastSuccessfulCheckAt:new Date().toISOString()},t)}function E(e){if(e)return e.length<=8?`redacted`:`${e.slice(0,4)}…${e.slice(-4)}`}async function ae(e,t,i=r.join(b(),`license-events.log`)){await n.mkdir(r.dirname(i),{recursive:!0});let a=JSON.stringify({at:new Date().toISOString(),event:e,domain:E(t.domain),installationId:E(t.installationId),reference:E(t.reference)});await n.appendFile(i,`${a}\n`,`utf8`)}let D=`unavailable`,O=`startup`,k;function oe(e){k=e}function A(t,n=e.randomUUID()){D=t,O=n,t===`invalid`&&k?.()}function j(){return{status:D,reference:O}}function se(){return D===`invalid`}function ce(e=Bun.env.NODE_ENV===`production`){return!!(D===`invalid`||e&&D===`unavailable`)}function le(){return Bun.env.JCHAT_LICENSE_SERVER_URL||(Bun.env.NODE_ENV===`development`||Bun.env.NODE_ENV===`test`?`http://localhost:4000`:c)}async function M(e){try{let t=e.registryEndpoint??le(),n=await(e.fetchImpl??fetch)(`${t}/api/v1/activate`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({purchaseCode:e.purchaseCode,domain:p(e.domain).host,siteSlug:e.siteSlug,domainScope:e.domainScope,provider:e.provider,routingMode:e.routingMode,installationId:e.installationId,environment:p(e.domain).isLocalhost?`development`:`production`})}),r=await n.json();return n.ok===!1||!r.success||!r.token?{success:!1,message:r.message??`This license is already active on another domain. Please deactivate it first.`}:{success:!0,buyer:r.buyer??`Customer`,token:r.token,domain:r.domain??p(e.domain).host}}catch{return{success:!1,message:`Failed to connect to the central registry. Please try again later.`}}}async function ue(e,t,n,r,i,a=`envato`,o=fetch){let s=S();try{let c=await M({purchaseCode:e,domain:t,siteSlug:n,domainScope:r,routingMode:i,provider:a,installationId:s.installationId,fetchImpl:o});return c.success?{valid:!0,buyer:c.buyer,token:c.token,domain:c.domain,state:s}:{valid:!1,error:c.message}}catch{return{valid:!1,error:`Failed to verify license. Check your internet connection.`}}}const N=new Set([`/health`,`/api/v1/license/status`]);async function P(e={}){if(!(e.isPrimaryWorker??(!Bun.env.WORKER_ID||Bun.env.WORKER_ID===`0`)))return;let t=e.domain??`localhost`;if(e.onInvalidate&&oe(e.onInvalidate),g(t)){A(`valid`);return}let n=await w();if(!n?.instanceToken){A(e.isBootstrapComplete?`valid`:`unavailable`);return}if(e.signingKey&&!Q(n.instanceToken,t,{publicKeyPem:e.signingKey})){A(`invalid`,`domain-mismatch`);return}if(await F(e)>1&&!$(n.instanceToken,t)){A(`invalid`,`multi-site-unauthorized`);return}A(`valid`)}function de(e={}){P(e);let t=e.allowedPaths??N;return new a({name:`jchatRuntime`}).derive({as:`global`},()=>({runtimeStatus:j()})).onBeforeHandle({as:`global`},async({request:n,set:r})=>{if(n.method===`POST`&&n.url.includes(`/owner/sites`)){let t=e.domain??`localhost`;if(!$((await w())?.instanceToken,t)&&await F(e)>=1)return r.status=403,{status:`license_restricted`,message:`Standard license is restricted to a single site. An Extended license is required to create additional sites.`,reference:`multi-site-unauthorized`}}return pe(n,r,t)})}function fe(e,t,n){return n.has(e)?!0:t===`invalid`?e===`/install/upgrade-license`:t===`unavailable`?e===`/install`||e.startsWith(`/install/`)||e.startsWith(`/assets/`)||e.startsWith(`/_`)||e===`/favicon.ico`:!1}function pe(e,t,n=N,r=Bun.env.NODE_ENV===`production`){let{status:i,reference:a}=j(),o=e.headers.get(`host`)?.split(`:`)[0]?.toLowerCase();if(o&&m(o)&&g(Bun.env.JCHAT_DOMAIN||`localhost`))return t.status=403,{status:`license_invalid`,message:`Public domain deployment requires a valid license matching this domain.`,reference:`public-host-requires-license`};if(i!==`valid`&&i!==`unreachable-fallback`&&(r||i!==`unavailable`)&&!fe(new URL(e.url).pathname.replace(/\/+$/,``)||`/`,i,n))return t.status=503,i===`unavailable`?{status:`license_unavailable`,message:`This JChat installation is not activated. Please complete the installation.`,reference:a}:{status:`license_invalid`,message:`This JChat installation is disabled because its license is invalid.`,reference:a}}async function F(e){let t=Bun.env.DATABASE_URL??process.env.DATABASE_URL;if(t&&typeof Bun<`u`&&`SQL`in Bun)try{let e=new Bun.SQL(t),n=await e`SELECT count(*)::int as count FROM sites WHERE deleted_at IS NULL`;if(await e.close(),n?.[0]?.count!==void 0&&n[0].count!==null)return Number(n[0].count)}catch{}if(e.getSiteCount)try{return await e.getSiteCount()}catch{return 0}return 0}`${[`manage`,`www`].join(`|`)}`;const I={PERMISSIONS:`permissions`,USERS:`users`,BOTS:`bots`,CORES:`cores`,SETTINGS:`settings`,MEMBERSHIPS:`memberships`,ROOMS:`rooms`,SITES:`sites`,RANKS:`ranks`,BLOCKS:`blocks`,BLOCKERS:`blockers`,SPAMS:`spams`,SOCIALS:`socials`,XPS:`xps`,JOINS:`joins`,DAILIES:`dailies`,SESSIONS:`sessions`,MEMBERS:`members`,STREAMS:`streams`,AUDITS:`audits`,ACTIONS:`actions`,UPDATES:`updates`,LOGS:`logs`,ERRORS:`errors`,CONFIGS:`configs`,BANS:`bans`};`${I.STREAMS}${I.AUDITS}${I.ACTIONS}`,`${I.STREAMS}${I.XPS}${I.UPDATES}`,`${I.STREAMS}${I.LOGS}${I.ERRORS}`;const L={LOCAL:`LOCAL`,MINIO:`MINIO`,CLOUDFLARE_R2:`CLOUDFLARE_R2`,AWS_S3:`AWS_S3`,GCS:`GCS`},R={MESSAGING:`Messaging`,PRIVATE_MESSAGING:`Private Messaging`,ROOMS:`Rooms`,MODERATION:`Moderation`,PROFILE:`Profile`,POSTS:`Posts`,GIFTS:`Gifts`,FRIENDS:`Friends`,SITE_ADMIN:`Site Admin`,ADDONS:`Addons`,COMMANDS:`Commands`},z={ALL:{value:`*`,label:`Super Admin`,description:`Grants absolute access to every feature and setting. Use with extreme caution.`,category:R.SITE_ADMIN},MESSAGE_SEND:{value:`message.send`,label:`Send Messages`,description:`Allows the user to send messages in public chat rooms.`,category:R.MESSAGING},MESSAGE_DELETE_OWN:{value:`message.delete.own`,label:`Delete Own Messages`,description:`Allows the user to remove their own sent messages.`,category:R.MESSAGING},MESSAGE_DELETE_ANY:{value:`message.delete.any`,label:`Delete Any Message`,description:`Moderator tool: delete messages sent by any user.`,category:R.MESSAGING},MESSAGE_CLEAR:{value:`message.clear`,label:`Clear Chat`,description:`Allows clearing the entire message history of a room.`,category:R.MESSAGING},MESSAGE_UPLOAD:{value:`message.upload`,label:`Upload Files`,description:`Allows attaching images and files to messages.`,category:R.MESSAGING},MESSAGE_USE_COMMANDS:{value:`message.use_commands`,label:`Use Slash Commands`,description:`Allows executing chat commands (e.g., /me, /shrug).`,category:R.MESSAGING},MESSAGE_WILDCARD:{value:`message.*`,label:`Full Messaging Access`,description:`Grants all message-related permissions in a room.`,category:R.MESSAGING},PM_SEND:{value:`pm.send`,label:`Send Private Messages`,description:`Allows starting and participating in 1-on-1 private conversations.`,category:R.PRIVATE_MESSAGING},PM_DELETE_OWN:{value:`pm.delete.own`,label:`Delete Own PMs`,description:`Allows deleting messages sent in private chats.`,category:R.PRIVATE_MESSAGING},PM_READ_ANY:{value:`pm.read.any`,label:`Read Any PM`,description:`Administrative tool: View all private conversations on the site.`,category:R.PRIVATE_MESSAGING},PM_DELETE_ANY:{value:`pm.delete.any`,label:`Delete Any PM`,description:`Moderator tool: Remove private messages sent by any user.`,category:R.PRIVATE_MESSAGING},PM_BYPASS:{value:`pm.bypass`,label:`Bypass Privacy Settings`,description:`Allows bypassing receiver's privacy settings (e.g. not accepting PMs or friends only).`,category:R.PRIVATE_MESSAGING},PM_WILDCARD:{value:`pm.*`,label:`Full PM Access`,description:`Grants all private-messaging related permissions.`,category:R.PRIVATE_MESSAGING},ROOM_JOIN:{value:`room.join`,label:`Join Rooms`,description:`Allows users to enter and view public or restricted chat rooms.`,category:R.ROOMS},ROOM_CREATE:{value:`room.create`,label:`Create Rooms`,description:`Allows users to create new chat rooms.`,category:R.ROOMS},ROOM_EDIT:{value:`room.edit`,label:`Edit Room Settings`,description:`Allows changing room names, descriptions, and settings.`,category:R.ROOMS},ROOM_DELETE:{value:`room.delete`,label:`Delete Rooms`,description:`Allows permanently removing chat rooms and their history.`,category:R.ROOMS},ROOM_LOCK:{value:`room.lock`,label:`Lock/Unlock Rooms`,description:`Allows moderators to temporarily prevent new messages in a room.`,category:R.ROOMS},ROOM_SET_TOPIC:{value:`room.set_topic`,label:`Change Room Topic`,description:`Allows updating the current topic/announcement displayed in a room.`,category:R.ROOMS},ROOM_WILDCARD:{value:`room.*`,label:`Full Room Management`,description:`Grants all room-related administration permissions.`,category:R.ROOMS},USER_KICK:{value:`user.kick`,label:`Kick Users`,description:`Allows removing a user from a room temporarily.`,category:R.MODERATION},USER_BAN:{value:`user.ban`,label:`Ban Users`,description:`Allows permanently banning users from the site or specific rooms.`,category:R.MODERATION},USER_UNBAN:{value:`user.unban`,label:`Unban Users`,description:`Allows lifting previously imposed bans.`,category:R.MODERATION},USER_MUTE:{value:`user.mute`,label:`Mute Users`,description:`Allows preventing a user from sending messages for a duration.`,category:R.MODERATION},USER_UNMUTE:{value:`user.unmute`,label:`Unmute Users`,description:`Allows lifting mutes early.`,category:R.MODERATION},USER_EDIT_RANK:{value:`user.edit_rank`,label:`Manage User Ranks`,description:`Allows assigning or removing ranks for other users.`,category:R.MODERATION},USER_VIEW_PROFILE:{value:`user.view_profile`,label:`View Profiles`,description:`Allows viewing detailed user profiles and settings.`,category:R.MODERATION},USER_WILDCARD:{value:`user.*`,label:`Full Moderation Access`,description:`Grants all user-management and moderation permissions.`,category:R.MODERATION},PROFILE_EDIT:{value:`profile.edit`,label:`Edit Own Profile`,description:`Allows users to change their display name, bio, and avatar.`,category:R.PROFILE},PROFILE_CUSTOMIZE:{value:`profile.customize`,label:`Profile Customization`,description:`Allows advanced styling like custom fonts, themes, and backgrounds.`,category:R.PROFILE},PROFILE_SET_STATUS:{value:`profile.set_status`,label:`Set Status`,description:`Allows setting custom status messages and status indicators.`,category:R.PROFILE},POST_CREATE:{value:`post.create`,label:`Create Posts`,description:`Allows sharing updates and media in the activity feed.`,category:R.POSTS},POST_CREATE_ANNOUNCEMENT:{value:`post.create.announcement`,label:`Create Announcements`,description:`Allows creating official site announcements.`,category:R.POSTS},POST_EDIT_OWN:{value:`post.edit.own`,label:`Edit Own Posts`,description:`Allows modifying previously shared feed posts.`,category:R.POSTS},POST_EDIT_ANY:{value:`post.edit.any`,label:`Edit Any Post`,description:`Administrative tool: Edit posts shared by any user.`,category:R.POSTS},POST_DELETE_OWN:{value:`post.delete.own`,label:`Delete Own Posts`,description:`Allows removing personal feed posts.`,category:R.POSTS},POST_DELETE_ANY:{value:`post.delete.any`,label:`Delete Any Post`,description:`Moderator tool: Remove posts shared by any user.`,category:R.POSTS},POST_REACT:{value:`post.react`,label:`React to Posts`,description:`Allows adding emoji reactions to feed posts.`,category:R.POSTS},POST_COMMENT:{value:`post.comment`,label:`Comment on Posts`,description:`Allows joining the conversation on shared feed items.`,category:R.POSTS},POST_COMMENT_DELETE_OWN:{value:`post.comment.delete.own`,label:`Delete Own Comments`,description:`Allows removing personal comments from feed posts.`,category:R.POSTS},POST_COMMENT_DELETE_ANY:{value:`post.comment.delete.any`,label:`Delete Any Comment`,description:`Moderator tool: Remove comments left by any user.`,category:R.POSTS},POST_WILDCARD:{value:`post.*`,label:`Full Feed Access`,description:`Grants all permissions related to the activity feed.`,category:R.POSTS},USER_BLOCK:{value:`user.block`,label:`Block Users`,description:`Allows preventing specific users from contacting the user.`,category:R.MODERATION},USER_UNBLOCK:{value:`user.unblock`,label:`Unblock Users`,description:`Allows lifting previously imposed user blocks.`,category:R.MODERATION},USER_IGNORE:{value:`user.ignore`,label:`Ignore Users`,description:`Allows hiding messages and notifications from specific users.`,category:R.MODERATION},USER_UNIGNORE:{value:`user.unignore`,label:`Stop Ignoring`,description:`Allows showing messages from previously ignored users again.`,category:R.MODERATION},SITE_EDIT:{value:`site.edit`,label:`Edit Site Settings`,description:`Allows changing site name, branding, and global configuration.`,category:R.SITE_ADMIN},SITE_CREATE:{value:`site.create`,label:`Create New Sites`,description:`Master Admin tool: Create independent Jchat site instances.`,category:R.SITE_ADMIN},SITE_MANAGE_RANKS:{value:`site.manage_ranks`,label:`Manage Ranks & Perms`,description:`Allows creating ranks and configuring their permission sets.`,category:R.SITE_ADMIN},SITE_MANAGE_ADDONS:{value:`site.manage_addons`,label:`Manage Addons`,description:`Allows installing, configuring, and removing jchat Addons.`,category:R.SITE_ADMIN},SITE_VIEW_AUDIT:{value:`site.view_audit`,label:`View Audit Logs`,description:`Administrative tool: See a detailed record of all admin actions.`,category:R.SITE_ADMIN},SITE_VIEW_REPORTS:{label:`View Reports`,value:`site.view_reports`,description:`Allows seeing reports filed by users against content or people.`,category:R.SITE_ADMIN},SITE_MANAGE_REPORTS:{value:`site.manage_reports`,label:`Handle Reports`,description:`Allows resolving or dismissing user reports.`,category:R.SITE_ADMIN},SITE_MANAGE_SPAM:{value:`site.manage_spam`,label:`Manage Anti-Spam`,description:`Allows configuring the anti-spam engine, thresholds, and per-module settings.`,category:R.SITE_ADMIN},SITE_WILDCARD:{value:`site.*`,label:`Full Site Administration`,description:`Grants all technical administration and site discovery permissions.`,category:R.SITE_ADMIN},FRIEND_REQUEST_SEND:{value:`friend.request.send`,label:`Send Friend Requests`,description:`Allows the user to send friend requests to other users.`,category:R.FRIENDS},FRIEND_REQUEST_MANAGE:{value:`friend.request.manage`,label:`Manage Friend Requests`,description:`Allows the user to accept or reject incoming friend requests.`,category:R.FRIENDS},FRIEND_REMOVE:{value:`friend.remove`,label:`Remove Friends`,description:`Allows the user to remove accepted friends from their list.`,category:R.FRIENDS},FRIEND_LIST:{value:`friend.list`,label:`View Friend List`,description:`Allows the user to view their own friends list.`,category:R.FRIENDS},ADDON_WILDCARD:{value:`addon.*`,label:`Full Addon System Access`,description:`Grants access to use every installed addon regardless of specific gates.`,category:R.ADDONS},COMMAND_CLEAR:{value:`command.clear`,label:`Clear Chat Command`,description:`Allows using the /clear slash command to clear chat history.`,category:R.COMMANDS},COMMAND_HELP:{value:`command.help`,label:`View Commands Help`,description:`Allows using the /help slash command.`,category:R.COMMANDS}},B=Object.fromEntries(Object.entries(z).map(([e,t])=>[e,t.value])),me=Object.fromEntries(Object.values(z).map(e=>[e.value,{name:e.value,label:e.label,description:e.description,category:e.category}]));Object.values(me);const V=[B.MESSAGE_SEND,B.MESSAGE_UPLOAD,B.MESSAGE_DELETE_OWN,B.PM_SEND,B.PM_DELETE_OWN,B.ROOM_JOIN,B.POST_REACT,B.POST_COMMENT,B.POST_COMMENT_DELETE_OWN,B.PROFILE_EDIT,B.PROFILE_SET_STATUS,B.COMMAND_HELP];B.ALL,[B.SITE_EDIT,B.SITE_MANAGE_RANKS,B.SITE_MANAGE_ADDONS,B.SITE_VIEW_AUDIT,B.SITE_VIEW_REPORTS,B.SITE_MANAGE_REPORTS,B.SITE_MANAGE_SPAM,B.USER_WILDCARD,B.ROOM_EDIT,B.ROOM_DELETE,B.ROOM_LOCK,B.ROOM_SET_TOPIC,B.MESSAGE_WILDCARD,B.POST_WILDCARD,B.PM_WILDCARD,B.PROFILE_CUSTOMIZE,B.MESSAGE_USE_COMMANDS,B.POST_CREATE,B.POST_EDIT_OWN,B.POST_DELETE_OWN,B.FRIEND_REQUEST_SEND,B.FRIEND_REQUEST_MANAGE,B.FRIEND_REMOVE,B.FRIEND_LIST,B.COMMAND_CLEAR,...V],[B.ROOM_EDIT,B.ROOM_LOCK,B.ROOM_SET_TOPIC,B.MESSAGE_WILDCARD,B.POST_WILDCARD,B.PM_SEND,B.PM_DELETE_OWN,B.PM_READ_ANY,B.PM_DELETE_ANY,B.USER_KICK,B.USER_MUTE,B.USER_UNMUTE,B.USER_BAN,B.USER_UNBAN,B.PROFILE_CUSTOMIZE,B.MESSAGE_USE_COMMANDS,B.POST_CREATE,B.POST_EDIT_OWN,B.POST_DELETE_OWN,B.FRIEND_REQUEST_SEND,B.FRIEND_REQUEST_MANAGE,B.FRIEND_REMOVE,B.FRIEND_LIST,B.COMMAND_CLEAR,...V],[B.USER_KICK,B.USER_MUTE,B.USER_UNMUTE,B.MESSAGE_DELETE_ANY,B.MESSAGE_CLEAR,B.PROFILE_CUSTOMIZE,B.MESSAGE_USE_COMMANDS,B.POST_CREATE,B.POST_EDIT_OWN,B.POST_DELETE_OWN,B.FRIEND_REQUEST_SEND,B.FRIEND_REQUEST_MANAGE,B.FRIEND_REMOVE,B.FRIEND_LIST,B.COMMAND_CLEAR,...V],[B.PROFILE_CUSTOMIZE,B.MESSAGE_USE_COMMANDS,B.POST_CREATE,B.POST_EDIT_OWN,B.POST_DELETE_OWN,B.FRIEND_REQUEST_SEND,B.FRIEND_REQUEST_MANAGE,B.FRIEND_REMOVE,B.FRIEND_LIST,...V],[B.MESSAGE_USE_COMMANDS,B.POST_CREATE,B.POST_EDIT_OWN,B.POST_DELETE_OWN,B.FRIEND_REQUEST_SEND,B.FRIEND_REQUEST_MANAGE,B.FRIEND_REMOVE,B.FRIEND_LIST,...V],[...V];function he(e){let t=e?.trim().replace(/^https?:\/\//,``).split(`/`)[0].split(`:`)[0].replace(/^\.+/,``);if(!t||t===`localhost`||t.endsWith(`.localhost`)||/^\d{1,3}(\.\d{1,3}){3}$/.test(t))return;let n=t.split(`.`);if(n.length>2){let e=[`co`,`com`,`org`,`net`,`gov`,`edu`,`ac`,`sch`],t=n[n.length-1],r=n[n.length-2];return e.includes(r)&&t.length===2&&n.length>=3?n.slice(-3).join(`.`):n.slice(-2).join(`.`)}return t}function ge(e){let t=e?.trim().replace(/^https?:\/\//,``).split(`/`)[0].split(`:`)[0].replace(/^\.+/,``).toLowerCase();return t?t===`localhost`||/^\d{1,3}(\.\d{1,3}){3}$/.test(t)?!0:he(t)===t:!1}new Map([{label:`Default`,value:`lato`,family:`'Lato', sans-serif`,googleName:`Lato`,scale:1},{label:`Elegant`,value:`playfair`,family:`'Playfair Display', serif`,googleName:`Playfair Display`,scale:1.1},{label:`Cursive`,value:`dancing`,family:`'Dancing Script', cursive`,googleName:`Dancing Script`,scale:1.2},{label:`Modern`,value:`poppins`,family:`'Poppins', sans-serif`,googleName:`Poppins`,scale:1},{label:`Monospace`,value:`jetbrains`,family:`'JetBrains Mono', monospace`,googleName:`JetBrains Mono`,scale:.95},{label:`Rounded`,value:`quicksand`,family:`'Quicksand', sans-serif`,googleName:`Quicksand`,scale:1},{label:`Serif`,value:`roboto-serif`,family:`'Roboto Serif', serif`,googleName:`Roboto Serif`,scale:1.05},{label:`Sci-Fi`,value:`audiowide`,family:`'Audiowide', cursive`,googleName:`Audiowide`,scale:.95},{label:`Type`,value:`special-elite`,family:`'Special Elite', cursive`,googleName:`Special Elite`,scale:1.15},{label:`Pixel`,value:`press-start`,family:`'Press Start 2P', cursive`,googleName:`Press Start 2P`,scale:.75},{label:`Gothic`,value:`grenze`,family:`'Grenze Gotisch', cursive`,googleName:`Grenze Gotisch`,scale:1.15},{label:`Deco`,value:`righteous`,family:`'Righteous', cursive`,googleName:`Righteous`,scale:1},{label:`Spooky`,value:`creepster`,family:`'Creepster', cursive`,googleName:`Creepster`,scale:1.1},{label:`Shadow`,value:`vast-shadow`,family:`'Vast Shadow', cursive`,googleName:`Vast Shadow`,scale:1.05},{label:`Tech`,value:`syncopate`,family:`'Syncopate', sans-serif`,googleName:`Syncopate`,scale:.9},{label:`Script`,value:`lobster`,family:`'Lobster', cursive`,googleName:`Lobster`,scale:1.05}].map(e=>[e.value,e.scale]));const H=typeof window<`u`&&window.document!==void 0&&!window.process,U={brand:`color: #06b6d4; font-weight: bold;`,timestamp:`color: #888; font-size: 10px; font-family: monospace;`,info:`color: #3b82f6; font-weight: bold;`,success:`color: #10b981; font-weight: bold;`,warn:`color: #f59e0b; font-weight: bold;`,error:`color: #ef4444; font-weight: bold;`,debug:`color: #6366f1; font-style: italic;`,content:`color: inherit; font-family: inherit;`},W={reset:`\x1B[0m`,brand:`\x1B[1m\x1B[36m`,timestamp:`\x1B[90m`,info:`\x1B[1m\x1B[34m`,success:`\x1B[1m\x1B[32m`,warn:`\x1B[1m\x1B[33m`,error:`\x1B[1m\x1B[31m`,debug:`\x1B[3m\x1B[35m`};function _e(e){let{traceId:t,siteId:n,userId:r,path:i,...a}=e;return a}function G(e){return new Proxy(console,{get(t,n){if(n===`withContext`)return t=>G({...e,...t});if(typeof n==`string`&&[`info`,`success`,`warn`,`error`,`debug`].includes(n)){let t=n;if(!H&&process.env.NODE_ENV===`production`)return(...n)=>{let r=Ce(n),i=typeof Bun<`u`?Bun.randomUUIDv7():crypto.randomUUID(),a=e?.traceId??r.traceId,o={id:i,timestamp:new Date().toISOString(),level:t,message:r.message,...a?{traceId:a}:{},...e?.siteId?{siteId:e.siteId}:{},...e?.userId?{userId:e.userId}:{},...e?.path?{path:e.path}:{},...r.stack?{stack:r.stack}:{},...r.code?{code:r.code}:{},metadata:{...e?_e(e):{},...r.metadata}};Object.keys(o.metadata||{}).length===0&&delete o.metadata;let s=JSON.stringify(o);t===`error`?process.stderr.write(s+`
|
|
2
|
-
`):process.stdout.write(s+`
|
|
3
|
-
`)};let r=new Date().toLocaleTimeString(),i=e?.traceId,a=i?` [${i.slice(0,8)}]`:``;if(H)return import.meta.env?.PROD&&t===`debug`?()=>{}:console.log.bind(console,`%c${r}%c%c[jchat]${a}%c ${t}%c`,U.timestamp,``,U.brand,U[t],U.content);{let e=W[t]||``,n=W.brand,i=W.timestamp,o=W.reset;return console.log.bind(console,`${i}${r}${o} ${n}[jchat]${a}${o} ${e}${t}${o}${i}`)}}return t[n]}})}G();function ve(e){return e.map(e=>{if(typeof e==`string`)return e;if(e instanceof Error)return e.message;if(e&&typeof e==`object`&&!Array.isArray(e)){let t=e;if(typeof t.msg==`string`)return t.msg;if(typeof t.message==`string`)return t.message}return JSON.stringify(e)}).join(` `)}function ye(e){let t=e.find(e=>e instanceof Error);if(t?.stack)return t.stack;let n=e.find(e=>e&&typeof e==`object`&&!Array.isArray(e)&&!(e instanceof Error));return typeof n?.stack==`string`?n.stack:void 0}function be(e){let t=e.find(e=>e&&typeof e==`object`&&!Array.isArray(e)&&!(e instanceof Error));return typeof t?.code==`string`?t.code:void 0}function xe(e){let t=e.find(e=>e&&typeof e==`object`&&!Array.isArray(e)&&!(e instanceof Error));return typeof t?.traceId==`string`?t.traceId:void 0}function Se(e){let t=e.find(e=>e&&typeof e==`object`&&!Array.isArray(e)&&!(e instanceof Error));if(!t)return;let{msg:n,message:r,stack:i,traceId:a,...o}=t;return Object.keys(o).length>0?o:void 0}function Ce(e){return{message:ve(e),traceId:xe(e),stack:ye(e),code:be(e),metadata:Se(e)}}function we(e){return t=>e.parse({...o(t,`VITE_`,``),...t})}const K=s.object({JCHAT_PORT:s.string().regex(/^\d+$/).transform(Number).refine(e=>e>=1&&e<=65535,{message:`Port must be between 1 and 65535`}).refine(e=>e!==80&&e!==443,{message:`Avoid ports 80/443 (reserved for HTTP/HTTPS)`}).default(3e3),JCHAT_COOKIE_SECRET:s.string().min(1),JCHAT_DATABASE_URL:s.url().startsWith(`postgresql://`).regex(/^postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/([^?]+)(\?.*)?$/,{message:`Must be in format: postgresql://username:password@host:port/database?schema`}),JCHAT_REDIS_DATABASE_URL:s.url().refine(e=>e.startsWith(`rediss://`)||e.startsWith(`redis://`),{message:`Must use redis:// or rediss:// protocol`}).refine(e=>/^(rediss?):\/\/(?:([^:]+):([^@]+)@)?([^:]+):(\d+)$/.test(e),{message:`Must be in format: rediss://default:<password>@<host>:<port>`}),JCHAT_STORAGE_TYPE:s.enum(L),JCHAT_MINIO_ENDPOINT:s.string().optional(),JCHAT_MINIO_BUCKET_NAME:s.string().optional(),JCHAT_MINIO_ACCESS_KEY_ID:s.string().optional(),JCHAT_MINIO_SECRET_ACCESS_KEY:s.string().optional(),JCHAT_MINIO_PUBLIC_URL:s.string().optional(),JCHAT_AWS_REGION:s.string().optional(),JCHAT_AWS_BUCKET_NAME:s.string().optional(),JCHAT_AWS_ACCESS_KEY_ID:s.string().optional(),JCHAT_AWS_SECRET_ACCESS_KEY:s.string().optional(),JCHAT_CLOUDFLARE_ACCOUNT_ID:s.string().optional(),JCHAT_CLOUDFLARE_BUCKET_NAME:s.string().optional(),JCHAT_CLOUDFLARE_ACCESS_KEY_ID:s.string().optional(),JCHAT_CLOUDFLARE_SECRET_ACCESS_KEY:s.string().optional(),JCHAT_CLOUDFLARE_PUBLIC_URL:s.string().optional(),JCHAT_GCS_BUCKET_NAME:s.string().optional(),JCHAT_GCS_PROJECT_ID:s.string().optional(),JCHAT_GCS_CLIENT_EMAIL:s.string().optional(),JCHAT_GCS_PRIVATE_KEY:s.string().optional(),JCHAT_GCS_KEY_FILE:s.string().optional(),JCHAT_BOT_NAME:s.string().trim().min(3),JCHAT_BOT_EMAIL:s.email(),JCHAT_DOMAIN:s.string().optional(),JCHAT_SITE_URL:s.url().optional(),JCHAT_ROUTING_MODE:s.enum([`path`,`subdomain`]).default(`subdomain`),JCHAT_ADDON_ENCRYPTION_KEY:s.string().min(16),JCHAT_LICENSE_SERVER_URL:s.url().optional(),JCHAT_LICENSE_CHECK_INTERVAL_DAYS:s.string().regex(/^\d+$/).transform(Number).refine(e=>e>=1,{message:`License check interval must be at least 1 day`}).default(14),JCHAT_STORE_SERVER_URL:s.url().optional(),NODE_ENV:s.enum([`development`,`test`,`production`]).default(`development`),SYSTEM_BOOTSTRAP_COMPLETE:s.boolean().optional()}).superRefine((e,t)=>{if(e.JCHAT_SITE_URL){let n;try{n=new URL(e.JCHAT_SITE_URL).hostname}catch{}n&&!ge(n)&&t.addIssue({path:[`JCHAT_SITE_URL`],code:`custom`,message:`JCHAT_SITE_URL must use the client main/root domain`})}e.JCHAT_STORAGE_TYPE===L.MINIO&&!(e.JCHAT_MINIO_ENDPOINT&&e.JCHAT_MINIO_BUCKET_NAME&&e.JCHAT_MINIO_ACCESS_KEY_ID&&e.JCHAT_MINIO_SECRET_ACCESS_KEY&&e.JCHAT_MINIO_PUBLIC_URL)&&(e.JCHAT_MINIO_ENDPOINT||t.addIssue({path:[`JCHAT_MINIO_ENDPOINT`],code:`custom`,message:`JCHAT_MINIO_ENDPOINT is required when JCHAT_STORAGE_TYPE is MINIO`}),e.JCHAT_MINIO_BUCKET_NAME||t.addIssue({path:[`JCHAT_MINIO_BUCKET_NAME`],code:`custom`,message:`JCHAT_MINIO_BUCKET_NAME is required when JCHAT_STORAGE_TYPE is MINIO`}),e.JCHAT_MINIO_ACCESS_KEY_ID||t.addIssue({path:[`JCHAT_MINIO_ACCESS_KEY_ID`],code:`custom`,message:`JCHAT_MINIO_ACCESS_KEY_ID is required when JCHAT_STORAGE_TYPE is MINIO`}),e.JCHAT_MINIO_SECRET_ACCESS_KEY||t.addIssue({path:[`JCHAT_MINIO_SECRET_ACCESS_KEY`],code:`custom`,message:`JCHAT_MINIO_SECRET_ACCESS_KEY is required when JCHAT_STORAGE_TYPE is MINIO`}),e.JCHAT_MINIO_PUBLIC_URL||t.addIssue({path:[`JCHAT_MINIO_PUBLIC_URL`],code:`custom`,message:`JCHAT_MINIO_PUBLIC_URL is required when JCHAT_STORAGE_TYPE is MINIO`})),e.JCHAT_STORAGE_TYPE===L.AWS_S3&&!(e.JCHAT_AWS_REGION&&e.JCHAT_AWS_ACCESS_KEY_ID&&e.JCHAT_AWS_SECRET_ACCESS_KEY&&e.JCHAT_AWS_BUCKET_NAME)&&(e.JCHAT_AWS_REGION||t.addIssue({path:[`JCHAT_AWS_REGION`],code:`custom`,message:`JCHAT_AWS_REGION is required when JCHAT_STORAGE_TYPE is AWS_S3`}),e.JCHAT_AWS_ACCESS_KEY_ID||t.addIssue({path:[`JCHAT_AWS_ACCESS_KEY_ID`],code:`custom`,message:`JCHAT_AWS_ACCESS_KEY_ID is required when JCHAT_STORAGE_TYPE is AWS_S3`}),e.JCHAT_AWS_SECRET_ACCESS_KEY||t.addIssue({path:[`JCHAT_AWS_SECRET_ACCESS_KEY`],code:`custom`,message:`JCHAT_AWS_SECRET_ACCESS_KEY is required when JCHAT_STORAGE_TYPE is AWS_S3`}),e.JCHAT_AWS_BUCKET_NAME||t.addIssue({path:[`JCHAT_AWS_BUCKET_NAME`],code:`custom`,message:`JCHAT_AWS_BUCKET_NAME is required when JCHAT_STORAGE_TYPE is AWS_S3`})),e.JCHAT_STORAGE_TYPE===L.CLOUDFLARE_R2&&!(e.JCHAT_CLOUDFLARE_ACCOUNT_ID&&e.JCHAT_CLOUDFLARE_ACCESS_KEY_ID&&e.JCHAT_CLOUDFLARE_SECRET_ACCESS_KEY&&e.JCHAT_CLOUDFLARE_BUCKET_NAME&&e.JCHAT_CLOUDFLARE_PUBLIC_URL)&&(e.JCHAT_CLOUDFLARE_ACCOUNT_ID||t.addIssue({path:[`JCHAT_CLOUDFLARE_ACCOUNT_ID`],code:`custom`,message:`JCHAT_CLOUDFLARE_ACCOUNT_ID is required when JCHAT_STORAGE_TYPE is CLOUDFLARE_R2`}),e.JCHAT_CLOUDFLARE_ACCESS_KEY_ID||t.addIssue({path:[`JCHAT_CLOUDFLARE_ACCESS_KEY_ID`],code:`custom`,message:`JCHAT_CLOUDFLARE_ACCESS_KEY_ID is required when JCHAT_STORAGE_TYPE is CLOUDFLARE_R2`}),e.JCHAT_CLOUDFLARE_SECRET_ACCESS_KEY||t.addIssue({path:[`JCHAT_CLOUDFLARE_SECRET_ACCESS_KEY`],code:`custom`,message:`JCHAT_CLOUDFLARE_SECRET_ACCESS_KEY is required when JCHAT_STORAGE_TYPE is CLOUDFLARE_R2`}),e.JCHAT_CLOUDFLARE_BUCKET_NAME||t.addIssue({path:[`JCHAT_CLOUDFLARE_BUCKET_NAME`],code:`custom`,message:`JCHAT_CLOUDFLARE_BUCKET_NAME is required when JCHAT_STORAGE_TYPE is CLOUDFLARE_R2`}),e.JCHAT_CLOUDFLARE_PUBLIC_URL||t.addIssue({path:[`JCHAT_CLOUDFLARE_PUBLIC_URL`],code:`custom`,message:`JCHAT_CLOUDFLARE_PUBLIC_URL is required when JCHAT_STORAGE_TYPE is CLOUDFLARE_R2`})),e.JCHAT_STORAGE_TYPE===L.GCS&&!(e.JCHAT_GCS_PROJECT_ID&&e.JCHAT_GCS_BUCKET_NAME&&e.JCHAT_GCS_CLIENT_EMAIL&&e.JCHAT_GCS_PRIVATE_KEY)&&(e.JCHAT_GCS_PROJECT_ID||t.addIssue({path:[`JCHAT_GCS_PROJECT_ID`],code:`custom`,message:`JCHAT_GCS_PROJECT_ID is required when JCHAT_STORAGE_TYPE is GCS`}),e.JCHAT_GCS_BUCKET_NAME||t.addIssue({path:[`JCHAT_GCS_BUCKET_NAME`],code:`custom`,message:`JCHAT_GCS_BUCKET_NAME is required when JCHAT_STORAGE_TYPE is GCS`}),e.JCHAT_GCS_CLIENT_EMAIL||t.addIssue({path:[`JCHAT_GCS_CLIENT_EMAIL`],code:`custom`,message:`JCHAT_GCS_CLIENT_EMAIL is required when JCHAT_STORAGE_TYPE is GCS`}),e.JCHAT_GCS_PRIVATE_KEY||t.addIssue({path:[`JCHAT_GCS_PRIVATE_KEY`],code:`custom`,message:`JCHAT_GCS_PRIVATE_KEY is required when JCHAT_STORAGE_TYPE is GCS`}))});function Te(t,n=`production`,r){let i,a=!0;try{i=we(K)(t);let o=C();if(n===`developer-sandbox`)a=!0;else if(!o)a=!1,n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`));else if(!i.JCHAT_DOMAIN||!h(i.JCHAT_DOMAIN)||i.NODE_ENV===`production`&&!m(i.JCHAT_DOMAIN))throw Error(`A valid installed JChat public domain is required for production deployments.`);else if(!Q(o,i.JCHAT_DOMAIN||`localhost`,{publicKeyPem:r}))a=!1,n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`));else{let t=Z(o);if(i.NODE_ENV===`production`&&t?.environment===`development`)a=!1,n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`));else{let n=o.split(`.`),r=n[2]||``;if(r&&t){let a=e.createHmac(`sha256`,r).update(`${n[0]}.${n[1]}:${t.domain}:${t.installationId}`).digest(`hex`);i.JCHAT_COOKIE_SECRET+=a}}}}catch{a=!1,i={...t,JCHAT_PORT:t.JCHAT_PORT?parseInt(String(t.JCHAT_PORT)):3e3},n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`))}return i.SYSTEM_BOOTSTRAP_COMPLETE=a,i}function q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function J(e){return typeof e==`string`&&e.trim().length>0}function Y(e){if(!e)return null;try{return JSON.parse(Buffer.from(e,`base64url`).toString(`utf8`))}catch{return null}}function X(e){let t=e.split(`.`);if(t.length!==3)return null;let n=Y(t[0]);return!q(n)||n.alg!==`RS256`||n.typ!==`JWT`?null:{alg:`RS256`,typ:`JWT`}}function Ee(e){if(!q(e))return null;let{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,plan:s,entitlements:c,purchaseProvider:l,environment:u,routingMode:d,iat:f}=e;if(!J(t)||!J(n)||!J(r)||!J(i)||!J(a)||!J(o)||s!==`standard`&&s!==`extended`||!Array.isArray(c)||!c.every(J)||l!==`envato`&&l!==`jchat`||u!==`production`&&u!==`development`||d!==`path`&&d!==`subdomain`||typeof f!=`number`||!Number.isSafeInteger(f)||f<0)return null;let h;try{h=p(i)}catch{return null}if(u===`development`&&!h.isLocalhost||u===`production`&&!m(i))return null;let g=c.includes(`site.create.multi`);return s===`extended`&&!g||s===`standard`&&g?null:{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,plan:s,entitlements:c,purchaseProvider:l,environment:u,routingMode:d,iat:f}}function Z(e){let t=e.split(`.`);return t.length!==3||!t.every(Boolean)||!X(e)?null:Ee(Y(t[1]))}function Q(t,n,r){if(!t||t===`YOUR_LICENSE_KEY_PLACEHOLDER`)return!1;try{let i=t.split(`.`);if(i.length!==3||!X(t))return!1;let a=Z(t);if(!a)return!1;let o=r.issuer??Bun.env.JCHAT_LICENSE_ISSUER??`https://license.jchatplatform.com`,s=r.audience??Bun.env.JCHAT_LICENSE_AUDIENCE??`jchat-chat`;if(a.iss!==o||a.aud!==s)return!1;let c=p(n).host,l=p(a.domain).host;if(c!==l&&(a.routingMode!==`subdomain`||!c.endsWith(`.${l}`)))return!1;let u=Buffer.from(`${i[0]}.${i[1]}`),d=Buffer.from(i[2].replace(/-/g,`+`).replace(/_/g,`/`),`base64`);return e.verify(`sha256`,u,r.publicKeyPem,d)}catch{return!1}}function De(e,t,n,r){if(r&&!Q(e,t,r))return!1;let i=Z(e);return i?i.installationId===n:!1}function Oe(e,t,n,r){return Q(e,t,r)?Z(e)?.entitlements?.includes(n)??!1:!1}function $(e,t=`localhost`){if(g(t))return!0;if(!e)return!1;let n=Z(e);return n?n.environment===`development`?g(t):n.plan===`extended`||(n.entitlements?.includes(`site.create.multi`)??!1):!1}export{u as DEFAULT_LICENSE_AUDIENCE,l as DEFAULT_LICENSE_ISSUER,c as DEFAULT_LICENSE_SERVER_URL,x as DEFAULT_STATE_PATH,K as ServerConfigSchema,M as activateRegistry,S as createIdentity,v as findRepoRoot,_ as getRootDomain,j as getRuntimeStatus,Oe as hasEntitlement,ue as installAndActivate,h as isAllowedConfiguredDomain,se as isApplicationDisabled,g as isLocalhostDomain,ee as isMatchingDomain,$ as isMultiSiteAllowed,m as isPublicDomain,ce as isRuntimeBlocked,de as jchatRuntime,Te as loadServerContext,p as normalizeDomain,Z as parsePayload,w as readState,C as readTokenSync,ae as recordAudit,te as resolveActivationDomain,b as resolveDataDir,ne as resolveRuntimePaths,A as setRuntimeStatus,P as startRuntimeSupervisor,ie as updateInstanceToken,De as verifyEntitlement,Q as verifySignature,T as writeState};
|
|
1
|
+
import e from"node:crypto";import t from"node:net";import n from"node:fs/promises";import r from"node:path";import i from"node:fs";import{Elysia as a}from"elysia";const o=`https://license.jchatcloud.com`,s=`https://license.jchatcloud.com`,c=`jchat-chat`,l=new Set([`localhost`,`127.0.0.1`,`::1`]),u=[`.local`,`.test`,`.invalid`,`.example`];function d(e){let t=e.trim();if(!t)throw Error(`Instance domain is required`);let n=new URL(t.includes(`://`)?t:`https://${t}`).hostname.toLowerCase().replace(/\.$/,``),r=n===`www.localhost`?`localhost`:n.replace(/^www\./,``);if(!r||r.includes(`@`)||/\s/.test(r))throw Error(`Invalid instance domain`);return{host:r,isLocalhost:l.has(r)}}function f(e){let{host:n,isLocalhost:r}=d(e);return!r&&t.isIP(n)===0&&n.includes(`.`)&&!u.some(e=>n.endsWith(e))}function p(e){try{return d(e).isLocalhost||f(e)}catch{return!1}}function m(e){try{return d(e).isLocalhost}catch{return!1}}function h(e){let{host:n,isLocalhost:r}=d(e);if(r||t.isIP(n)!==0)return null;let i=n.split(`.`);if(i.length<=2)return n;let a=new Set([`co`,`com`,`org`,`net`,`gov`,`edu`,`ac`,`sch`]),o=i.at(-1)??``,s=i.at(-2)??``;return a.has(s)&&o.length===2&&i.length>=3?i.slice(-3).join(`.`):i.slice(-2).join(`.`)}function g(e,t){try{let n=d(e).host,r=d(t).host;if(n===r||n.endsWith(`.${r}`))return!0;let i=h(n),a=h(r);return!!(i&&a&&i===a)}catch{return!1}}function _(e,t,n,r=`current-host`){let i=d(e).host;return n===`subdomain`&&t&&r===`parent-domain`?h(i)??i:i}function v(e={}){let t=r.resolve(e.cwd??process.cwd()),n=e.env??Bun.env,i=b(t);return{repoRoot:i,runtimeRoot:t,uploadsDir:x(n.JCHAT_UPLOADS_DIR,t,i,`uploads`,`uploads`),addonBundlesDir:x(n.JCHAT_ADDONS_DIR,t,i,`addons`,`addons`),addonSourceDir:i?r.join(i,`addons`):r.join(t,`addons`),migrationsDir:x(n.JCHAT_MIGRATIONS_DIR,t,i,r.join(`packages`,`db`,`migrations`),`migrations`),dataDir:x(n.JCHAT_DATA_DIR,t,i,`data`,`data`)}}function y(e){return i.existsSync(r.join(e,`package.json`))&&i.existsSync(r.join(e,`apps`))&&i.existsSync(r.join(e,`packages`))}function b(e){let t=r.resolve(e);for(;;){if(y(t))return t;let e=r.dirname(t);if(e===t)return null;t=e}}function x(e,t,n,i,a){return e?r.resolve(e):n?r.join(n,i):r.join(t,a)}function S(){return v().dataDir}const C=r.join(S(),`instance.json`);function w(){return{installationId:Bun.randomUUIDv7()}}function T(e=C){try{if(!i.existsSync(e))return``;let t=i.readFileSync(e,`utf8`);return JSON.parse(t)?.instanceToken??``}catch{return``}}async function E(e=C){try{let t=Bun.file(e);if(!await t.exists())return null;let n=JSON.parse(await t.text());if(!n||typeof n!=`object`)return null;let r=n;return r.version!==1||typeof r.installationId!=`string`||!r.installationId||typeof r.instanceToken!=`string`||!r.instanceToken?null:r}catch{return null}}async function D(e,t=C){let i=r.dirname(t);await n.mkdir(i,{recursive:!0});let a=`${t}.${process.pid}.${Bun.randomUUIDv7()}.tmp`;await Bun.write(a,`${JSON.stringify(e,null,2)}\n`),await n.rename(a,t)}async function ee(e,t=C){let n=await E(t);if(!n)throw Error(`Local license state is missing`);await D({...n,instanceToken:e,lastSuccessfulCheckAt:new Date().toISOString()},t)}function O(e){if(e)return e.length<=8?`redacted`:`${e.slice(0,4)}…${e.slice(-4)}`}async function te(e,t,i=r.join(S(),`license-events.log`)){await n.mkdir(r.dirname(i),{recursive:!0});let a=JSON.stringify({at:new Date().toISOString(),event:e,domain:O(t.domain),installationId:O(t.installationId),reference:O(t.reference)});await n.appendFile(i,`${a}\n`,`utf8`)}let k=`unavailable`,A=`startup`,j;function ne(e){j=e}function M(t,n=e.randomUUID()){k=t,A=n,t===`invalid`&&j?.()}function N(){return{status:k,reference:A}}function P(){return k===`invalid`}function F(e=Bun.env.NODE_ENV===`production`){return!!(k===`invalid`||e&&k===`unavailable`)}function I(){return Bun.env.JCHAT_LICENSE_SERVER_URL||(Bun.env.NODE_ENV===`development`||Bun.env.NODE_ENV===`test`?`http://localhost:4100`:o)}async function L(e){try{let t=e.registryEndpoint??I(),n=await(e.fetchImpl??fetch)(`${t}/api/v1/activate`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({purchaseCode:e.purchaseCode,domain:d(e.domain).host,siteSlug:e.siteSlug,domainScope:e.domainScope,provider:e.provider,routingMode:e.routingMode,installationId:e.installationId,environment:d(e.domain).isLocalhost?`development`:`production`})}),r=await n.json();return n.ok===!1||!r.success||!r.token?{success:!1,message:r.message??`This license is already active on another domain. Please deactivate it first.`}:{success:!0,buyer:r.buyer??`Customer`,token:r.token,domain:r.domain??d(e.domain).host}}catch{return{success:!1,message:`Failed to connect to the central registry. Please try again later.`}}}async function R(e,t,n,r,i,a=`envato`,o=fetch){let s=w();try{let c=await L({purchaseCode:e,domain:t,siteSlug:n,domainScope:r,routingMode:i,provider:a,installationId:s.installationId,fetchImpl:o});return c.success?{valid:!0,buyer:c.buyer,token:c.token,domain:c.domain,state:s}:{valid:!1,error:c.message}}catch{return{valid:!1,error:`Failed to verify license. Check your internet connection.`}}}const z=new Set([`/health`,`/api/v1/license/status`]);async function B(e={}){if(!(e.isPrimaryWorker??(!Bun.env.WORKER_ID||Bun.env.WORKER_ID===`0`)))return;let t=e.domain??`localhost`;if(e.onInvalidate&&ne(e.onInvalidate),m(t)){M(`valid`);return}let n=await E();if(!n?.instanceToken){M(e.isBootstrapComplete?`valid`:`unavailable`);return}if(e.signingKey&&!Z(n.instanceToken,t,{publicKeyPem:e.signingKey})){M(`invalid`,`domain-mismatch`);return}if(await W(e)>(m(t)?1/0:$(n.instanceToken))){M(`invalid`,`multi-site-unauthorized`);return}M(`valid`)}function V(e={}){B(e);let t=e.allowedPaths??z;return new a({name:`jchatRuntime`}).derive({as:`global`},()=>({runtimeStatus:N()})).onBeforeHandle({as:`global`},async({request:n,set:r})=>{if(n.method===`POST`&&n.url.includes(`/owner/sites`)){let t=e.domain??`localhost`,n=await E(),i=m(t)?1/0:$(n?.instanceToken);if(await W(e)>=i)return r.status=403,{status:`license_restricted`,message:`Site creation limit reached (${i} site${i===1?``:`s`} allowed on your license). Please upgrade your license to create additional sites.`,reference:`multi-site-unauthorized`}}return U(n,r,t)})}function H(e,t,n){return n.has(e)?!0:t===`invalid`?e===`/install/upgrade-license`:t===`unavailable`?e===`/install`||e.startsWith(`/install/`)||e.startsWith(`/assets/`)||e.startsWith(`/_`)||e===`/favicon.ico`:!1}function U(e,t,n=z,r=Bun.env.NODE_ENV===`production`){let{status:i,reference:a}=N(),o=e.headers.get(`host`)?.split(`:`)[0]?.toLowerCase();if(o&&f(o)&&m(Bun.env.JCHAT_DOMAIN||`localhost`))return t.status=403,{status:`license_invalid`,message:`Public domain deployment requires a valid license matching this domain.`,reference:`public-host-requires-license`};if(i!==`valid`&&i!==`unreachable-fallback`&&(r||i!==`unavailable`)&&!H(new URL(e.url).pathname.replace(/\/+$/,``)||`/`,i,n))return t.status=503,i===`unavailable`?{status:`license_unavailable`,message:`This JChat installation is not activated. Please complete the installation.`,reference:a}:{status:`license_invalid`,message:`This JChat installation is disabled because its license is invalid.`,reference:a}}async function W(e){let t=Bun.env.DATABASE_URL??process.env.DATABASE_URL;if(t&&typeof Bun<`u`&&`SQL`in Bun)try{let e=new Bun.SQL(t),n=await e`SELECT count(*)::int as count FROM sites WHERE deleted_at IS NULL`;if(await e.close(),n?.[0]?.count!==void 0&&n[0].count!==null)return Number(n[0].count)}catch{}if(e.getSiteCount)try{return await e.getSiteCount()}catch{return 0}return 0}function G(t,n=`production`,r){let i={...t},a=!0;typeof i.JCHAT_COOKIE_SECRET!=`string`&&(i.JCHAT_COOKIE_SECRET=``);try{let t=T();if(n===`developer-sandbox`)a=!0;else if(!t)a=!1,n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`));else{let o=typeof i.JCHAT_DOMAIN==`string`?i.JCHAT_DOMAIN:void 0,s=typeof i.NODE_ENV==`string`?i.NODE_ENV:void 0;if(!o||!p(o)||s===`production`&&!f(o))throw Error(`A valid installed JChat public domain is required for production deployments.`);if(!Z(t,o||`localhost`,{publicKeyPem:r}))a=!1,n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`));else{let r=X(t);if(s===`production`&&r?.environment===`development`)a=!1,n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`));else{let n=t.split(`.`),a=n[2]||``;if(a&&r){let t=e.createHmac(`sha256`,a).update(`${n[0]}.${n[1]}:${r.domain}:${r.installationId}`).digest(`hex`);i.JCHAT_COOKIE_SECRET+=t}}}}}catch{a=!1,n===`production`&&(i.JCHAT_COOKIE_SECRET=e.randomBytes(64).toString(`hex`))}return i.SYSTEM_BOOTSTRAP_COMPLETE=a,i}function re(e,t=`production`,n){return G(e,t,n)}function K(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function q(e){return typeof e==`string`&&e.trim().length>0}function J(e){if(!e)return null;try{return JSON.parse(Buffer.from(e,`base64url`).toString(`utf8`))}catch{return null}}function Y(e){let t=e.split(`.`);if(t.length!==3)return null;let n=J(t[0]);return!K(n)||n.alg!==`RS256`||n.typ!==`JWT`?null:{alg:`RS256`,typ:`JWT`}}function ie(e){if(!K(e))return null;let{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,plan:s,entitlements:c,purchaseProvider:l,environment:u,routingMode:p,iat:m,exp:h,isTrial:g,stages:_,channel:v,limits:y}=e;if(!q(t)||!q(n)||!q(r)||!q(i)||!q(a)||!q(o)||!q(s)||!Array.isArray(c)||!c.every(q)||l!==`envato`&&l!==`jchat`||u!==`production`&&u!==`development`||p!==`path`&&p!==`subdomain`||typeof m!=`number`||!Number.isSafeInteger(m)||m<0||h!==void 0&&(typeof h!=`number`||!Number.isSafeInteger(h)||h<0)||g!==void 0&&typeof g!=`boolean`)return null;let b=new Set([`stable`,`beta`,`experimental`]),x;if(_!==void 0){if(!Array.isArray(_)||!_.every(e=>typeof e==`string`&&b.has(e)))return null;x=_}let S=new Set([`stable`,`lts`]),C;if(v!==void 0){if(typeof v!=`string`||!S.has(v))return null;C=v}let w;if(y!==void 0){if(!K(y)||typeof y.maxSites!=`number`||!Number.isSafeInteger(y.maxSites))return null;w={maxSites:y.maxSites,...typeof y.maxRoomsPerSite==`number`&&Number.isSafeInteger(y.maxRoomsPerSite)?{maxRoomsPerSite:y.maxRoomsPerSite}:{}}}let T;try{T=d(i)}catch{return null}return u===`development`&&!T.isLocalhost||u===`production`&&!f(i)?null:{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,plan:s,entitlements:c,purchaseProvider:l,environment:u,routingMode:p,iat:m,...h===void 0?{}:{exp:h},...g===void 0?{}:{isTrial:g},...x===void 0?{}:{stages:x},...C===void 0?{}:{channel:C},...w===void 0?{}:{limits:w}}}function X(e){let t=e.split(`.`);return t.length!==3||!t.every(Boolean)||!Y(e)?null:ie(J(t[1]))}function Z(t,n,r){if(!t||t===`YOUR_LICENSE_KEY_PLACEHOLDER`)return!1;try{let i=t.split(`.`);if(i.length!==3||!Y(t))return!1;let a=X(t);if(!a)return!1;let o=r.issuer??Bun.env.JCHAT_LICENSE_ISSUER??`https://license.jchatcloud.com`,s=r.audience??Bun.env.JCHAT_LICENSE_AUDIENCE??`jchat-chat`;if(a.iss!==o||a.aud!==s)return!1;let c=d(n).host,l=d(a.domain).host;if(c!==l&&(a.routingMode!==`subdomain`||!c.endsWith(`.${l}`)))return!1;let u=Buffer.from(`${i[0]}.${i[1]}`),f=Buffer.from(i[2].replace(/-/g,`+`).replace(/_/g,`/`),`base64`);return e.verify(`sha256`,u,r.publicKeyPem,f)}catch{return!1}}function ae(e,t,n,r){if(r&&!Z(e,t,r))return!1;let i=X(e);return i?i.installationId===n:!1}function oe(e,t,n,r){return Z(e,t,r)?X(e)?.entitlements?.includes(n)??!1:!1}function se(e,t=`localhost`,n){if(n&&(!e||!Z(e,t,n))||!e)return!1;let r=X(e);return r?r.environment===`development`&&m(t)?!0:$(e)>1||(r.entitlements?.includes(`site.create.multi`)??!1):!1}function Q(e,t){return e?X(e)?.entitlements?.includes(t)??!1:!1}function ce(e,t){return t===`stable`?!0:e?(X(e)?.stages??[`stable`]).includes(t):!1}function $(e){return e?X(e)?.limits?.maxSites??1:1}function le(e){return e?X(e)?.channel??`stable`:`stable`}function ue(e){return $(e)>1||Q(e,`site.create.multi`)}export{c as DEFAULT_LICENSE_AUDIENCE,s as DEFAULT_LICENSE_ISSUER,o as DEFAULT_LICENSE_SERVER_URL,C as DEFAULT_STATE_PATH,L as activateRegistry,ue as canCreateMultiSite,w as createIdentity,b as findRepoRoot,$ as getMaxSites,le as getReleaseChannel,h as getRootDomain,N as getRuntimeStatus,Q as hasCapability,oe as hasEntitlement,ce as hasStageAccess,R as installAndActivate,p as isAllowedConfiguredDomain,P as isApplicationDisabled,m as isLocalhostDomain,g as isMatchingDomain,se as isMultiSiteAllowed,f as isPublicDomain,F as isRuntimeBlocked,V as jchatRuntime,re as loadServerContext,d as normalizeDomain,X as parsePayload,E as readState,T as readTokenSync,te as recordAudit,_ as resolveActivationDomain,S as resolveDataDir,v as resolveRuntimePaths,G as sealServerContext,M as setRuntimeStatus,B as startRuntimeSupervisor,ee as updateInstanceToken,ae as verifyEntitlement,Z as verifySignature,D as writeState};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jchat/runtime",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.25",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Runtime support package for JChat deployments.",
|
|
6
6
|
"keywords": [
|
|
@@ -8,12 +8,8 @@
|
|
|
8
8
|
"jchat",
|
|
9
9
|
"runtime"
|
|
10
10
|
],
|
|
11
|
+
"homepage": "https://jchatcloud.com",
|
|
11
12
|
"license": "SEE LICENSE IN LICENSE",
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/mjjabarullah/jchat.git",
|
|
15
|
-
"directory": "packages/runtime"
|
|
16
|
-
},
|
|
17
13
|
"files": [
|
|
18
14
|
"dist",
|
|
19
15
|
"README.md",
|