anbaric-cloud-hosting 1.68.3 → 1.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/src/app-management/BaseBuildLayer.ts +30 -8
- package/src/app-management/BuildLayer.ts +11 -2
- package/src/app-management/FargateBuildLayer.ts +81 -0
- package/src/data-store/AppSettingsStore.ts +30 -0
- package/src/data-store/InMemoryAppSettingsStore.ts +27 -0
- package/src/data-store/InMemorySkillStore.ts +31 -0
- package/src/data-store/PlatformMigrations.ts +25 -0
- package/src/data-store/PostgresAppSettingsStore.ts +40 -0
- package/src/data-store/PostgresSkillStore.ts +47 -0
- package/src/data-store/SkillStore.ts +38 -0
- package/src/hosting/HostingServer.ts +9 -2
- package/src/hosting/handlers/AppsHandler.ts +33 -5
- package/src/hosting/handlers/SkillsHandler.ts +81 -0
- package/src/hosting/pages/platform-ui.html +1 -1
- package/src/main.ts +18 -3
- package/src/usage/UsageReporter.ts +10 -7
package/src/main.ts
CHANGED
|
@@ -26,6 +26,8 @@ import {FargateBuildLayer} from "./app-management/FargateBuildLayer";
|
|
|
26
26
|
import {DocGenerator} from "./docs/DocGenerator";
|
|
27
27
|
import {GatewayDocModel} from "./docs/GatewayDocModel";
|
|
28
28
|
import {PostgresAppDocsStore} from "./data-store/PostgresAppDocsStore";
|
|
29
|
+
import {PostgresAppSettingsStore} from "./data-store/PostgresAppSettingsStore";
|
|
30
|
+
import {PostgresSkillStore} from "./data-store/PostgresSkillStore";
|
|
29
31
|
import {PostgresEntitlementStore} from "./data-store/PostgresEntitlementStore";
|
|
30
32
|
import {PostgresUserDirectory} from "./data-store/PostgresUserDirectory";
|
|
31
33
|
import {HttpMembershipService} from "./auth/HttpMembershipService";
|
|
@@ -41,6 +43,12 @@ import {CentralBilling} from "./billing/CentralBilling";
|
|
|
41
43
|
|
|
42
44
|
new FatalErrors().install();
|
|
43
45
|
|
|
46
|
+
// "key=value,key=value", as the hosting module writes it; nothing means untagged.
|
|
47
|
+
const resourceTags = (spec? : string) : Record<string, string> | undefined => {
|
|
48
|
+
const entries = (spec ?? "").split(",").map(pair => pair.split("=")).filter(pair => pair.length === 2 && pair[0].trim() && pair[1].trim());
|
|
49
|
+
return entries.length === 0 ? undefined : Object.fromEntries(entries.map(([key, value]) => [key.trim(), value.trim()]));
|
|
50
|
+
};
|
|
51
|
+
|
|
44
52
|
const pool = new Pool({ connectionString: process.env.ANBARIC_DATABASE_URL });
|
|
45
53
|
await ensureSchema(pool);
|
|
46
54
|
|
|
@@ -54,6 +62,7 @@ const appsDir = process.env.ANBARIC_APPS_DIR ?? "/tmp/anbaric-apps";
|
|
|
54
62
|
// Generates user docs from an app's source on deploy; no-ops without an AI
|
|
55
63
|
// gateway token. The build layer calls it fire-and-forget off the deploy path.
|
|
56
64
|
const docGenerator = new DocGenerator(new GatewayDocModel(), new PostgresAppDocsStore(pool));
|
|
65
|
+
const appSettings = new PostgresAppSettingsStore(pool);
|
|
57
66
|
|
|
58
67
|
const buildLayer = process.env.ANBARIC_BUILD_LAYER === "docker"
|
|
59
68
|
? new DockerBuildLayer(appsDir, {
|
|
@@ -85,6 +94,7 @@ const buildLayer = process.env.ANBARIC_BUILD_LAYER === "docker"
|
|
|
85
94
|
sqlDatabaseUrlSecretArn: process.env.ANBARIC_AWS_APP_SQL_URL_SECRET,
|
|
86
95
|
sqlSchema: process.env.ANBARIC_SQL_SCHEMA,
|
|
87
96
|
docGenerator,
|
|
97
|
+
tags: resourceTags(process.env.ANBARIC_RESOURCE_TAGS),
|
|
88
98
|
})
|
|
89
99
|
: undefined;
|
|
90
100
|
|
|
@@ -147,7 +157,7 @@ const server = new HostingServer(jobs, queue, registry, buildLayer,
|
|
|
147
157
|
tokenAuthenticator, process.env.ANBARIC_TENANT, auditRecords, plugins,
|
|
148
158
|
new PostgresJobRunSchedulePersistence(pool), await loadNotifier(process.env.ANBARIC_NOTIFIER_MODULE),
|
|
149
159
|
new PostgresEntitlementStore(pool), new PostgresUserDirectory(pool), memberships,
|
|
150
|
-
(appId) => new PostgresPromptManager(pool, appId), fileStorageFor, subdomains, billing);
|
|
160
|
+
(appId) => new PostgresPromptManager(pool, appId), fileStorageFor, subdomains, billing, appSettings, new PostgresSkillStore(pool));
|
|
151
161
|
const port = await server.listen(hostingPort);
|
|
152
162
|
const internal = await server.listenInternal(internalPort);
|
|
153
163
|
|
|
@@ -163,9 +173,14 @@ if (buildLayer && process.env.ANBARIC_TENANT && process.env.ANBARIC_CLI_KEY_LOOK
|
|
|
163
173
|
process.env.ANBARIC_CLI_KEY_LOOKUP_URL,
|
|
164
174
|
process.env.ANBARIC_CLI_KEY_LOOKUP_SECRET ?? "",
|
|
165
175
|
process.env.ANBARIC_TENANT,
|
|
166
|
-
() => {
|
|
176
|
+
async () => {
|
|
167
177
|
const live = buildLayer.list().filter(app => app.status === "running" || app.status === "draining");
|
|
168
|
-
|
|
178
|
+
const zeroDowntime = new Set((await appSettings.list()).filter(each => each.zeroDowntime).map(each => each.appName));
|
|
179
|
+
return {
|
|
180
|
+
apps: live.length,
|
|
181
|
+
large: live.filter(app => app.size === "large").length,
|
|
182
|
+
zeroDowntime: live.filter(app => zeroDowntime.has(app.appName)).length,
|
|
183
|
+
};
|
|
169
184
|
},
|
|
170
185
|
).start();
|
|
171
186
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
type FetchFn = (url : string, init : RequestInit) => Promise<Response>;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/* How many apps are running, how many of those are on a large instance, and
|
|
4
|
+
how many have zero-downtime deploys turned on. Counted afresh each report,
|
|
5
|
+
and allowed to look something up on the way. */
|
|
6
|
+
type AppCounts = { apps : number, large : number, zeroDowntime? : number };
|
|
7
|
+
type CountApps = () => AppCounts | Promise<AppCounts>;
|
|
5
8
|
|
|
6
9
|
const REPORT_INTERVAL_MS = 60 * 60_000;
|
|
7
10
|
|
|
@@ -37,7 +40,7 @@ class UsageReporter {
|
|
|
37
40
|
const response = await this.fetchFn(`${this.centralUrl}/usage`, {
|
|
38
41
|
method: "POST",
|
|
39
42
|
headers: { "content-type": "application/json", "x-anbaric-central-key": this.secret },
|
|
40
|
-
body: JSON.stringify({ slug: this.tenantSlug, ...this.counted(), day: this.today() }),
|
|
43
|
+
body: JSON.stringify({ slug: this.tenantSlug, ...(await this.counted()), day: this.today() }),
|
|
41
44
|
});
|
|
42
45
|
if (! response.ok) console.warn(`[usage] central answered ${response.status} to the app count`);
|
|
43
46
|
return response.ok;
|
|
@@ -47,9 +50,9 @@ class UsageReporter {
|
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
private counted() : { apps : number, largeApps : number } {
|
|
51
|
-
const { apps, large } = this.countApps();
|
|
52
|
-
return { apps, largeApps: large };
|
|
53
|
+
private async counted() : Promise<{ apps : number, largeApps : number, zeroDowntimeApps : number }> {
|
|
54
|
+
const { apps, large, zeroDowntime } = await this.countApps();
|
|
55
|
+
return { apps, largeApps: large, zeroDowntimeApps: zeroDowntime ?? 0 };
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
// The day in UTC, so every tenant's day starts at the same moment however
|
|
@@ -66,4 +69,4 @@ class UsageReporter {
|
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
export { UsageReporter };
|
|
69
|
-
export type { CountApps };
|
|
72
|
+
export type { CountApps, AppCounts };
|