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/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
- return { apps: live.length, large: live.filter(app => app.size === "large").length };
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
- // How many apps are running, and how many of those are on a large instance.
4
- type CountApps = () => { apps : number, large : number };
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 };