@jchat/runtime 1.0.0-alpha.1 → 1.0.0-alpha.11
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 +50 -0
- package/LICENSE +18 -122
- package/README.md +6 -4
- package/dist/index.d.mts +75 -26
- package/dist/index.mjs +1 -1
- package/package.json +6 -6
- package/src/audit.test.ts +0 -25
- package/src/audit.ts +0 -25
- package/src/check.test.ts +0 -77
- package/src/check.ts +0 -202
- package/src/domain.test.ts +0 -45
- package/src/domain.ts +0 -75
- package/src/env.test.ts +0 -146
- package/src/env.ts +0 -261
- package/src/index.test.ts +0 -117
- package/src/index.ts +0 -206
- package/src/install.ts +0 -62
- package/src/paths.test.ts +0 -49
- package/src/paths.ts +0 -74
- package/src/runtime.test.ts +0 -21
- package/src/runtime.ts +0 -31
- package/src/state.test.ts +0 -54
- package/src/state.ts +0 -129
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @jchat/runtime
|
|
2
2
|
|
|
3
|
+
## 1.0.0-alpha.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4cdf5a9: Consolidate `Provider` and `RoutingMode` types and unify runtime type exports across domain, check, install, and plugin modules.
|
|
8
|
+
- 8ce0db9: Add `jchatRuntime` Elysia plugin encapsulating background license supervision, boot check throttling (24h), configurable check intervals (`JCHAT_LICENSE_CHECK_INTERVAL_DAYS`), timer unref on exit, and fail-closed route guards.
|
|
9
|
+
|
|
10
|
+
## 1.0.0-alpha.10
|
|
11
|
+
|
|
12
|
+
## 1.0.0-alpha.9
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 7aef82c: Refactor runtime registry endpoint defaults to focus strictly on cryptographic licensing and support environment-aware overrides.
|
|
17
|
+
|
|
18
|
+
## 1.0.0-alpha.8
|
|
19
|
+
|
|
20
|
+
## 1.0.0-alpha.7
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- 7cef24c: - Enhance addon package diagnostics with semantic version compatibility validation for JChat host and Addon SDK
|
|
25
|
+
- Add support for local workspace development, sandbox test harnesses, and distribution packaging
|
|
26
|
+
- Improve runtime license verification and multi-environment path resolution
|
|
27
|
+
- Update addon generator templates with 20-language i18n support, standard scripts, and type definitions
|
|
28
|
+
|
|
29
|
+
## 1.0.0-alpha.6
|
|
30
|
+
|
|
31
|
+
## 1.0.0-alpha.5
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- 457b0fc: Refactor server configuration: merged environment schema into `server-context.ts` in `@jchat/runtime` and updated `loadServerContext` to properly accept `publicKeyPem`. Updated `@jchat/chat` to pass `SIGNING_ROOT_KEY` into `loadServerContext`.
|
|
36
|
+
- 93d798f: Fix `ServerConfigSchema` export
|
|
37
|
+
|
|
38
|
+
## 1.0.0-alpha.4
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- 457b0fc: Refactor server configuration: merged environment schema into `server-context.ts` in `@jchat/runtime` and updated `loadServerContext` to properly accept `publicKeyPem`. Updated `@jchat/chat` to pass `SIGNING_ROOT_KEY` into `loadServerContext`.
|
|
43
|
+
- 93d798f: Fix `ServerConfigSchema` export
|
|
44
|
+
|
|
45
|
+
## 1.0.0-alpha.3
|
|
46
|
+
|
|
47
|
+
## 1.0.0-alpha.2
|
|
48
|
+
|
|
49
|
+
### Patch Changes
|
|
50
|
+
|
|
51
|
+
- 448e6cd: Move `makeTypesEnv` from runtime to shared package
|
|
52
|
+
|
|
3
53
|
## 1.0.0-alpha.1
|
|
4
54
|
|
|
5
55
|
### Patch Changes
|
package/LICENSE
CHANGED
|
@@ -1,127 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
CUSTOM COMMERCIAL END USER LICENSE AGREEMENT (EULA)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 JChat Project. All rights reserved.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Runtime open-source software and does not grant any rights except those stated below.
|
|
5
|
+
1. DEFINITIONS
|
|
6
|
+
"Software" refers to this npm package and its source code.
|
|
7
|
+
"Authorized Application" refers strictly to the application known as "JChat".
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
2. LICENSE GRANT
|
|
10
|
+
Subject to the terms of this Agreement and payment of any applicable fees, the
|
|
11
|
+
Licensor grants you a non-exclusive, non-transferable, revocable license to
|
|
12
|
+
download, install, and use the Software.
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
3. RESTRICTIONS ON USE
|
|
15
|
+
You are expressly prohibited from using this Software, in whole or in part,
|
|
16
|
+
in any application, product, or service other than the Authorized Application (JChat).
|
|
17
|
+
Any deployment, linking, execution, or inclusion of this Software into an
|
|
18
|
+
application that is not JChat constitutes a breach of this contract and
|
|
19
|
+
an infringement of copyright law.
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
constitutes a perpetual Vendor License for the purchased edition, provided the purchase remains
|
|
18
|
-
valid and compliant with the applicable marketplace terms.
|
|
19
|
-
|
|
20
|
-
"Authorized Deployment" means a JChat installation that complies with its Vendor License,
|
|
21
|
-
including its domain, installation, environment, and usage restrictions.
|
|
22
|
-
|
|
23
|
-
"You" means the individual or legal entity that owns a Vendor License or is acting for its
|
|
24
|
-
authorized license holder.
|
|
25
|
-
|
|
26
|
-
2. Limited License Grant
|
|
27
|
-
|
|
28
|
-
Subject to continued compliance with this agreement and the applicable Vendor License, you receive
|
|
29
|
-
a limited, non-exclusive, non-transferable except as expressly allowed by Section 2(d),
|
|
30
|
-
non-sublicensable, and revocable license to:
|
|
31
|
-
|
|
32
|
-
a. download the Runtime from npm or another distribution channel authorized by JChat;
|
|
33
|
-
|
|
34
|
-
b. install and execute the Runtime solely as an unmodified dependency of an Authorized Deployment;
|
|
35
|
-
|
|
36
|
-
c. make reasonable technical copies required for private package caches, continuous integration,
|
|
37
|
-
Docker or other deployment images, staging environments, disaster recovery, and backups for the
|
|
38
|
-
Authorized Deployment; and
|
|
39
|
-
|
|
40
|
-
d. provide those copies to a client only as an integral part of an end product and only when that
|
|
41
|
-
transfer is permitted by the applicable Vendor License. Distributing JChat (including this
|
|
42
|
-
Runtime as a dependency) as a purchased item on Envato Market or another authorized marketplace
|
|
43
|
-
constitutes permitted redistribution under this section, provided the recipient is the
|
|
44
|
-
end-product client and not a downstream redistributor.
|
|
45
|
-
|
|
46
|
-
A vendor-issued development license may additionally authorize local development use according to
|
|
47
|
-
the conditions encoded in that license. No right is granted to use the Runtime independently from
|
|
48
|
-
JChat or without a valid Vendor License.
|
|
49
|
-
|
|
50
|
-
3. Restrictions
|
|
51
|
-
|
|
52
|
-
Except where applicable law does not permit a restriction, you must not:
|
|
53
|
-
|
|
54
|
-
a. sell, rent, sublicense, republish, publicly mirror, or distribute the Runtime as a standalone or
|
|
55
|
-
reusable package;
|
|
56
|
-
|
|
57
|
-
b. modify, adapt, translate, or create derivative works of the Runtime;
|
|
58
|
-
|
|
59
|
-
c. reverse engineer, decompile, disassemble, or otherwise attempt to discover non-public source
|
|
60
|
-
code or implementation details;
|
|
61
|
-
|
|
62
|
-
d. remove or alter copyright, license, attribution, integrity, entitlement, or ownership notices;
|
|
63
|
-
|
|
64
|
-
e. bypass, disable, interfere with, or misrepresent activation, entitlement, domain, installation,
|
|
65
|
-
signature, integrity, or registry verification;
|
|
66
|
-
|
|
67
|
-
f. use the Runtime to build, enable, or operate an unlicensed JChat deployment; or
|
|
68
|
-
|
|
69
|
-
g. use the Runtime as a component of a competing standalone product or licensing system.
|
|
70
|
-
|
|
71
|
-
Internal caches and deployment artifacts allowed by Section 2 are not prohibited redistribution,
|
|
72
|
-
provided they remain access-controlled and are used only for the Authorized Deployment.
|
|
73
|
-
|
|
74
|
-
4. Ownership
|
|
75
|
-
|
|
76
|
-
The Runtime is licensed, not sold. JChat and its licensors retain all title, copyright, patent,
|
|
77
|
-
trade-secret, and other intellectual-property rights in the Runtime. No implied license is granted.
|
|
78
|
-
|
|
79
|
-
5. Third-Party Software
|
|
80
|
-
|
|
81
|
-
Third-party components, if any, remain governed by their respective licenses. This agreement does
|
|
82
|
-
not reduce rights granted under a third-party license.
|
|
83
|
-
|
|
84
|
-
6. Updates, Services, and Vendor License Terms
|
|
85
|
-
|
|
86
|
-
This agreement does not promise updates, support, availability, or continued registry access.
|
|
87
|
-
Purchase, activation, installation, replacement, and service terms remain governed by the
|
|
88
|
-
applicable Vendor License and written JChat terms. If those terms conflict with this agreement for
|
|
89
|
-
the Runtime, the more specific written terms supplied by JChat control.
|
|
90
|
-
|
|
91
|
-
7. Term and Termination
|
|
92
|
-
|
|
93
|
-
This agreement remains effective while you comply with it and maintain the required Vendor License.
|
|
94
|
-
For one-time marketplace purchases (Envato Market, Codester, or equivalent), the Vendor License
|
|
95
|
-
does not expire; this agreement terminates only if the purchase is invalidated by the marketplace,
|
|
96
|
-
or if you materially breach this agreement. For subscription or time-limited licenses issued
|
|
97
|
-
directly by JChat Hub, this agreement terminates when that license expires or is revoked. On
|
|
98
|
-
termination, you must stop using the Runtime and remove all copies except copies retained solely
|
|
99
|
-
where applicable law requires archival retention.
|
|
100
|
-
|
|
101
|
-
Sections 3, 4, 5, 8, 9, and 10 survive termination.
|
|
102
|
-
|
|
103
|
-
8. Disclaimer of Warranty
|
|
104
|
-
|
|
105
|
-
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE RUNTIME IS PROVIDED "AS IS" AND "AS
|
|
106
|
-
AVAILABLE", WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY,
|
|
107
|
-
INCLUDING WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
108
|
-
PURPOSE, ACCURACY, OR RELIABILITY.
|
|
109
|
-
|
|
110
|
-
9. Limitation of Liability
|
|
111
|
-
|
|
112
|
-
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, JCHAT, MD. JABARULLAH, AND THEIR SUPPLIERS WILL
|
|
113
|
-
NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, CONSEQUENTIAL, OR PUNITIVE DAMAGES,
|
|
114
|
-
OR FOR LOSS OF DATA, PROFITS, REVENUE, BUSINESS, OR GOODWILL, ARISING FROM OR RELATED TO THE
|
|
115
|
-
RUNTIME, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
116
|
-
|
|
117
|
-
10. General
|
|
118
|
-
|
|
119
|
-
The governing law, venue, and dispute terms stated in the applicable Vendor License or written
|
|
120
|
-
JChat purchase terms apply to this agreement. If any provision is unenforceable, it will be limited
|
|
121
|
-
to the minimum extent necessary, and the remaining provisions will continue in effect. A waiver
|
|
122
|
-
must be in writing and applies only to the specific instance waived.
|
|
123
|
-
|
|
124
|
-
The JChat name, logos, and related marks are not licensed except for truthful identification of
|
|
125
|
-
compatible products and services.
|
|
126
|
-
|
|
127
|
-
For permissions outside this agreement, contact JChat through https://jchat.com.
|
|
21
|
+
4. NO REDISTRIBUTION
|
|
22
|
+
You may not sub-license, rent, lease, or publicly redistribute this Software
|
|
23
|
+
to any third party without explicit written consent from the Licensor.
|
package/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# @jchat/runtime
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Core runtime environment and instance state management package for JChat platform distributions.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@jchat/runtime` provides low-level runtime foundations, instance state persistence, and host communication utilities for JChat server applications and licensed environments.
|
|
6
8
|
|
|
7
9
|
## Requirements
|
|
8
10
|
|
|
9
|
-
-
|
|
11
|
+
- Bun 1.4.0+
|
|
10
12
|
|
|
11
13
|
## License
|
|
12
14
|
|
|
13
|
-
Proprietary —
|
|
15
|
+
Proprietary — All rights reserved. See the [LICENSE](./LICENSE) file included in this distribution for full terms and conditions.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Elysia } from "elysia";
|
|
1
2
|
import * as z from "zod";
|
|
2
3
|
//#region src/domain.d.ts
|
|
4
|
+
type Provider = "envato" | "jchat";
|
|
3
5
|
type Plan = "standard" | "extended";
|
|
4
6
|
type RoutingMode = "path" | "subdomain";
|
|
5
7
|
type DomainScope = "current-host" | "parent-domain";
|
|
@@ -34,15 +36,13 @@ type CheckResult = {
|
|
|
34
36
|
error: string;
|
|
35
37
|
reference: string;
|
|
36
38
|
};
|
|
37
|
-
type RegistryProvider = "envato" | "jchat";
|
|
38
|
-
type RegistryRoutingMode = "path" | "subdomain";
|
|
39
39
|
type ActivationInput = {
|
|
40
40
|
purchaseCode: string;
|
|
41
41
|
domain: string;
|
|
42
42
|
siteSlug?: string;
|
|
43
43
|
domainScope?: "current-host" | "parent-domain";
|
|
44
|
-
provider:
|
|
45
|
-
routingMode:
|
|
44
|
+
provider: Provider;
|
|
45
|
+
routingMode: RoutingMode;
|
|
46
46
|
installationId: string;
|
|
47
47
|
installationPublicKey: string;
|
|
48
48
|
machineFingerprint: string;
|
|
@@ -62,7 +62,7 @@ declare function activateRegistry(input: ActivationInput): Promise<ActivationRes
|
|
|
62
62
|
type CheckInput = {
|
|
63
63
|
token: string;
|
|
64
64
|
domain: string;
|
|
65
|
-
routingMode:
|
|
65
|
+
routingMode: RoutingMode;
|
|
66
66
|
installationId: string;
|
|
67
67
|
machineFingerprint: string;
|
|
68
68
|
registryEndpoint?: string;
|
|
@@ -71,7 +71,6 @@ type CheckInput = {
|
|
|
71
71
|
fetchImpl?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
72
72
|
localTokenValidator?: (token: string, domain: string, installationId: string, machineFingerprint: string) => boolean;
|
|
73
73
|
};
|
|
74
|
-
declare function checkRegistry(input: CheckInput): Promise<CheckResult>;
|
|
75
74
|
//#endregion
|
|
76
75
|
//#region src/state.d.ts
|
|
77
76
|
type LocalState = {
|
|
@@ -86,7 +85,7 @@ type LocalState = {
|
|
|
86
85
|
declare const DEFAULT_STATE_PATH: string;
|
|
87
86
|
declare function createMachineFingerprint(): string;
|
|
88
87
|
declare function createIdentity(machineFingerprint?: string): {
|
|
89
|
-
installationId:
|
|
88
|
+
installationId: string;
|
|
90
89
|
machineFingerprint: string;
|
|
91
90
|
privateKeyPem: string;
|
|
92
91
|
publicKeyPem: string;
|
|
@@ -95,17 +94,6 @@ declare function readTokenSync(filePath?: string): string;
|
|
|
95
94
|
declare function readState(filePath?: string): Promise<LocalState | null>;
|
|
96
95
|
declare function writeState(state: LocalState, filePath?: string): Promise<void>;
|
|
97
96
|
declare function updateInstanceToken(instanceToken: string, statePath?: string): Promise<void>;
|
|
98
|
-
declare function acquireProcessLock(statePath?: string): Promise<() => Promise<void>>;
|
|
99
|
-
//#endregion
|
|
100
|
-
//#region src/runtime.d.ts
|
|
101
|
-
type RuntimeStatus$1 = "valid" | "unreachable-fallback" | "invalid" | "unavailable";
|
|
102
|
-
declare function registerInvalidationHandler(handler: () => void): void;
|
|
103
|
-
declare function setRuntimeStatus(status: RuntimeStatus$1, reference?: string): void;
|
|
104
|
-
declare function getRuntimeStatus(): {
|
|
105
|
-
status: RuntimeStatus$1;
|
|
106
|
-
reference: string;
|
|
107
|
-
};
|
|
108
|
-
declare function isApplicationDisabled(): boolean;
|
|
109
97
|
//#endregion
|
|
110
98
|
//#region src/install.d.ts
|
|
111
99
|
type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
@@ -119,7 +107,7 @@ type LicenseVerificationResult = {
|
|
|
119
107
|
valid: false;
|
|
120
108
|
error: string;
|
|
121
109
|
};
|
|
122
|
-
declare function installAndActivate(purchaseCode: string, domain: string, siteSlug: string | undefined, domainScope: DomainScope | undefined, routingMode: RoutingMode, provider?:
|
|
110
|
+
declare function installAndActivate(purchaseCode: string, domain: string, siteSlug: string | undefined, domainScope: DomainScope | undefined, routingMode: RoutingMode, provider?: Provider, fetchImpl?: FetchLike): Promise<LicenseVerificationResult>;
|
|
123
111
|
//#endregion
|
|
124
112
|
//#region src/paths.d.ts
|
|
125
113
|
interface RuntimePathOptions {
|
|
@@ -135,10 +123,68 @@ declare function resolveRuntimePaths(options?: RuntimePathOptions): {
|
|
|
135
123
|
migrationsDir: string;
|
|
136
124
|
};
|
|
137
125
|
//#endregion
|
|
138
|
-
//#region src/
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
126
|
+
//#region src/runtime.d.ts
|
|
127
|
+
type RuntimeStatus$1 = "valid" | "unreachable-fallback" | "invalid" | "unavailable";
|
|
128
|
+
declare function setRuntimeStatus(status: RuntimeStatus$1, reference?: string): void;
|
|
129
|
+
declare function getRuntimeStatus(): {
|
|
130
|
+
status: RuntimeStatus$1;
|
|
131
|
+
reference: string;
|
|
132
|
+
};
|
|
133
|
+
declare function isApplicationDisabled(): boolean;
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/plugin.d.ts
|
|
136
|
+
type RuntimePluginOptions = {
|
|
137
|
+
domain?: string;
|
|
138
|
+
routingMode?: RoutingMode;
|
|
139
|
+
checkIntervalDays?: number;
|
|
140
|
+
signingKey?: string;
|
|
141
|
+
isBootstrapComplete?: boolean;
|
|
142
|
+
isPrimaryWorker?: boolean;
|
|
143
|
+
allowedPaths?: Set<string>;
|
|
144
|
+
onInvalidate?: () => void;
|
|
145
|
+
};
|
|
146
|
+
declare function startRuntimeSupervisor(options?: RuntimePluginOptions): Promise<void>;
|
|
147
|
+
declare function jchatRuntime(options?: RuntimePluginOptions): Elysia<"", {
|
|
148
|
+
decorator: {};
|
|
149
|
+
store: {};
|
|
150
|
+
derive: {
|
|
151
|
+
readonly runtimeStatus: {
|
|
152
|
+
status: RuntimeStatus$1;
|
|
153
|
+
reference: string;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
resolve: {};
|
|
157
|
+
}, {
|
|
158
|
+
typebox: {};
|
|
159
|
+
error: {};
|
|
160
|
+
}, {
|
|
161
|
+
schema: {};
|
|
162
|
+
standaloneSchema: {};
|
|
163
|
+
macro: {};
|
|
164
|
+
macroFn: {};
|
|
165
|
+
parser: {};
|
|
166
|
+
response: {
|
|
167
|
+
200: {
|
|
168
|
+
status: string;
|
|
169
|
+
message: string;
|
|
170
|
+
reference: string;
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
}, {}, {
|
|
174
|
+
derive: {};
|
|
175
|
+
resolve: {};
|
|
176
|
+
schema: {};
|
|
177
|
+
standaloneSchema: {};
|
|
178
|
+
response: {};
|
|
179
|
+
}, {
|
|
180
|
+
derive: {};
|
|
181
|
+
resolve: {};
|
|
182
|
+
schema: {};
|
|
183
|
+
standaloneSchema: {};
|
|
184
|
+
response: {};
|
|
185
|
+
}>;
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/server-context.d.ts
|
|
142
188
|
declare const ServerConfigSchema: z.ZodObject<{
|
|
143
189
|
JCHAT_PORT: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
|
|
144
190
|
JCHAT_COOKIE_SECRET: z.ZodString;
|
|
@@ -179,6 +225,9 @@ declare const ServerConfigSchema: z.ZodObject<{
|
|
|
179
225
|
subdomain: "subdomain";
|
|
180
226
|
}>>;
|
|
181
227
|
JCHAT_ADDON_ENCRYPTION_KEY: z.ZodString;
|
|
228
|
+
JCHAT_LICENSE_SERVER_URL: z.ZodOptional<z.ZodURL>;
|
|
229
|
+
JCHAT_LICENSE_CHECK_INTERVAL_DAYS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
|
|
230
|
+
JCHAT_STORE_SERVER_URL: z.ZodOptional<z.ZodURL>;
|
|
182
231
|
NODE_ENV: z.ZodDefault<z.ZodEnum<{
|
|
183
232
|
development: "development";
|
|
184
233
|
production: "production";
|
|
@@ -187,9 +236,9 @@ declare const ServerConfigSchema: z.ZodObject<{
|
|
|
187
236
|
SYSTEM_BOOTSTRAP_COMPLETE: z.ZodOptional<z.ZodBoolean>;
|
|
188
237
|
}, z.core.$strip>;
|
|
189
238
|
type ServerConfig = z.infer<typeof ServerConfigSchema>;
|
|
239
|
+
declare function loadServerContext(environment: Record<string, unknown>, distribution: "production" | "developer-sandbox" | undefined, publicKeyPem: string): ServerConfig;
|
|
190
240
|
//#endregion
|
|
191
241
|
//#region src/index.d.ts
|
|
192
|
-
type PurchaseProvider = "envato" | "jchat";
|
|
193
242
|
type RuntimeEnvironment = "production" | "development";
|
|
194
243
|
type ConfigPayload = {
|
|
195
244
|
iss: string;
|
|
@@ -201,7 +250,7 @@ type ConfigPayload = {
|
|
|
201
250
|
machineFingerprint: string;
|
|
202
251
|
plan: Plan;
|
|
203
252
|
entitlements: string[];
|
|
204
|
-
purchaseProvider:
|
|
253
|
+
purchaseProvider: Provider;
|
|
205
254
|
environment: RuntimeEnvironment;
|
|
206
255
|
routingMode: RoutingMode;
|
|
207
256
|
iat: number;
|
|
@@ -217,4 +266,4 @@ declare function verifySignature(token: string, currentDomain: string, options:
|
|
|
217
266
|
declare function verifyEntitlement(token: string, currentDomain: string, installationId: string, machineFingerprint: string, options: RuntimeOptions): boolean;
|
|
218
267
|
declare function hasEntitlement(token: string, currentDomain: string, entitlement: string, options: RuntimeOptions): boolean;
|
|
219
268
|
//#endregion
|
|
220
|
-
export { ActivationInput, ActivationResult, AuditEvent, CheckInput, CheckResult, ConfigPayload, DEFAULT_STATE_PATH, DomainScope, FetchLike, LicenseVerificationResult, LocalState, NormalizedDomain, Plan,
|
|
269
|
+
export { type ActivationInput, type ActivationResult, AuditEvent, type CheckInput, type CheckResult, ConfigPayload, DEFAULT_STATE_PATH, DomainScope, FetchLike, LicenseVerificationResult, type LocalState, NormalizedDomain, Plan, Provider, RoutingMode, RuntimeEnvironment, RuntimeOptions, RuntimePluginOptions, RuntimeStatus, ServerConfig, ServerConfigSchema, activateRegistry, createIdentity, createMachineFingerprint, getRuntimeStatus, hasEntitlement, installAndActivate, isAllowedConfiguredDomain, isApplicationDisabled, isPublicDomain, jchatRuntime, loadServerContext, normalizeDomain, parsePayload, readState, readTokenSync, recordAudit, resolveActivationDomain, resolveRuntimePaths, setRuntimeStatus, startRuntimeSupervisor, updateInstanceToken, verifyEntitlement, verifySignature, writeState };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +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 a from"node:os";import{replaceKeys as o}from"string-ts";import*as s from"zod";const c=new Set([`localhost`,`127.0.0.1`,`::1`]),l=[`.local`,`.test`,`.invalid`,`.example`];function u(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:c.has(r)}}function d(e){let{host:n,isLocalhost:r}=u(e);return!r&&t.isIP(n)===0&&n.includes(`.`)&&!l.some(e=>n.endsWith(e))}function f(e){try{return u(e).isLocalhost||d(e)}catch{return!1}}function p(e){let{host:n,isLocalhost:r}=u(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 m(e,t,n,r=`current-host`){let i=u(e).host;return n===`subdomain`&&t===`extended`&&r===`parent-domain`?p(i)??i:i}function h(e){if(e)return e.length<=8?`redacted`:`${e.slice(0,4)}…${e.slice(-4)}`}async function g(e,t,i=r.resolve(process.cwd(),`data/license-events.log`)){await n.mkdir(r.dirname(i),{recursive:!0});let a=JSON.stringify({at:new Date().toISOString(),event:e,domain:h(t.domain),installationId:h(t.installationId),reference:h(t.reference)});await n.appendFile(i,`${a}\n`,`utf8`)}let _=`unavailable`,v=`startup`,y;function ee(e){y=e}function b(t,n=e.randomUUID()){_=t,v=n,t===`invalid`&&y?.()}function x(){return{status:_,reference:v}}function S(){return _===`invalid`}const C=Buffer.from(`aHR0cDovL2xvY2FsaG9zdDo0MDAw`,`base64`).toString(`utf8`);async function w(e){try{let t=await(e.fetchImpl??fetch)(`${e.registryEndpoint??C}/api/activate`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({purchaseCode:e.purchaseCode,domain:u(e.domain).host,siteSlug:e.siteSlug,domainScope:e.domainScope,provider:e.provider,routingMode:e.routingMode,installationId:e.installationId,installationPublicKey:e.installationPublicKey,machineFingerprint:e.machineFingerprint})}),n=await t.json();return t.ok===!1||!n.success||!n.token?{success:!1,message:n.message??`This license is already active on another domain. Please deactivate it first.`}:{success:!0,buyer:n.buyer??`Customer`,token:n.token,domain:n.domain??u(e.domain).host}}catch{return{success:!1,message:`Failed to connect to the central registry. Please try again later.`}}}async function te(t){let n=t.fetchImpl??fetch,r=t.localTokenValidator??((e,n,r,i)=>t.runtimeOptions?$(e,n,r,i,t.runtimeOptions):!1),i=(n,r)=>{let i=e.randomUUID();return b(`invalid`,i),g(`check_rejected`,{domain:n,reference:i},t.auditPath),{status:`invalid`,error:r,reference:i}},a=e=>{let n=u(t.domain).host;return r(t.token,n,t.installationId,t.machineFingerprint)?(b(`unreachable-fallback`),g(`check_unreachable`,{domain:n},t.auditPath),{status:`unreachable-fallback`,token:t.token,error:e}):i(n,`Local license validation failed.`)};try{let e=u(t.domain).host,r=await n(`${t.registryEndpoint??C}/api/check`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:t.token,domain:e,routingMode:t.routingMode,installationId:t.installationId,machineFingerprint:t.machineFingerprint})});if(!r.ok)return r.status>=500?a(`HTTP ${r.status}`):i(e,`License rejected by jchat service.`);let o=await r.json();if(!o.success||!o.token||!(t.runtimeOptions&&$(o.token,e,t.installationId,t.machineFingerprint,t.runtimeOptions))&&!t.localTokenValidator||t.localTokenValidator&&!t.localTokenValidator(o.token,e,t.installationId,t.machineFingerprint))return i(e,`License rejected by jchat service.`);let s=Z(o.token);return!s||s.installationId!==t.installationId||s.machineFingerprint!==t.machineFingerprint||s.routingMode!==t.routingMode?i(e,`License binding rejected by jchat service.`):(b(`valid`),g(`check_success`,{domain:e},t.auditPath),{status:`valid`,token:o.token})}catch{return a(`License service unreachable.`)}}const T=r.resolve(process.cwd(),`data/instance.json`);function E(){return e.createHash(`sha256`).update([a.hostname(),a.platform(),a.arch()].join(`|`)).digest(`hex`)}function D(t=E()){let n=e.generateKeyPairSync(`ed25519`);return{installationId:e.randomUUID(),machineFingerprint:t,privateKeyPem:n.privateKey.export({type:`pkcs8`,format:`pem`}).toString(),publicKeyPem:n.publicKey.export({type:`spki`,format:`pem`}).toString()}}function ne(e=T){try{if(!i.existsSync(e))return``;let t=i.readFileSync(e,`utf8`);return JSON.parse(t)?.instanceToken??``}catch{return``}}async function O(e=T){try{let t=JSON.parse(await Bun.file(e).text());if(!t||typeof t!=`object`)return null;let n=t;return n.version!==1||typeof n.installationId!=`string`||!n.installationId||typeof n.privateKeyPem!=`string`||!n.privateKeyPem||typeof n.publicKeyPem!=`string`||!n.publicKeyPem||typeof n.machineFingerprint!=`string`||!n.machineFingerprint||typeof n.instanceToken!=`string`||!n.instanceToken||n.lastSuccessfulCheckAt!==void 0&&(typeof n.lastSuccessfulCheckAt!=`string`||!n.lastSuccessfulCheckAt)?null:n}catch{return null}}async function k(t,i=T){let a=r.dirname(i);await n.mkdir(a,{recursive:!0});let o=`${i}.${process.pid}.${e.randomUUID()}.tmp`;await Bun.write(o,`${JSON.stringify(t,null,2)}\n`),await n.rename(o,i)}async function A(e,t=T){let n=await O(t);if(!n)throw Error(`Local license state is missing`);await k({...n,instanceToken:e,lastSuccessfulCheckAt:new Date().toISOString()},t)}async function j(e=T){let t=`${e}.lock`;await n.mkdir(r.dirname(t),{recursive:!0});let i;try{i=await n.open(t,`wx`)}catch(e){if(!(e instanceof Error)||!(`code`in e)||e.code!==`EEXIST`)throw e;let r=Number.parseInt(await Bun.file(t).text().catch(()=>``),10),a=!1;if(Number.isInteger(r)&&r>0)try{process.kill(r,0),a=!0}catch{a=!1}if(a)throw Error(`Another JChat process already owns the license lock`);await n.rm(t,{force:!0}),i=await n.open(t,`wx`)}return await i.writeFile(`${process.pid}\n`),async()=>{await i.close(),await n.rm(t,{force:!0})}}async function M(e,t,n,r,i,a=`envato`,o=fetch){let s=D();try{let c=await w({purchaseCode:e,domain:t,siteSlug:n,domainScope:r,routingMode:i,provider:a,installationId:s.installationId,installationPublicKey:s.publicKeyPem,machineFingerprint:s.machineFingerprint,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.`}}}function N(e={}){let t=r.resolve(e.cwd??process.cwd()),n=e.env??process.env,i=F(t);return{repoRoot:i,runtimeRoot:t,uploadsDir:I(n.JCHAT_UPLOADS_DIR,t,i,`uploads`,`uploads`),addonBundlesDir:I(n.JCHAT_ADDONS_DIR,t,i,`addons`,`addons`),addonSourceDir:i?r.join(i,`addons-dev`):r.join(t,`addons-dev`),migrationsDir:I(n.JCHAT_MIGRATIONS_DIR,t,i,r.join(`packages`,`db`,`migrations`),`migrations`)}}function P(e){return i.existsSync(r.join(e,`package.json`))&&i.existsSync(r.join(e,`apps`))&&i.existsSync(r.join(e,`packages`))}function F(e){let t=r.resolve(e);for(;;){if(P(t))return t;let e=r.dirname(t);if(e===t)return null;t=e}}function I(e,t,n,i,a){return e?r.resolve(e):n?r.join(n,i):r.join(t,a)}`${[`manage`,`www`].join(`|`)}`;const L={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`};`${L.STREAMS}${L.AUDITS}${L.ACTIONS}`,`${L.STREAMS}${L.XPS}${L.UPDATES}`,`${L.STREAMS}${L.LOGS}${L.ERRORS}`;const 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])),V=Object.fromEntries(Object.entries(z).map(([e,t])=>[t.value,{name:t.value,label:t.label,description:t.description,category:t.category}]));Object.values(V);const H=[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,...H],[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,...H],[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,...H],[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,...H],[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,...H],[...H];const U={LOCAL:`LOCAL`,MINIO:`MINIO`,CLOUDFLARE_R2:`CLOUDFLARE_R2`,AWS_S3:`AWS_S3`,GCS:`GCS`};function W(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 re(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:W(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 ie=typeof window<`u`&&window.document!==void 0&&!window.process,G={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;`},K={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`};new Proxy(console,{get(e,t){if([`info`,`success`,`warn`,`error`,`debug`].includes(t)){let e=new Date().toLocaleTimeString();if(ie)return import.meta.env?.PROD&&t===`debug`?()=>{}:console.log.bind(console,`%c${e} %c[jchat]%c ${t}%c`,G.timestamp,G.brand,G[t],G.content);{let n=K[t]||``,r=K.brand,i=K.timestamp,a=K.reset;return console.log.bind(console,`${i}${e}${a} ${r}[jchat]${a} ${n}${t}${a}${i}`)}}return e[t]}});function ae(e){return t=>e.parse(o(t,`VITE_`,``))}const oe=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(U),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),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&&!re(n)&&t.addIssue({path:[`JCHAT_SITE_URL`],code:`custom`,message:`JCHAT_SITE_URL must use the client main/root domain`})}e.JCHAT_STORAGE_TYPE===`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===`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===`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===`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 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 se(e){if(!q(e))return null;let{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,machineFingerprint:s,plan:c,entitlements:l,purchaseProvider:f,environment:p,routingMode:m,iat:h}=e;if(!J(t)||!J(n)||!J(r)||!J(i)||!J(a)||!J(o)||!J(s)||c!==`standard`&&c!==`extended`||!Array.isArray(l)||!l.every(J)||f!==`envato`&&f!==`jchat`||p!==`production`&&p!==`development`||m!==`path`&&m!==`subdomain`||typeof h!=`number`||!Number.isSafeInteger(h)||h<0)return null;let g;try{g=u(i)}catch{return null}if(p===`development`&&!g.isLocalhost||p===`production`&&!d(i))return null;let _=l.includes(`site.create.multi`);return c===`extended`&&!_||c===`standard`&&_?null:{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,machineFingerprint:s,plan:c,entitlements:l,purchaseProvider:f,environment:p,routingMode:m,iat:h}}function Z(e){let t=e.split(`.`);return t.length!==3||!t.every(Boolean)||!X(e)?null:se(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||a.iss!==(r.issuer??`https://license.jchat.local`)||a.aud!==(r.audience??`jchat-chat`)||u(a.domain).host!==u(n).host)return!1;let o=Buffer.from(`${i[0]}.${i[1]}`),s=Buffer.from(i[2].replace(/-/g,`+`).replace(/_/g,`/`),`base64`);return e.verify(`sha256`,o,r.publicKeyPem,s)}catch{return!1}}function $(e,t,n,r,i){if(!Q(e,t,i))return!1;let a=Z(e);return a?a.installationId===n&&a.machineFingerprint===r:!1}function ce(e,t,n,r){return Q(e,t,r)?Z(e)?.entitlements?.includes(n)??!1:!1}export{T as DEFAULT_STATE_PATH,oe as ServerConfigSchema,j as acquireProcessLock,w as activateRegistry,te as checkRegistry,D as createIdentity,E as createMachineFingerprint,x as getRuntimeStatus,ce as hasEntitlement,M as installAndActivate,f as isAllowedConfiguredDomain,S as isApplicationDisabled,d as isPublicDomain,ae as makeTypedEnv,u as normalizeDomain,Z as parsePayload,O as readState,ne as readTokenSync,g as recordAudit,ee as registerInvalidationHandler,m as resolveActivationDomain,N as resolveRuntimePaths,b as setRuntimeStatus,A as updateInstanceToken,$ as verifyEntitlement,Q as verifySignature,k 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 a from"node:os";import{Elysia as o}from"elysia";import{replaceKeys as s}from"string-ts";import*as c from"zod";const 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){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 h(e,t,n,r=`current-host`){let i=d(e).host;return n===`subdomain`&&t===`extended`&&r===`parent-domain`?m(i)??i:i}function g(e){if(e)return e.length<=8?`redacted`:`${e.slice(0,4)}…${e.slice(-4)}`}async function _(e,t,i=r.resolve(process.cwd(),`data/license-events.log`)){await n.mkdir(r.dirname(i),{recursive:!0});let a=JSON.stringify({at:new Date().toISOString(),event:e,domain:g(t.domain),installationId:g(t.installationId),reference:g(t.reference)});await n.appendFile(i,`${a}\n`,`utf8`)}let v=`unavailable`,y=`startup`,b;function ee(e){b=e}function x(t,n=e.randomUUID()){v=t,y=n,t===`invalid`&&b?.()}function S(){return{status:v,reference:y}}function C(){return v===`invalid`}const w=process.env.JCHAT_LICENSE_SERVER_URL||`https://license.jchat.im`;async function T(e){try{let t=await(e.fetchImpl??fetch)(`${e.registryEndpoint??w}/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,installationPublicKey:e.installationPublicKey,machineFingerprint:e.machineFingerprint})}),n=await t.json();return t.ok===!1||!n.success||!n.token?{success:!1,message:n.message??`This license is already active on another domain. Please deactivate it first.`}:{success:!0,buyer:n.buyer??`Customer`,token:n.token,domain:n.domain??d(e.domain).host}}catch{return{success:!1,message:`Failed to connect to the central registry. Please try again later.`}}}async function te(t){let n=t.fetchImpl??fetch,r=t.localTokenValidator??((e,n,r,i)=>t.runtimeOptions?$(e,n,r,i,t.runtimeOptions):!1),i=(n,r)=>{let i=e.randomUUID();return x(`invalid`,i),_(`check_rejected`,{domain:n,reference:i},t.auditPath),{status:`invalid`,error:r,reference:i}},a=e=>{let n=d(t.domain).host;return r(t.token,n,t.installationId,t.machineFingerprint)?(x(`unreachable-fallback`),_(`check_unreachable`,{domain:n},t.auditPath),{status:`unreachable-fallback`,token:t.token,error:e}):i(n,`Local license validation failed.`)};try{let e=d(t.domain).host,r=await n(`${t.registryEndpoint??w}/api/v1/check`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:t.token,domain:e,routingMode:t.routingMode,installationId:t.installationId,machineFingerprint:t.machineFingerprint})});if(!r.ok)return r.status>=500?a(`HTTP ${r.status}`):i(e,`License rejected by jchat service.`);let o=await r.json();if(!o.success||!o.token||!(t.runtimeOptions&&$(o.token,e,t.installationId,t.machineFingerprint,t.runtimeOptions))&&!t.localTokenValidator||t.localTokenValidator&&!t.localTokenValidator(o.token,e,t.installationId,t.machineFingerprint))return i(e,`License rejected by jchat service.`);let s=Z(o.token);return!s||s.installationId!==t.installationId||s.machineFingerprint!==t.machineFingerprint||s.routingMode!==t.routingMode?i(e,`License binding rejected by jchat service.`):(x(`valid`),_(`check_success`,{domain:e},t.auditPath),{status:`valid`,token:o.token})}catch{return a(`License service unreachable.`)}}const E=r.resolve(process.cwd(),`data/instance.json`);function D(){return new Bun.CryptoHasher(`sha256`).update([a.hostname(),a.platform(),a.arch()].join(`|`)).digest(`hex`)}function O(t=D()){let n=e.generateKeyPairSync(`ed25519`);return{installationId:Bun.randomUUIDv7(),machineFingerprint:t,privateKeyPem:n.privateKey.export({type:`pkcs8`,format:`pem`}).toString(),publicKeyPem:n.publicKey.export({type:`spki`,format:`pem`}).toString()}}function k(e=E){try{if(!i.existsSync(e))return``;let t=i.readFileSync(e,`utf8`);return JSON.parse(t)?.instanceToken??``}catch{return``}}async function A(e=E){try{let t=JSON.parse(await Bun.file(e).text());if(!t||typeof t!=`object`)return null;let n=t;return n.version!==1||typeof n.installationId!=`string`||!n.installationId||typeof n.privateKeyPem!=`string`||!n.privateKeyPem||typeof n.publicKeyPem!=`string`||!n.publicKeyPem||typeof n.machineFingerprint!=`string`||!n.machineFingerprint||typeof n.instanceToken!=`string`||!n.instanceToken||n.lastSuccessfulCheckAt!==void 0&&(typeof n.lastSuccessfulCheckAt!=`string`||!n.lastSuccessfulCheckAt)?null:n}catch{return null}}async function j(e,t=E){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 ne(e,t=E){let n=await A(t);if(!n)throw Error(`Local license state is missing`);await j({...n,instanceToken:e,lastSuccessfulCheckAt:new Date().toISOString()},t)}async function re(e=E){let t=`${e}.lock`;await n.mkdir(r.dirname(t),{recursive:!0});let i;try{i=await n.open(t,`wx`)}catch(e){if(!(e instanceof Error)||!(`code`in e)||e.code!==`EEXIST`)throw e;let r=Number.parseInt(await Bun.file(t).text().catch(()=>``),10),a=!1;if(Number.isInteger(r)&&r>0)try{process.kill(r,0),a=!0}catch{a=!1}if(a)throw Error(`Another JChat process already owns the license lock`,{cause:e});await n.rm(t,{force:!0}),i=await n.open(t,`wx`)}return await i.writeFile(`${process.pid}\n`),async()=>{await i.close(),await n.rm(t,{force:!0})}}async function ie(e,t,n,r,i,a=`envato`,o=fetch){let s=O();try{let c=await T({purchaseCode:e,domain:t,siteSlug:n,domainScope:r,routingMode:i,provider:a,installationId:s.installationId,installationPublicKey:s.publicKeyPem,machineFingerprint:s.machineFingerprint,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.`}}}function M(e={}){let t=r.resolve(e.cwd??process.cwd()),n=e.env??process.env,i=P(t);return{repoRoot:i,runtimeRoot:t,uploadsDir:F(n.JCHAT_UPLOADS_DIR,t,i,`uploads`,`uploads`),addonBundlesDir:F(n.JCHAT_ADDONS_DIR,t,i,`addons`,`addons`),addonSourceDir:i?r.join(i,`addons`):r.join(t,`addons`),migrationsDir:F(n.JCHAT_MIGRATIONS_DIR,t,i,r.join(`packages`,`db`,`migrations`),`migrations`)}}function N(e){return i.existsSync(r.join(e,`package.json`))&&i.existsSync(r.join(e,`apps`))&&i.existsSync(r.join(e,`packages`))}function P(e){let t=r.resolve(e);for(;;){if(N(t))return t;let e=r.dirname(t);if(e===t)return null;t=e}}function F(e,t,n,i,a){return e?r.resolve(e):n?r.join(n,i):r.join(t,a)}const I=new Set([`/health`,`/api/v1/license/status`]);async function L(e={}){let t=e.isPrimaryWorker??(!process.env.WORKER_ID||process.env.WORKER_ID===`0`);e.onInvalidate&&ee(e.onInvalidate);let n=await A();if(!n){x(e.isBootstrapComplete?`valid`:`unavailable`);return}if(!t)return;let r=await re();process.once(`exit`,()=>void r());let i=async()=>{try{let t=await A();if(!t)return;let n=await te({token:t.instanceToken,domain:e.domain??`localhost`,routingMode:e.routingMode??`subdomain`,installationId:t.installationId,machineFingerprint:t.machineFingerprint,runtimeOptions:e.signingKey?{publicKeyPem:e.signingKey}:void 0});n.status===`valid`&&await j({...t,...n.token===t.instanceToken?{}:{instanceToken:n.token},lastSuccessfulCheckAt:new Date().toISOString()})}catch{}},a=n.lastSuccessfulCheckAt?new Date(n.lastSuccessfulCheckAt).getTime():0;(!a||Date.now()-a>864e5)&&await i();let o=e.checkIntervalDays??14,s=(Math.random()*2-1)*60*60*1e3,c=Math.max(1440*60*1e3,o*24*60*60*1e3+s);setInterval(i,c).unref?.()}function ae(e={}){L(e);let t=e.allowedPaths??I;return new o({name:`jchatRuntime`}).derive({as:`global`},()=>({runtimeStatus:S()})).onBeforeHandle({as:`global`},({request:e,set:n})=>oe(e,n,t))}function oe(e,t,n=I){if(!C())return;let r=new URL(e.url).pathname.replace(/\/+$/,``)||`/`;if(!n.has(r))return t.status=503,{status:`license_invalid`,message:`This JChat installation is disabled because its license is invalid.`,reference:S().reference}}`${[`manage`,`www`].join(`|`)}`;const R={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`};`${R.STREAMS}${R.AUDITS}${R.ACTIONS}`,`${R.STREAMS}${R.XPS}${R.UPDATES}`,`${R.STREAMS}${R.LOGS}${R.ERRORS}`;const z={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`},B={ALL:{value:`*`,label:`Super Admin`,description:`Grants absolute access to every feature and setting. Use with extreme caution.`,category:z.SITE_ADMIN},MESSAGE_SEND:{value:`message.send`,label:`Send Messages`,description:`Allows the user to send messages in public chat rooms.`,category:z.MESSAGING},MESSAGE_DELETE_OWN:{value:`message.delete.own`,label:`Delete Own Messages`,description:`Allows the user to remove their own sent messages.`,category:z.MESSAGING},MESSAGE_DELETE_ANY:{value:`message.delete.any`,label:`Delete Any Message`,description:`Moderator tool: delete messages sent by any user.`,category:z.MESSAGING},MESSAGE_CLEAR:{value:`message.clear`,label:`Clear Chat`,description:`Allows clearing the entire message history of a room.`,category:z.MESSAGING},MESSAGE_UPLOAD:{value:`message.upload`,label:`Upload Files`,description:`Allows attaching images and files to messages.`,category:z.MESSAGING},MESSAGE_USE_COMMANDS:{value:`message.use_commands`,label:`Use Slash Commands`,description:`Allows executing chat commands (e.g., /me, /shrug).`,category:z.MESSAGING},MESSAGE_WILDCARD:{value:`message.*`,label:`Full Messaging Access`,description:`Grants all message-related permissions in a room.`,category:z.MESSAGING},PM_SEND:{value:`pm.send`,label:`Send Private Messages`,description:`Allows starting and participating in 1-on-1 private conversations.`,category:z.PRIVATE_MESSAGING},PM_DELETE_OWN:{value:`pm.delete.own`,label:`Delete Own PMs`,description:`Allows deleting messages sent in private chats.`,category:z.PRIVATE_MESSAGING},PM_READ_ANY:{value:`pm.read.any`,label:`Read Any PM`,description:`Administrative tool: View all private conversations on the site.`,category:z.PRIVATE_MESSAGING},PM_DELETE_ANY:{value:`pm.delete.any`,label:`Delete Any PM`,description:`Moderator tool: Remove private messages sent by any user.`,category:z.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:z.PRIVATE_MESSAGING},PM_WILDCARD:{value:`pm.*`,label:`Full PM Access`,description:`Grants all private-messaging related permissions.`,category:z.PRIVATE_MESSAGING},ROOM_JOIN:{value:`room.join`,label:`Join Rooms`,description:`Allows users to enter and view public or restricted chat rooms.`,category:z.ROOMS},ROOM_CREATE:{value:`room.create`,label:`Create Rooms`,description:`Allows users to create new chat rooms.`,category:z.ROOMS},ROOM_EDIT:{value:`room.edit`,label:`Edit Room Settings`,description:`Allows changing room names, descriptions, and settings.`,category:z.ROOMS},ROOM_DELETE:{value:`room.delete`,label:`Delete Rooms`,description:`Allows permanently removing chat rooms and their history.`,category:z.ROOMS},ROOM_LOCK:{value:`room.lock`,label:`Lock/Unlock Rooms`,description:`Allows moderators to temporarily prevent new messages in a room.`,category:z.ROOMS},ROOM_SET_TOPIC:{value:`room.set_topic`,label:`Change Room Topic`,description:`Allows updating the current topic/announcement displayed in a room.`,category:z.ROOMS},ROOM_WILDCARD:{value:`room.*`,label:`Full Room Management`,description:`Grants all room-related administration permissions.`,category:z.ROOMS},USER_KICK:{value:`user.kick`,label:`Kick Users`,description:`Allows removing a user from a room temporarily.`,category:z.MODERATION},USER_BAN:{value:`user.ban`,label:`Ban Users`,description:`Allows permanently banning users from the site or specific rooms.`,category:z.MODERATION},USER_UNBAN:{value:`user.unban`,label:`Unban Users`,description:`Allows lifting previously imposed bans.`,category:z.MODERATION},USER_MUTE:{value:`user.mute`,label:`Mute Users`,description:`Allows preventing a user from sending messages for a duration.`,category:z.MODERATION},USER_UNMUTE:{value:`user.unmute`,label:`Unmute Users`,description:`Allows lifting mutes early.`,category:z.MODERATION},USER_EDIT_RANK:{value:`user.edit_rank`,label:`Manage User Ranks`,description:`Allows assigning or removing ranks for other users.`,category:z.MODERATION},USER_VIEW_PROFILE:{value:`user.view_profile`,label:`View Profiles`,description:`Allows viewing detailed user profiles and settings.`,category:z.MODERATION},USER_WILDCARD:{value:`user.*`,label:`Full Moderation Access`,description:`Grants all user-management and moderation permissions.`,category:z.MODERATION},PROFILE_EDIT:{value:`profile.edit`,label:`Edit Own Profile`,description:`Allows users to change their display name, bio, and avatar.`,category:z.PROFILE},PROFILE_CUSTOMIZE:{value:`profile.customize`,label:`Profile Customization`,description:`Allows advanced styling like custom fonts, themes, and backgrounds.`,category:z.PROFILE},PROFILE_SET_STATUS:{value:`profile.set_status`,label:`Set Status`,description:`Allows setting custom status messages and status indicators.`,category:z.PROFILE},POST_CREATE:{value:`post.create`,label:`Create Posts`,description:`Allows sharing updates and media in the activity feed.`,category:z.POSTS},POST_CREATE_ANNOUNCEMENT:{value:`post.create.announcement`,label:`Create Announcements`,description:`Allows creating official site announcements.`,category:z.POSTS},POST_EDIT_OWN:{value:`post.edit.own`,label:`Edit Own Posts`,description:`Allows modifying previously shared feed posts.`,category:z.POSTS},POST_EDIT_ANY:{value:`post.edit.any`,label:`Edit Any Post`,description:`Administrative tool: Edit posts shared by any user.`,category:z.POSTS},POST_DELETE_OWN:{value:`post.delete.own`,label:`Delete Own Posts`,description:`Allows removing personal feed posts.`,category:z.POSTS},POST_DELETE_ANY:{value:`post.delete.any`,label:`Delete Any Post`,description:`Moderator tool: Remove posts shared by any user.`,category:z.POSTS},POST_REACT:{value:`post.react`,label:`React to Posts`,description:`Allows adding emoji reactions to feed posts.`,category:z.POSTS},POST_COMMENT:{value:`post.comment`,label:`Comment on Posts`,description:`Allows joining the conversation on shared feed items.`,category:z.POSTS},POST_COMMENT_DELETE_OWN:{value:`post.comment.delete.own`,label:`Delete Own Comments`,description:`Allows removing personal comments from feed posts.`,category:z.POSTS},POST_COMMENT_DELETE_ANY:{value:`post.comment.delete.any`,label:`Delete Any Comment`,description:`Moderator tool: Remove comments left by any user.`,category:z.POSTS},POST_WILDCARD:{value:`post.*`,label:`Full Feed Access`,description:`Grants all permissions related to the activity feed.`,category:z.POSTS},USER_BLOCK:{value:`user.block`,label:`Block Users`,description:`Allows preventing specific users from contacting the user.`,category:z.MODERATION},USER_UNBLOCK:{value:`user.unblock`,label:`Unblock Users`,description:`Allows lifting previously imposed user blocks.`,category:z.MODERATION},USER_IGNORE:{value:`user.ignore`,label:`Ignore Users`,description:`Allows hiding messages and notifications from specific users.`,category:z.MODERATION},USER_UNIGNORE:{value:`user.unignore`,label:`Stop Ignoring`,description:`Allows showing messages from previously ignored users again.`,category:z.MODERATION},SITE_EDIT:{value:`site.edit`,label:`Edit Site Settings`,description:`Allows changing site name, branding, and global configuration.`,category:z.SITE_ADMIN},SITE_CREATE:{value:`site.create`,label:`Create New Sites`,description:`Master Admin tool: Create independent Jchat site instances.`,category:z.SITE_ADMIN},SITE_MANAGE_RANKS:{value:`site.manage_ranks`,label:`Manage Ranks & Perms`,description:`Allows creating ranks and configuring their permission sets.`,category:z.SITE_ADMIN},SITE_MANAGE_ADDONS:{value:`site.manage_addons`,label:`Manage Addons`,description:`Allows installing, configuring, and removing jchat Addons.`,category:z.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:z.SITE_ADMIN},SITE_VIEW_REPORTS:{label:`View Reports`,value:`site.view_reports`,description:`Allows seeing reports filed by users against content or people.`,category:z.SITE_ADMIN},SITE_MANAGE_REPORTS:{value:`site.manage_reports`,label:`Handle Reports`,description:`Allows resolving or dismissing user reports.`,category:z.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:z.SITE_ADMIN},SITE_WILDCARD:{value:`site.*`,label:`Full Site Administration`,description:`Grants all technical administration and site discovery permissions.`,category:z.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:z.FRIENDS},FRIEND_REQUEST_MANAGE:{value:`friend.request.manage`,label:`Manage Friend Requests`,description:`Allows the user to accept or reject incoming friend requests.`,category:z.FRIENDS},FRIEND_REMOVE:{value:`friend.remove`,label:`Remove Friends`,description:`Allows the user to remove accepted friends from their list.`,category:z.FRIENDS},FRIEND_LIST:{value:`friend.list`,label:`View Friend List`,description:`Allows the user to view their own friends list.`,category:z.FRIENDS},ADDON_WILDCARD:{value:`addon.*`,label:`Full Addon System Access`,description:`Grants access to use every installed addon regardless of specific gates.`,category:z.ADDONS},COMMAND_CLEAR:{value:`command.clear`,label:`Clear Chat Command`,description:`Allows using the /clear slash command to clear chat history.`,category:z.COMMANDS},COMMAND_HELP:{value:`command.help`,label:`View Commands Help`,description:`Allows using the /help slash command.`,category:z.COMMANDS}},V=Object.fromEntries(Object.entries(B).map(([e,t])=>[e,t.value])),se=Object.fromEntries(Object.values(B).map(e=>[e.value,{name:e.value,label:e.label,description:e.description,category:e.category}]));Object.values(se);const H=[V.MESSAGE_SEND,V.MESSAGE_UPLOAD,V.MESSAGE_DELETE_OWN,V.PM_SEND,V.PM_DELETE_OWN,V.ROOM_JOIN,V.POST_REACT,V.POST_COMMENT,V.POST_COMMENT_DELETE_OWN,V.PROFILE_EDIT,V.PROFILE_SET_STATUS,V.COMMAND_HELP];V.ALL,[V.SITE_EDIT,V.SITE_MANAGE_RANKS,V.SITE_MANAGE_ADDONS,V.SITE_VIEW_AUDIT,V.SITE_VIEW_REPORTS,V.SITE_MANAGE_REPORTS,V.SITE_MANAGE_SPAM,V.USER_WILDCARD,V.ROOM_EDIT,V.ROOM_DELETE,V.ROOM_LOCK,V.ROOM_SET_TOPIC,V.MESSAGE_WILDCARD,V.POST_WILDCARD,V.PM_WILDCARD,V.PROFILE_CUSTOMIZE,V.MESSAGE_USE_COMMANDS,V.POST_CREATE,V.POST_EDIT_OWN,V.POST_DELETE_OWN,V.FRIEND_REQUEST_SEND,V.FRIEND_REQUEST_MANAGE,V.FRIEND_REMOVE,V.FRIEND_LIST,V.COMMAND_CLEAR,...H],[V.ROOM_EDIT,V.ROOM_LOCK,V.ROOM_SET_TOPIC,V.MESSAGE_WILDCARD,V.POST_WILDCARD,V.PM_SEND,V.PM_DELETE_OWN,V.PM_READ_ANY,V.PM_DELETE_ANY,V.USER_KICK,V.USER_MUTE,V.USER_UNMUTE,V.USER_BAN,V.USER_UNBAN,V.PROFILE_CUSTOMIZE,V.MESSAGE_USE_COMMANDS,V.POST_CREATE,V.POST_EDIT_OWN,V.POST_DELETE_OWN,V.FRIEND_REQUEST_SEND,V.FRIEND_REQUEST_MANAGE,V.FRIEND_REMOVE,V.FRIEND_LIST,V.COMMAND_CLEAR,...H],[V.USER_KICK,V.USER_MUTE,V.USER_UNMUTE,V.MESSAGE_DELETE_ANY,V.MESSAGE_CLEAR,V.PROFILE_CUSTOMIZE,V.MESSAGE_USE_COMMANDS,V.POST_CREATE,V.POST_EDIT_OWN,V.POST_DELETE_OWN,V.FRIEND_REQUEST_SEND,V.FRIEND_REQUEST_MANAGE,V.FRIEND_REMOVE,V.FRIEND_LIST,V.COMMAND_CLEAR,...H],[V.PROFILE_CUSTOMIZE,V.MESSAGE_USE_COMMANDS,V.POST_CREATE,V.POST_EDIT_OWN,V.POST_DELETE_OWN,V.FRIEND_REQUEST_SEND,V.FRIEND_REQUEST_MANAGE,V.FRIEND_REMOVE,V.FRIEND_LIST,...H],[V.MESSAGE_USE_COMMANDS,V.POST_CREATE,V.POST_EDIT_OWN,V.POST_DELETE_OWN,V.FRIEND_REQUEST_SEND,V.FRIEND_REQUEST_MANAGE,V.FRIEND_REMOVE,V.FRIEND_LIST,...H],[...H];const U={LOCAL:`LOCAL`,MINIO:`MINIO`,CLOUDFLARE_R2:`CLOUDFLARE_R2`,AWS_S3:`AWS_S3`,GCS:`GCS`};function ce(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 le(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:ce(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 ue=typeof window<`u`&&window.document!==void 0&&!window.process,W={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;`},G={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`};new Proxy(console,{get(e,t){if([`info`,`success`,`warn`,`error`,`debug`].includes(t)){let e=new Date().toLocaleTimeString();if(ue)return import.meta.env?.PROD&&t===`debug`?()=>{}:console.log.bind(console,`%c${e} %c[jchat]%c ${t}%c`,W.timestamp,W.brand,W[t],W.content);{let n=G[t]||``,r=G.brand,i=G.timestamp,a=G.reset;return console.log.bind(console,`${i}${e}${a} ${r}[jchat]${a} ${n}${t}${a}${i}`)}}return e[t]}});function de(e){return t=>e.parse(s(t,`VITE_`,``))}const K=c.object({JCHAT_PORT:c.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:c.string().min(1),JCHAT_DATABASE_URL:c.url().startsWith(`postgresql://`).regex(/^postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/([^?]+)(\?.*)?$/,{message:`Must be in format: postgresql://username:password@host:port/database?schema`}),JCHAT_REDIS_DATABASE_URL:c.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:c.enum(U),JCHAT_MINIO_ENDPOINT:c.string().optional(),JCHAT_MINIO_BUCKET_NAME:c.string().optional(),JCHAT_MINIO_ACCESS_KEY_ID:c.string().optional(),JCHAT_MINIO_SECRET_ACCESS_KEY:c.string().optional(),JCHAT_MINIO_PUBLIC_URL:c.string().optional(),JCHAT_AWS_REGION:c.string().optional(),JCHAT_AWS_BUCKET_NAME:c.string().optional(),JCHAT_AWS_ACCESS_KEY_ID:c.string().optional(),JCHAT_AWS_SECRET_ACCESS_KEY:c.string().optional(),JCHAT_CLOUDFLARE_ACCOUNT_ID:c.string().optional(),JCHAT_CLOUDFLARE_BUCKET_NAME:c.string().optional(),JCHAT_CLOUDFLARE_ACCESS_KEY_ID:c.string().optional(),JCHAT_CLOUDFLARE_SECRET_ACCESS_KEY:c.string().optional(),JCHAT_CLOUDFLARE_PUBLIC_URL:c.string().optional(),JCHAT_GCS_BUCKET_NAME:c.string().optional(),JCHAT_GCS_PROJECT_ID:c.string().optional(),JCHAT_GCS_CLIENT_EMAIL:c.string().optional(),JCHAT_GCS_PRIVATE_KEY:c.string().optional(),JCHAT_GCS_KEY_FILE:c.string().optional(),JCHAT_BOT_NAME:c.string().trim().min(3),JCHAT_BOT_EMAIL:c.email(),JCHAT_DOMAIN:c.string().optional(),JCHAT_SITE_URL:c.url().optional(),JCHAT_ROUTING_MODE:c.enum([`path`,`subdomain`]).default(`subdomain`),JCHAT_ADDON_ENCRYPTION_KEY:c.string().min(16),JCHAT_LICENSE_SERVER_URL:c.url().optional(),JCHAT_LICENSE_CHECK_INTERVAL_DAYS:c.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:c.url().optional(),NODE_ENV:c.enum([`development`,`test`,`production`]).default(`development`),SYSTEM_BOOTSTRAP_COMPLETE:c.boolean().optional()}).superRefine((e,t)=>{if(e.JCHAT_SITE_URL){let n;try{n=new URL(e.JCHAT_SITE_URL).hostname}catch{}n&&!le(n)&&t.addIssue({path:[`JCHAT_SITE_URL`],code:`custom`,message:`JCHAT_SITE_URL must use the client main/root domain`})}e.JCHAT_STORAGE_TYPE===U.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===U.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===U.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===U.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 fe(t,n=`production`,r){let i,a=!0;try{i=de(K)(t);let o=k();if(n===`developer-sandbox`)a=!0;else if(!o)a=!1;else if(!i.JCHAT_DOMAIN||!p(i.JCHAT_DOMAIN))throw Error(`A valid installed JChat domain is required`);else if(!Q(o,i.JCHAT_DOMAIN||`localhost`,{publicKeyPem:r}))a=!1;else{let t=o.split(`.`),n=t[2]||``;if(n){let r=e.createHmac(`sha256`,n).update(`${t[0]}.${t[1]}`).digest(`hex`);i.JCHAT_COOKIE_SECRET+=r}}}catch{a=!1,i={...t,JCHAT_PORT:t.JCHAT_PORT?parseInt(String(t.JCHAT_PORT)):3e3}}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 pe(e){if(!q(e))return null;let{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,machineFingerprint:s,plan:c,entitlements:l,purchaseProvider:u,environment:p,routingMode:m,iat:h}=e;if(!J(t)||!J(n)||!J(r)||!J(i)||!J(a)||!J(o)||!J(s)||c!==`standard`&&c!==`extended`||!Array.isArray(l)||!l.every(J)||u!==`envato`&&u!==`jchat`||p!==`production`&&p!==`development`||m!==`path`&&m!==`subdomain`||typeof h!=`number`||!Number.isSafeInteger(h)||h<0)return null;let g;try{g=d(i)}catch{return null}if(p===`development`&&!g.isLocalhost||p===`production`&&!f(i))return null;let _=l.includes(`site.create.multi`);return c===`extended`&&!_||c===`standard`&&_?null:{iss:t,aud:n,licenseId:r,domain:i,buyer:a,installationId:o,machineFingerprint:s,plan:c,entitlements:l,purchaseProvider:u,environment:p,routingMode:m,iat:h}}function Z(e){let t=e.split(`.`);return t.length!==3||!t.every(Boolean)||!X(e)?null:pe(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||a.iss!==(r.issuer??`https://license.jchat.local`)||a.aud!==(r.audience??`jchat-chat`)||d(a.domain).host!==d(n).host)return!1;let o=Buffer.from(`${i[0]}.${i[1]}`),s=Buffer.from(i[2].replace(/-/g,`+`).replace(/_/g,`/`),`base64`);return e.verify(`sha256`,o,r.publicKeyPem,s)}catch{return!1}}function $(e,t,n,r,i){if(!Q(e,t,i))return!1;let a=Z(e);return a?a.installationId===n&&a.machineFingerprint===r:!1}function me(e,t,n,r){return Q(e,t,r)?Z(e)?.entitlements?.includes(n)??!1:!1}export{E as DEFAULT_STATE_PATH,K as ServerConfigSchema,T as activateRegistry,O as createIdentity,D as createMachineFingerprint,S as getRuntimeStatus,me as hasEntitlement,ie as installAndActivate,p as isAllowedConfiguredDomain,C as isApplicationDisabled,f as isPublicDomain,ae as jchatRuntime,fe as loadServerContext,d as normalizeDomain,Z as parsePayload,A as readState,k as readTokenSync,_ as recordAudit,h as resolveActivationDomain,M as resolveRuntimePaths,x as setRuntimeStatus,L as startRuntimeSupervisor,ne as updateInstanceToken,$ as verifyEntitlement,Q as verifySignature,j 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.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Runtime support package for JChat deployments.",
|
|
6
6
|
"keywords": [
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"jchat",
|
|
9
9
|
"runtime"
|
|
10
10
|
],
|
|
11
|
-
"license": "SEE LICENSE",
|
|
11
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "git+https://github.com/mjjabarullah/jchat.git",
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
|
-
"src",
|
|
20
19
|
"README.md",
|
|
21
20
|
"CHANGELOG.md"
|
|
22
21
|
],
|
|
@@ -34,7 +33,7 @@
|
|
|
34
33
|
},
|
|
35
34
|
"scripts": {
|
|
36
35
|
"build": "tsdown",
|
|
37
|
-
"lint": "oxlint .
|
|
36
|
+
"lint": "oxlint .",
|
|
38
37
|
"format": "oxfmt . --write",
|
|
39
38
|
"typecheck": "tsc --noEmit",
|
|
40
39
|
"test": "bun --config ./bunfig.toml test",
|
|
@@ -45,13 +44,14 @@
|
|
|
45
44
|
},
|
|
46
45
|
"devDependencies": {
|
|
47
46
|
"@jchat/shared": "workspace:*",
|
|
48
|
-
"@types/
|
|
49
|
-
"
|
|
47
|
+
"@types/bun": "catalog:",
|
|
48
|
+
"elysia": "catalog:",
|
|
50
49
|
"tsdown": "^0.22.14",
|
|
51
50
|
"typescript": "catalog:",
|
|
52
51
|
"zod": "catalog:"
|
|
53
52
|
},
|
|
54
53
|
"peerDependencies": {
|
|
54
|
+
"elysia": "catalog:",
|
|
55
55
|
"zod": "catalog:"
|
|
56
56
|
}
|
|
57
57
|
}
|
package/src/audit.test.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
2
|
-
import fs from "node:fs/promises"
|
|
3
|
-
import os from "node:os"
|
|
4
|
-
import path from "node:path"
|
|
5
|
-
import { recordAudit } from "./audit"
|
|
6
|
-
|
|
7
|
-
describe("license audit", () => {
|
|
8
|
-
test("writes redacted non-sensitive events", async () => {
|
|
9
|
-
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "jchat-audit-runtime-"))
|
|
10
|
-
const filePath = path.join(directory, "events.log")
|
|
11
|
-
await recordAudit(
|
|
12
|
-
"check_rejected",
|
|
13
|
-
{
|
|
14
|
-
domain: "example.com",
|
|
15
|
-
installationId: "installation-secret-123",
|
|
16
|
-
reference: "reference-secret-123"
|
|
17
|
-
},
|
|
18
|
-
filePath
|
|
19
|
-
)
|
|
20
|
-
const content = await Bun.file(filePath).text()
|
|
21
|
-
expect(content).toContain('"event":"check_rejected"')
|
|
22
|
-
expect(content).not.toContain("installation-secret-123")
|
|
23
|
-
expect(content).not.toContain("reference-secret-123")
|
|
24
|
-
})
|
|
25
|
-
})
|