@adventurelabs/scout-core 1.4.65 → 1.4.67

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.
@@ -0,0 +1,14 @@
1
+ import { Database } from "../types/supabase";
2
+ import { CertificateInsert, CertificateUpdate, ICertificate } from "../types/db";
3
+ import { IWebResponseCompatible } from "../types/requests";
4
+ import { SupabaseClient } from "@supabase/supabase-js";
5
+ export declare function get_certificates_by_device(client: SupabaseClient<Database>, device_id: number): Promise<IWebResponseCompatible<ICertificate[]>>;
6
+ export declare function get_certificate_by_device(client: SupabaseClient<Database>, certificate_id: number, device_id: number): Promise<IWebResponseCompatible<ICertificate | null>>;
7
+ export declare function create_certificate_by_device(client: SupabaseClient<Database>, device_id: number, row: CertificateInsert): Promise<IWebResponseCompatible<ICertificate | null>>;
8
+ export declare function update_certificate_by_device(client: SupabaseClient<Database>, certificate_id: number, device_id: number, patch: CertificateUpdate): Promise<IWebResponseCompatible<ICertificate | null>>;
9
+ export declare function delete_certificate_by_device(client: SupabaseClient<Database>, certificate_id: number, device_id: number): Promise<IWebResponseCompatible<ICertificate | null>>;
10
+ export declare function get_certificates_by_part(client: SupabaseClient<Database>, part_id: number): Promise<IWebResponseCompatible<ICertificate[]>>;
11
+ export declare function get_certificate_by_part(client: SupabaseClient<Database>, certificate_id: number, part_id: number): Promise<IWebResponseCompatible<ICertificate | null>>;
12
+ export declare function create_certificate_by_part(client: SupabaseClient<Database>, part_id: number, row: CertificateInsert): Promise<IWebResponseCompatible<ICertificate | null>>;
13
+ export declare function update_certificate_by_part(client: SupabaseClient<Database>, certificate_id: number, part_id: number, patch: CertificateUpdate): Promise<IWebResponseCompatible<ICertificate | null>>;
14
+ export declare function delete_certificate_by_part(client: SupabaseClient<Database>, certificate_id: number, part_id: number): Promise<IWebResponseCompatible<ICertificate | null>>;
@@ -0,0 +1,233 @@
1
+ import { EnumWebResponse, IWebResponse, } from "../types/requests";
2
+ const SELECT_CERT_FOR_DEVICE = `
3
+ id,
4
+ created_at,
5
+ expiration,
6
+ issuer,
7
+ part_id,
8
+ tracking_number,
9
+ type,
10
+ updated_at,
11
+ parts!inner ( device_id )
12
+ `;
13
+ function to_certificate(row) {
14
+ const { parts: _p, ...cert } = row;
15
+ return cert;
16
+ }
17
+ function assertCertificateInsertBase(row) {
18
+ if (!row.issuer?.trim()) {
19
+ return IWebResponse.error("issuer is required")
20
+ .to_compatible();
21
+ }
22
+ if (!row.type?.trim()) {
23
+ return IWebResponse.error("type is required")
24
+ .to_compatible();
25
+ }
26
+ return null;
27
+ }
28
+ // --- by device (join `parts` so scope is the device the part belongs to) ---
29
+ export async function get_certificates_by_device(client, device_id) {
30
+ const { data, error } = await client
31
+ .from("certificates")
32
+ .select(SELECT_CERT_FOR_DEVICE)
33
+ .eq("parts.device_id", device_id)
34
+ .order("created_at", { ascending: false });
35
+ if (error) {
36
+ return IWebResponse.error(error.message).to_compatible();
37
+ }
38
+ const rows = (data ?? []);
39
+ return IWebResponse.success(rows.map(to_certificate)).to_compatible();
40
+ }
41
+ export async function get_certificate_by_device(client, certificate_id, device_id) {
42
+ const { data, error } = await client
43
+ .from("certificates")
44
+ .select(SELECT_CERT_FOR_DEVICE)
45
+ .eq("id", certificate_id)
46
+ .eq("parts.device_id", device_id)
47
+ .single();
48
+ if (error) {
49
+ return IWebResponse.error(error.message).to_compatible();
50
+ }
51
+ if (!data) {
52
+ return IWebResponse.error("Certificate not found")
53
+ .to_compatible();
54
+ }
55
+ return IWebResponse.success(to_certificate(data)).to_compatible();
56
+ }
57
+ export async function create_certificate_by_device(client, device_id, row) {
58
+ if (row.part_id == null) {
59
+ return IWebResponse.error("part_id is required")
60
+ .to_compatible();
61
+ }
62
+ const baseErr = assertCertificateInsertBase(row);
63
+ if (baseErr) {
64
+ return baseErr;
65
+ }
66
+ const { data: part, error: partError } = await client
67
+ .from("parts")
68
+ .select("id")
69
+ .eq("id", row.part_id)
70
+ .eq("device_id", device_id)
71
+ .maybeSingle();
72
+ if (partError) {
73
+ return IWebResponse.error(partError.message)
74
+ .to_compatible();
75
+ }
76
+ if (!part) {
77
+ return IWebResponse.error("part not found for this device").to_compatible();
78
+ }
79
+ const { data, error } = await client
80
+ .from("certificates")
81
+ .insert([row])
82
+ .select("*")
83
+ .single();
84
+ if (error) {
85
+ return IWebResponse.error(error.message).to_compatible();
86
+ }
87
+ if (!data) {
88
+ return IWebResponse.error("Failed to create certificate").to_compatible();
89
+ }
90
+ return IWebResponse.success(data).to_compatible();
91
+ }
92
+ export async function update_certificate_by_device(client, certificate_id, device_id, patch) {
93
+ // Herd RLS is broader than device: must verify this cert is on the device.
94
+ const check = await get_certificate_by_device(client, certificate_id, device_id);
95
+ if (check.status !== EnumWebResponse.SUCCESS || check.data == null) {
96
+ return IWebResponse.error(check.msg ?? "Certificate not found").to_compatible();
97
+ }
98
+ const partId = check.data.part_id;
99
+ if (partId == null) {
100
+ return IWebResponse.error("Certificate not found")
101
+ .to_compatible();
102
+ }
103
+ const { id: _id, part_id: _pid, created_at: _ca, ...updateData } = patch;
104
+ if (Object.keys(updateData).length === 0) {
105
+ return IWebResponse.error("No valid fields to update").to_compatible();
106
+ }
107
+ const { data, error } = await client
108
+ .from("certificates")
109
+ .update(updateData)
110
+ .eq("id", certificate_id)
111
+ .eq("part_id", partId)
112
+ .select("*")
113
+ .single();
114
+ if (error) {
115
+ return IWebResponse.error(error.message).to_compatible();
116
+ }
117
+ if (!data) {
118
+ return IWebResponse.error("Certificate not found")
119
+ .to_compatible();
120
+ }
121
+ return IWebResponse.success(data).to_compatible();
122
+ }
123
+ export async function delete_certificate_by_device(client, certificate_id, device_id) {
124
+ const check = await get_certificate_by_device(client, certificate_id, device_id);
125
+ if (check.status !== EnumWebResponse.SUCCESS || check.data == null) {
126
+ return IWebResponse.error(check.msg ?? "Certificate not found").to_compatible();
127
+ }
128
+ const partId = check.data.part_id;
129
+ if (partId == null) {
130
+ return IWebResponse.error("Certificate not found")
131
+ .to_compatible();
132
+ }
133
+ const { data, error } = await client
134
+ .from("certificates")
135
+ .delete()
136
+ .eq("id", certificate_id)
137
+ .eq("part_id", partId)
138
+ .select("*")
139
+ .single();
140
+ if (error) {
141
+ return IWebResponse.error(error.message).to_compatible();
142
+ }
143
+ if (!data) {
144
+ return IWebResponse.error("Certificate not found")
145
+ .to_compatible();
146
+ }
147
+ return IWebResponse.success(data).to_compatible();
148
+ }
149
+ // --- by part (direct `part_id` on `certificates`) ---
150
+ export async function get_certificates_by_part(client, part_id) {
151
+ const { data, error } = await client
152
+ .from("certificates")
153
+ .select("*")
154
+ .eq("part_id", part_id)
155
+ .order("created_at", { ascending: false });
156
+ if (error) {
157
+ return IWebResponse.error(error.message).to_compatible();
158
+ }
159
+ return IWebResponse.success(data ?? []).to_compatible();
160
+ }
161
+ export async function get_certificate_by_part(client, certificate_id, part_id) {
162
+ const { data, error } = await client
163
+ .from("certificates")
164
+ .select("*")
165
+ .eq("id", certificate_id)
166
+ .eq("part_id", part_id)
167
+ .single();
168
+ if (error) {
169
+ return IWebResponse.error(error.message).to_compatible();
170
+ }
171
+ if (!data) {
172
+ return IWebResponse.error("Certificate not found")
173
+ .to_compatible();
174
+ }
175
+ return IWebResponse.success(data).to_compatible();
176
+ }
177
+ export async function create_certificate_by_part(client, part_id, row) {
178
+ const baseErr = assertCertificateInsertBase(row);
179
+ if (baseErr) {
180
+ return baseErr;
181
+ }
182
+ const insert = { ...row, part_id };
183
+ const { data, error } = await client
184
+ .from("certificates")
185
+ .insert([insert])
186
+ .select("*")
187
+ .single();
188
+ if (error) {
189
+ return IWebResponse.error(error.message).to_compatible();
190
+ }
191
+ if (!data) {
192
+ return IWebResponse.error("Failed to create certificate").to_compatible();
193
+ }
194
+ return IWebResponse.success(data).to_compatible();
195
+ }
196
+ export async function update_certificate_by_part(client, certificate_id, part_id, patch) {
197
+ const { id: _id, part_id: _pid, created_at: _ca, ...updateData } = patch;
198
+ if (Object.keys(updateData).length === 0) {
199
+ return IWebResponse.error("No valid fields to update").to_compatible();
200
+ }
201
+ const { data, error } = await client
202
+ .from("certificates")
203
+ .update(updateData)
204
+ .eq("id", certificate_id)
205
+ .eq("part_id", part_id)
206
+ .select("*")
207
+ .single();
208
+ if (error) {
209
+ return IWebResponse.error(error.message).to_compatible();
210
+ }
211
+ if (!data) {
212
+ return IWebResponse.error("Certificate not found")
213
+ .to_compatible();
214
+ }
215
+ return IWebResponse.success(data).to_compatible();
216
+ }
217
+ export async function delete_certificate_by_part(client, certificate_id, part_id) {
218
+ const { data, error } = await client
219
+ .from("certificates")
220
+ .delete()
221
+ .eq("id", certificate_id)
222
+ .eq("part_id", part_id)
223
+ .select("*")
224
+ .single();
225
+ if (error) {
226
+ return IWebResponse.error(error.message).to_compatible();
227
+ }
228
+ if (!data) {
229
+ return IWebResponse.error("Certificate not found")
230
+ .to_compatible();
231
+ }
232
+ return IWebResponse.success(data).to_compatible();
233
+ }
@@ -2,6 +2,7 @@ export * from "./analysis_usage";
2
2
  export * from "./auth";
