anbaric-cloud-hosting 1.3.0 → 1.5.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anbaric-cloud-hosting",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Anbaric Cloud hosting service: Postgres-backed job persistence and queuing exposed over an HTTP API",
5
5
  "license": "MIT",
6
6
  "author": "chris@anbaric.ai",
@@ -17,15 +17,15 @@
17
17
  "@aws-sdk/client-s3": "^3.1110.0",
18
18
  "@aws-sdk/client-secrets-manager": "^3.700.0",
19
19
  "@aws-sdk/client-servicediscovery": "^3.1110.0",
20
- "anbaric-data-store": "^1.3.0",
21
- "anbaric-tsapi": "^1.3.0",
20
+ "anbaric-data-store": "^1.5.0",
21
+ "anbaric-tsapi": "^1.5.0",
22
22
  "pg": "^8.16.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^26.2.0",
26
26
  "@types/pg": "^8.15.0",
27
- "anbaric-cloud": "^1.3.0",
28
- "anbaric-state-machine": "^1.3.0",
27
+ "anbaric-impl-cloud": "^1.5.0",
28
+ "anbaric-state-machine": "^1.5.0",
29
29
  "tsx": "^4.20.0",
30
30
  "typescript": "^7.0.2"
31
31
  },
@@ -1,8 +1,9 @@
1
- import {AuditRecord} from "anbaric-tsapi";
1
+ import {AuditChange, AuditRecord} from "anbaric-tsapi";
2
2
 
3
3
  type AuditFilter = {
4
4
  jobId? : string,
5
5
  actorId? : string,
6
+ change? : AuditChange,
6
7
  search? : string,
7
8
  pageSize? : number,
8
9
  page? : number,
@@ -17,6 +17,7 @@ class InMemoryAuditRecordStore implements AuditRecordStore {
17
17
  return this.records
18
18
  .filter(record => !filter.jobId || record.jobId === filter.jobId)
19
19
  .filter(record => !filter.actorId || record.actorId === filter.actorId)
20
+ .filter(record => !filter.change || record.change === filter.change)
20
21
  .filter(record => !filter.search ||
21
22
  record.description.toLowerCase().includes(filter.search.toLowerCase()))
22
23
  .reverse()
@@ -8,9 +8,9 @@ class PostgresAuditRecordStore implements AuditRecordStore {
8
8
 
9
9
  async save(record : AuditRecord) : Promise<void> {
10
10
  await this.pool.query(
11
- `INSERT INTO anbaric_system.audit_records (job_id, actor_id, actor_type, description, details)
12
- VALUES ($1, $2, $3, $4, $5)`,
13
- [record.jobId, record.actorId ?? null, record.actorType ?? null,
11
+ `INSERT INTO anbaric_system.audit_records (job_id, actor_id, actor_type, change, description, details)
12
+ VALUES ($1, $2, $3, $4, $5, $6)`,
13
+ [record.jobId, record.actorId, record.actorType, record.change,
14
14
  record.description, JSON.stringify(record.details ?? null)],
15
15
  );
16
16
  }
@@ -27,6 +27,10 @@ class PostgresAuditRecordStore implements AuditRecordStore {
27
27
  parameters.push(filter.actorId);
28
28
  conditions.push(`actor_id = $${parameters.length}`);
29
29
  }
30
+ if (filter.change) {
31
+ parameters.push(filter.change);
32
+ conditions.push(`change = $${parameters.length}::anbaric_system.audit_change`);
33
+ }
30
34
  if (filter.search) {
31
35
  parameters.push(`%${filter.search}%`);
32
36
  conditions.push(`description ILIKE $${parameters.length}`);
@@ -39,7 +43,7 @@ class PostgresAuditRecordStore implements AuditRecordStore {
39
43
  const offset = `OFFSET $${parameters.length}`;
40
44
 
41
45
  const result = await this.pool.query(
42
- `SELECT id, job_id, actor_id, actor_type, description, details, at
46
+ `SELECT id, job_id, actor_id, actor_type, change, description, details, at
43
47
  FROM anbaric_system.audit_records ${where}
44
48
  ORDER BY at DESC, id DESC ${limit} ${offset}`,
45
49
  parameters,
@@ -48,8 +52,9 @@ class PostgresAuditRecordStore implements AuditRecordStore {
48
52
  return result.rows.map(row => ({
49
53
  id: String(row.id),
50
54
  jobId: row.job_id,
51
- actorId: row.actor_id ?? undefined,
52
- actorType: row.actor_type ?? undefined,
55
+ actorId: row.actor_id,
56
+ actorType: row.actor_type,
57
+ change: row.change,
53
58
  description: row.description,
54
59
  details: row.details,
55
60
  at: row.at.toISOString(),
@@ -52,17 +52,31 @@ const ensureSchema = async (pool : Pool) : Promise<void> => {
52
52
  )
53
53
  `);
54
54
  await pool.query("ALTER TABLE anbaric_system.cli_keys ADD COLUMN IF NOT EXISTS tenant TEXT");
55
+ await pool.query(`
56
+ DO $$ BEGIN
57
+ CREATE TYPE anbaric_system.audit_change AS ENUM ('CREATE', 'UPDATE_PROPERTIES', 'CHANGE_STATE', 'DELETE');
58
+ EXCEPTION WHEN duplicate_object THEN null;
59
+ END $$
60
+ `);
55
61
  await pool.query(`
56
62
  CREATE TABLE IF NOT EXISTS anbaric_system.audit_records (
57
63
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
58
64
  job_id TEXT NOT NULL,
59
- actor_id TEXT,
60
- actor_type TEXT,
65
+ actor_id TEXT NOT NULL,
66
+ actor_type TEXT NOT NULL,
67
+ change anbaric_system.audit_change NOT NULL,
61
68
  description TEXT NOT NULL,
62
69
  details JSONB,
63
70
  at TIMESTAMPTZ NOT NULL DEFAULT now()
64
71
  )
65
72
  `);
73
+ await pool.query("ALTER TABLE anbaric_system.audit_records ADD COLUMN IF NOT EXISTS change anbaric_system.audit_change");
74
+ await pool.query("UPDATE anbaric_system.audit_records SET change = 'UPDATE_PROPERTIES' WHERE change IS NULL");
75
+ await pool.query("UPDATE anbaric_system.audit_records SET actor_id = 'system' WHERE actor_id IS NULL");
76
+ await pool.query("UPDATE anbaric_system.audit_records SET actor_type = 'CODE' WHERE actor_type IS NULL");
77
+ await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN change SET NOT NULL");
78
+ await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN actor_id SET NOT NULL");
79
+ await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN actor_type SET NOT NULL");
66
80
  await pool.query("CREATE INDEX IF NOT EXISTS audit_records_job_id ON anbaric_system.audit_records (job_id)");
67
81
  };
68
82
 
@@ -30,6 +30,7 @@ class HostingServer {
30
30
  private tokenAuthenticator? : TokenAuthenticator,
31
31
  private tenant? : string,
32
32
  auditRecords? : AuditRecordStore) {
33
+
33
34
  this.router = new Router(persistence, queue, registry, buildLayer, documentStoreFor, secretStore, cliAuthorizer, tenant, auditRecords);
34
35
  this.server = this.serverFor((request, response) => this.handle(request, response));
35
36
  this.internalServer = this.serverFor((request, response) => this.handleInternal(request, response));