3
3
  export * from "./bounding_boxes";
4
4
  export * from "./chat";
5
+ export * from "./certificates";
5
6
  export * from "./credentials";
6
7
  export * from "./db";
7
8
  export * from "./devices";
@@ -2,6 +2,7 @@ export * from "./analysis_usage";
2
2
  export * from "./auth";
3
3
  export * from "./bounding_boxes";
4
4
  export * from "./chat";
5
+ export * from "./certificates";
5
6
  export * from "./credentials";
6
7
  export * from "./db";
7
8
  export * from "./devices";
@@ -31,14 +31,7 @@ export async function server_get_users_with_herd_access(herd_id, supabaseClient)
31
31
  .from("users_roles_per_herd")
32
32
  .select(`
33
33
  role,
34
- users (
35
- id,
36
- username,
37
- earthranger_id,
38
- first,
39
- last,
40
- title
41
- )
34
+ users (*)
42
35
  `)
43
36
  .eq("herd_id", herd_id);
44
37
  if (error) {
@@ -49,7 +42,6 @@ export async function server_get_users_with_herd_access(herd_id, supabaseClient)
49
42
  };
50
43
  }
51
44
  else {
52
- // Transform the data to match IUserAndRole interface
53
45
  const transformedData = data.map((item) => ({
54
46
  user: item.users,
55
47
  role: item.role,
@@ -76,12 +68,9 @@ export async function server_upsert_user_with_role(herd_id, username, role) {
76
68
  herd_id: herd_id,
77
69
  role: role,
78
70
  };
79
- // upddate or insert user role
80
- const { data, error } = await supabase
81
- .from("users_roles_per_herd")
82
- .upsert(newRoleForDb);
71
+ const { error } = await supabase.from("users_roles_per_herd").upsert(newRoleForDb);
83
72
  if (error) {
84
73
  return IWebResponse.error("Unable to update or add user with provided username. Ensure you have sufficient permissions.").to_compatible();
85
74
  }
86
- return IWebResponse.success(data).to_compatible();
75
+ return IWebResponse.success({ user, role }).to_compatible();
87
76
  }
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export * from "./helpers/artifacts";
15
15
  export * from "./helpers/auth";
16
16
  export * from "./helpers/bounding_boxes";
17
17
  export * from "./helpers/chat";
18
+ export * from "./helpers/certificates";
18
19
  export * from "./helpers/connectivity";
19
20
  export * from "./helpers/credentials";
20
21
  export * from "./helpers/db";
@@ -69,5 +70,5 @@ export * from "./supabase/middleware";
69
70
  export * from "./supabase/server";
70
71
  export * from "./api_keys/actions";
71
72
  export type { HerdModule, IHerdModule } from "./types/herd_module";
72
- export type { IDevice, IEvent, IUser, IHerd, IHerdPrettyLocation, IEventWithTags, IZoneWithActions, ICredential, CredentialInsert, CredentialUpdate, IUserAndRole, IApiKeyScout, ILayer, IHeartbeat, IProvider, IConnectivity, ISession, ISessionWithCoordinates, IConnectivityWithCoordinates, IObservation, ObservationInsert, ObservationUpdate, IAnalysisJob, IAnalysisTask, AnalysisWorkStatus, } from "./types/db";
73
+ export type { IDevice, IEvent, IUser, IHerd, IHerdPrettyLocation, IEventWithTags, IZoneWithActions, ICredential, CredentialInsert, CredentialUpdate, ICertificate, CertificateInsert, CertificateUpdate, IUserAndRole, IUserProfileRow, IApiKeyScout, ILayer, IHeartbeat, IProvider, IConnectivity, ISession, ISessionWithCoordinates, IConnectivityWithCoordinates, IObservation, ObservationInsert, ObservationUpdate, IAnalysisJob, IAnalysisTask, AnalysisWorkStatus, } from "./types/db";
73
74
  export { EnumSessionsVisibility } from "./types/events";
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ export * from "./helpers/artifacts";
18
18
  export * from "./helpers/auth";
19
19
  export * from "./helpers/bounding_boxes";
20
20
  export * from "./helpers/chat";
21
+ export * from "./helpers/certificates";
21
22
  export * from "./helpers/connectivity";
22
23
  export * from "./helpers/credentials";
23
24
  export * from "./helpers/db";
@@ -1366,26 +1366,32 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1366
1366
  };
1367
1367
  users: {
1368
1368
  Row: {
1369
+ auto_approve_sessions: boolean;
1369
1370
  earthranger_id: string | null;
1370
1371
  first: string | null;
1371
1372
  id: string;
1372
1373
  last: string | null;
1374
+ signature_base64: string | null;
1373
1375
  title: string | null;
1374
1376
  username: string | null;
1375
1377
  };
1376
1378
  Insert: {
1379
+ auto_approve_sessions?: boolean;
1377
1380
  earthranger_id?: string | null;
1378
1381
  first?: string | null;
1379
1382
  id: string;
1380
1383
  last?: string | null;
1384
+ signature_base64?: string | null;
1381
1385
  title?: string | null;
1382
1386
  username?: string | null;
1383
1387
  };
1384
1388
  Update: {
1389
+ auto_approve_sessions?: boolean;
1385
1390
  earthranger_id?: string | null;
1386
1391
  first?: string | null;
1387
1392
  id?: string;
1388
1393
  last?: string | null;
1394
+ signature_base64?: string | null;
1389
1395
  title?: string | null;
1390
1396
  username?: string | null;
1391
1397
  };
@@ -6,7 +6,10 @@ export type DeviceType = Database["public"]["Enums"]["device_type"];
6
6
  export type MediaType = Database["public"]["Enums"]["media_type"];
7
7
  export type TagObservationType = Database["public"]["Enums"]["tag_observation_type"];
8
8
  export type AnalysisWorkStatus = Database["public"]["Enums"]["analysis_work_status"];
9
+ /** Supabase Auth user (`auth.getUser()`); not `public.users`. */
9
10
  export type IUser = User;
11
+ /** Full `public.users` profile row (generated from DB). */
12
+ export type IUserProfileRow = Database["public"]["Tables"]["users"]["Row"];
10
13
  export type IDevice = Database["public"]["CompositeTypes"]["device_pretty_location"] & {
11
14
  api_keys_scout?: IApiKeyScout[];
12
15
  parts?: IPart[];
@@ -31,6 +34,7 @@ export type IObservation = Database["public"]["Tables"]["observations"]["Row"];
31
34
  export type IProvider = Database["public"]["Tables"]["providers"]["Row"];
32
35
  export type IPart = Database["public"]["Tables"]["parts"]["Row"];
33
36
  export type ICredential = Database["public"]["Tables"]["credentials"]["Row"];
37
+ export type ICertificate = Database["public"]["Tables"]["certificates"]["Row"];
34
38
  export type IVersionsSoftware = Database["public"]["Tables"]["versions_software"]["Row"];
35
39
  export type IArtifact = Database["public"]["Tables"]["artifacts"]["Row"];
36
40
  export type IHealthMetric = Database["public"]["Tables"]["health_metrics"]["Row"];
@@ -72,6 +76,8 @@ export type ISessionUsageOverTime = Database["public"]["Functions"]["get_session
72
76
  export type PartInsert = Database["public"]["Tables"]["parts"]["Insert"];
73
77
  export type CredentialInsert = Database["public"]["Tables"]["credentials"]["Insert"];
74
78
  export type CredentialUpdate = Database["public"]["Tables"]["credentials"]["Update"];
79
+ export type CertificateInsert = Database["public"]["Tables"]["certificates"]["Insert"];
80
+ export type CertificateUpdate = Database["public"]["Tables"]["certificates"]["Update"];
75
81
  export type VersionsSoftwareInsert = Database["public"]["Tables"]["versions_software"]["Insert"];
76
82
  export type ArtifactInsert = Database["public"]["Tables"]["artifacts"]["Insert"];
77
83
  export type ArtifactUpdate = Database["public"]["Tables"]["artifacts"]["Update"];
@@ -128,7 +134,7 @@ export interface IEventWithSession extends IEvent {
128
134
  session: ISession | null;
129
135
  }
130
136
  export type IUserAndRole = {
131
- user: Pick<Database["public"]["Tables"]["users"]["Row"], "id" | "username" | "earthranger_id" | "first" | "last" | "title"> | null;
137
+ user: IUserProfileRow | null;
132
138
  role: Role;
133
139
  };
134
140
  export interface IApiKeyScout {
@@ -1436,26 +1436,32 @@ export type Database = {
1436
1436
  };
1437
1437
  users: {
1438
1438
  Row: {
1439
+ auto_approve_sessions: boolean;
1439
1440
  earthranger_id: string | null;
1440
1441
  first: string | null;
1441
1442
  id: string;
1442
1443
  last: string | null;
1444
+ signature_base64: string | null;
1443
1445
  title: string | null;
1444
1446
  username: string | null;
1445
1447
  };
1446
1448
  Insert: {
1449
+ auto_approve_sessions?: boolean;
1447
1450
  earthranger_id?: string | null;
1448
1451
  first?: string | null;
1449
1452
  id: string;
1450
1453
  last?: string | null;
1454
+ signature_base64?: string | null;
1451
1455
  title?: string | null;
1452
1456
  username?: string | null;
1453
1457
  };
1454
1458
  Update: {
1459
+ auto_approve_sessions?: boolean;
1455
1460
  earthranger_id?: string | null;
1456
1461
  first?: string | null;
1457
1462
  id?: string;
1458
1463
  last?: string | null;
1464
+ signature_base64?: string | null;
1459
1465
  title?: string | null;
1460
1466
  username?: string | null;
1461
1467
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.4.65",
3
+ "version": "1.4.67",
4
4
  "description": "Core utilities and helpers for Adventure Labs Scout applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",