@selvajs/supabase-provider 0.20.0-beta.4 → 0.20.0-beta.6
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/README.md +3 -1
- package/dist/auth/SupabaseAuthProvider.d.ts.map +1 -1
- package/dist/auth/SupabaseAuthProvider.js +18 -39
- package/dist/auth/SupabaseAuthProvider.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +126 -0
- package/dist/config.js.map +1 -0
- package/dist/data/SupabaseAuditQuery.js +1 -1
- package/dist/data/SupabaseAuditQuery.js.map +1 -1
- package/dist/data/SupabaseComputeServerStore.d.ts +14 -5
- package/dist/data/SupabaseComputeServerStore.d.ts.map +1 -1
- package/dist/data/SupabaseComputeServerStore.js +103 -80
- package/dist/data/SupabaseComputeServerStore.js.map +1 -1
- package/dist/data/SupabaseDataProvider.d.ts.map +1 -1
- package/dist/data/SupabaseDataProvider.js +5 -3
- package/dist/data/SupabaseDataProvider.js.map +1 -1
- package/dist/data/SupabaseDefinitionStore.js +7 -6
- package/dist/data/SupabaseDefinitionStore.js.map +1 -1
- package/dist/data/SupabaseInviteStore.d.ts +1 -1
- package/dist/data/SupabaseInviteStore.d.ts.map +1 -1
- package/dist/data/SupabaseInviteStore.js +3 -2
- package/dist/data/SupabaseInviteStore.js.map +1 -1
- package/dist/data/SupabaseOrgStore.d.ts.map +1 -1
- package/dist/data/SupabaseOrgStore.js +10 -34
- package/dist/data/SupabaseOrgStore.js.map +1 -1
- package/dist/data/SupabaseProjectStore.d.ts.map +1 -1
- package/dist/data/SupabaseProjectStore.js +15 -12
- package/dist/data/SupabaseProjectStore.js.map +1 -1
- package/dist/data/SupabaseShareLinkStore.d.ts.map +1 -1
- package/dist/data/SupabaseShareLinkStore.js +34 -29
- package/dist/data/SupabaseShareLinkStore.js.map +1 -1
- package/dist/data/SupabaseSolveMetricSink.d.ts +11 -3
- package/dist/data/SupabaseSolveMetricSink.d.ts.map +1 -1
- package/dist/data/SupabaseSolveMetricSink.js +38 -14
- package/dist/data/SupabaseSolveMetricSink.js.map +1 -1
- package/dist/data/errors.d.ts.map +1 -1
- package/dist/data/errors.js +3 -0
- package/dist/data/errors.js.map +1 -1
- package/dist/data/migrationHead.d.ts +1 -1
- package/dist/data/migrationHead.js +1 -1
- package/dist/data/pagination.d.ts +2 -3
- package/dist/data/pagination.d.ts.map +1 -1
- package/dist/data/pagination.js +6 -15
- package/dist/data/pagination.js.map +1 -1
- package/dist/permissions/SupabasePlatformPermissionStore.d.ts +2 -2
- package/dist/permissions/SupabasePlatformPermissionStore.d.ts.map +1 -1
- package/dist/permissions/SupabasePlatformPermissionStore.js +10 -49
- package/dist/permissions/SupabasePlatformPermissionStore.js.map +1 -1
- package/dist/storage/SupabaseStorageProvider.d.ts +3 -2
- package/dist/storage/SupabaseStorageProvider.d.ts.map +1 -1
- package/dist/storage/SupabaseStorageProvider.js +30 -19
- package/dist/storage/SupabaseStorageProvider.js.map +1 -1
- package/dist/userProfile/SupabaseUserProfileProvider.d.ts +7 -0
- package/dist/userProfile/SupabaseUserProfileProvider.d.ts.map +1 -1
- package/dist/userProfile/SupabaseUserProfileProvider.js +9 -19
- package/dist/userProfile/SupabaseUserProfileProvider.js.map +1 -1
- package/package.json +9 -3
- package/supabase/migrations/20261101152228_selva_revoke_privileged_functions.sql +44 -0
- package/supabase/migrations/20261101160000_selva_rls_is_the_boundary.sql +527 -0
- package/supabase/migrations/20261101170000_selva_compute_api_key_private.sql +14 -0
- package/supabase/migrations/20261101180000_selva_erasure_keeps_tenant_data.sql +28 -0
- package/supabase/migrations/20261101190000_selva_atomic_compute_save.sql +136 -0
- package/supabase/migrations/20261101200000_selva_last_admin_lock_order.sql +62 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/data/pagination.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/data/pagination.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAkB,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACd;AAED,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,kBAAkB,IAAI,YAAY,EAAE,CAAC;AAElF,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG,SAAS,GAAG,SAAS,CAI1F;AAED,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,SAAS,EAChB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACnC,MAAM,GAAG,SAAS,CAOpB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,MAAM,CAU/E"}
|
package/dist/data/pagination.js
CHANGED
|
@@ -4,29 +4,20 @@
|
|
|
4
4
|
* scale (bounded admin/definition lists, not infinite scroll over millions
|
|
5
5
|
* of rows). Keyset pagination would be faster if that ever changes.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
export function decodeCursor(cursor) {
|
|
10
|
-
if (!cursor)
|
|
11
|
-
return 0;
|
|
12
|
-
const n = parseInt(cursor, 10);
|
|
13
|
-
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
14
|
-
}
|
|
15
|
-
export function encodeCursor(offset) {
|
|
16
|
-
return String(offset);
|
|
17
|
-
}
|
|
7
|
+
import { clampPageLimit, decodeOffsetCursor, encodeOffsetCursor } from '@selvajs/platform';
|
|
8
|
+
export { decodeOffsetCursor as decodeCursor, encodeOffsetCursor as encodeCursor };
|
|
18
9
|
export function toRange(opts) {
|
|
19
|
-
const limit =
|
|
20
|
-
const from =
|
|
10
|
+
const limit = clampPageLimit(opts?.limit);
|
|
11
|
+
const from = decodeOffsetCursor(opts?.cursor);
|
|
21
12
|
return { from, to: from + limit - 1, limit };
|
|
22
13
|
}
|
|
23
14
|
export function nextCursorFromRange(range, returnedCount, totalCount) {
|
|
24
15
|
const consumed = range.from + returnedCount;
|
|
25
16
|
if (totalCount != null) {
|
|
26
|
-
return consumed < totalCount ?
|
|
17
|
+
return consumed < totalCount ? encodeOffsetCursor(consumed) : undefined;
|
|
27
18
|
}
|
|
28
19
|
// Without total, we can only infer "there might be more" if we got a full page.
|
|
29
|
-
return returnedCount >= range.limit ?
|
|
20
|
+
return returnedCount >= range.limit ? encodeOffsetCursor(consumed) : undefined;
|
|
30
21
|
}
|
|
31
22
|
export function orderColumn(orderBy) {
|
|
32
23
|
switch (orderBy) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/data/pagination.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/data/pagination.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAS3F,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,kBAAkB,IAAI,YAAY,EAAE,CAAC;AAElF,MAAM,UAAU,OAAO,CAAC,IAAuD;IAC9E,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,KAAgB,EAChB,aAAqB,EACrB,UAAqC;IAErC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5C,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACxB,OAAO,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzE,CAAC;IACD,gFAAgF;IAChF,OAAO,aAAa,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAA2C;IACtE,QAAQ,OAAO,EAAE,CAAC;QACjB,KAAK,MAAM;YACV,OAAO,MAAM,CAAC;QACf,KAAK,WAAW;YACf,OAAO,YAAY,CAAC;QACrB,KAAK,WAAW,CAAC;QACjB;YACC,OAAO,YAAY,CAAC;IACtB,CAAC;AACF,CAAC"}
|
|
@@ -3,8 +3,8 @@ import type { ClientBundle } from '../data/client.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Reads and writes `public.user_profiles.platform_permissions` (auto-seeded
|
|
5
5
|
* by the `handle_new_auth_user` trigger). Service-role throughout — RLS on
|
|
6
|
-
* `user_profiles` is too coarse for cross-user reads, so `
|
|
7
|
-
* `
|
|
6
|
+
* `user_profiles` is too coarse for cross-user reads, so `assertCanManagePermissions` /
|
|
7
|
+
* `assertCanReadPermissions` enforce the auth boundary here instead.
|
|
8
8
|
*
|
|
9
9
|
* `user_profiles.disabled` mirrors `auth.users.user_metadata.disabled` via
|
|
10
10
|
* the `sync_auth_user_disabled` trigger, so admin-count queries can exclude
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupabasePlatformPermissionStore.d.ts","sourceRoot":"","sources":["../../src/permissions/SupabasePlatformPermissionStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,MAAM,mBAAmB,CAAC;AAO3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,qBAAa,+BAAgC,YAAW,wBAAwB;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY;IAElD,OAAO,CAAC,MAAM;IAIR,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAW1E,WAAW,CAChB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,SAAS,MAAM,EAAE,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAevC,GAAG,CACR,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,SAAS,kBAAkB,EAAE,GACxC,OAAO,CAAC,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"SupabasePlatformPermissionStore.d.ts","sourceRoot":"","sources":["../../src/permissions/SupabasePlatformPermissionStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,MAAM,mBAAmB,CAAC;AAO3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,qBAAa,+BAAgC,YAAW,wBAAwB;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY;IAElD,OAAO,CAAC,MAAM;IAIR,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAW1E,WAAW,CAChB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,SAAS,MAAM,EAAE,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAevC,GAAG,CACR,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,SAAS,kBAAkB,EAAE,GACxC,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,uBAAuB,CAC5B,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,SAAS,kBAAkB,EAAE,GACxC,OAAO,CAAC,OAAO,CAAC;IAYb,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAUxD,4BAA4B,CAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAIlF,uBAAuB;CAUrC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ProviderError,
|
|
1
|
+
import { ProviderError, ALL_PLATFORM_PERMISSIONS } from '@selvajs/platform';
|
|
2
|
+
import { assertCanManagePermissions, assertCanReadPermissions, assertCanReadPermissionsBatch } from '@selvajs/platform/permissions';
|
|
2
3
|
/**
|
|
3
4
|
* Reads and writes `public.user_profiles.platform_permissions` (auto-seeded
|
|
4
5
|
* by the `handle_new_auth_user` trigger). Service-role throughout — RLS on
|
|
5
|
-
* `user_profiles` is too coarse for cross-user reads, so `
|
|
6
|
-
* `
|
|
6
|
+
* `user_profiles` is too coarse for cross-user reads, so `assertCanManagePermissions` /
|
|
7
|
+
* `assertCanReadPermissions` enforce the auth boundary here instead.
|
|
7
8
|
*
|
|
8
9
|
* `user_profiles.disabled` mirrors `auth.users.user_metadata.disabled` via
|
|
9
10
|
* the `sync_auth_user_disabled` trigger, so admin-count queries can exclude
|
|
@@ -19,7 +20,7 @@ export class SupabasePlatformPermissionStore {
|
|
|
19
20
|
return this.clients.serviceClient;
|
|
20
21
|
}
|
|
21
22
|
async getFor(ctx, userId) {
|
|
22
|
-
|
|
23
|
+
assertCanReadPermissions(ctx, userId);
|
|
23
24
|
const { data, error } = await this.client()
|
|
24
25
|
.from('user_profiles')
|
|
25
26
|
.select('platform_permissions')
|
|
@@ -30,7 +31,7 @@ export class SupabasePlatformPermissionStore {
|
|
|
30
31
|
return filterValid(data?.platform_permissions ?? []);
|
|
31
32
|
}
|
|
32
33
|
async getForBatch(ctx, userIds) {
|
|
33
|
-
|
|
34
|
+
assertCanReadPermissionsBatch(ctx);
|
|
34
35
|
const out = new Map();
|
|
35
36
|
if (userIds.length === 0)
|
|
36
37
|
return out;
|
|
@@ -46,11 +47,9 @@ export class SupabasePlatformPermissionStore {
|
|
|
46
47
|
return out;
|
|
47
48
|
}
|
|
48
49
|
async set(ctx, userId, permissions) {
|
|
49
|
-
|
|
50
|
-
// The
|
|
51
|
-
//
|
|
52
|
-
// as count-then-update let two concurrent demotions each observe the
|
|
53
|
-
// other as "another admin exists" and both commit, leaving zero admins.
|
|
50
|
+
assertCanManagePermissions(ctx);
|
|
51
|
+
// The last-admin check runs inside the RPC, under the locks that guard the
|
|
52
|
+
// write. A count here would let two demotions each see the other survive.
|
|
54
53
|
const { data, error } = await this.client().rpc('set_platform_permissions', {
|
|
55
54
|
p_user_id: userId,
|
|
56
55
|
p_permissions: [...permissions]
|
|
@@ -71,7 +70,7 @@ export class SupabasePlatformPermissionStore {
|
|
|
71
70
|
return existing ? 'last_admin' : 'not_found';
|
|
72
71
|
}
|
|
73
72
|
async claimFirstInstanceAdmin(_ctx, userId, permissions) {
|
|
74
|
-
// No `
|
|
73
|
+
// No `assertCanManagePermissions`: by definition nobody holds `instance_admin` when this
|
|
75
74
|
// succeeds. The RPC takes an advisory lock and re-reads inside it, so a
|
|
76
75
|
// second concurrent signer blocks and then correctly loses.
|
|
77
76
|
const { data, error } = await this.client().rpc('claim_first_instance_admin', {
|
|
@@ -111,44 +110,6 @@ const VALID_PERMISSIONS = new Set(ALL_PLATFORM_PERMISSIONS);
|
|
|
111
110
|
function filterValid(raw) {
|
|
112
111
|
return raw.filter((p) => VALID_PERMISSIONS.has(p));
|
|
113
112
|
}
|
|
114
|
-
// Reading one row and reading many must agree, and both must match the local
|
|
115
|
-
// provider — §8 gives `manage_instance_users` the user-admin surface, which
|
|
116
|
-
// cannot render its locks without knowing who holds `instance_admin`. This
|
|
117
|
-
// store denied both until now, so on Supabase `/admin/users` failed for exactly
|
|
118
|
-
// the role it exists to serve.
|
|
119
|
-
//
|
|
120
|
-
// Read access is not authority: `set` stays `instance_admin`-only via
|
|
121
|
-
// `assertAdmin`, and the delete/disable routes gate separately.
|
|
122
|
-
function assertCanRead(ctx, userId) {
|
|
123
|
-
assertNotShareContext(ctx, 'read permissions');
|
|
124
|
-
if (ctx.system)
|
|
125
|
-
return;
|
|
126
|
-
if (ctx.userId === userId)
|
|
127
|
-
return;
|
|
128
|
-
if (hasPermission(ctx, 'instance_admin'))
|
|
129
|
-
return;
|
|
130
|
-
if (hasPermission(ctx, 'manage_instance_users'))
|
|
131
|
-
return;
|
|
132
|
-
throw new ProviderError('Forbidden: cannot read another user’s permissions', 403);
|
|
133
|
-
}
|
|
134
|
-
function assertCanReadBatch(ctx) {
|
|
135
|
-
assertNotShareContext(ctx, 'read permissions');
|
|
136
|
-
if (ctx.system)
|
|
137
|
-
return;
|
|
138
|
-
if (hasPermission(ctx, 'instance_admin'))
|
|
139
|
-
return;
|
|
140
|
-
if (hasPermission(ctx, 'manage_instance_users'))
|
|
141
|
-
return;
|
|
142
|
-
throw new ProviderError('Forbidden: instance admin or manage_instance_users required', 403);
|
|
143
|
-
}
|
|
144
|
-
function assertAdmin(ctx) {
|
|
145
|
-
assertNotShareContext(ctx, 'manage permissions');
|
|
146
|
-
if (ctx.system)
|
|
147
|
-
return;
|
|
148
|
-
if (hasPermission(ctx, 'instance_admin'))
|
|
149
|
-
return;
|
|
150
|
-
throw new ProviderError('Forbidden: instance admin required', 403);
|
|
151
|
-
}
|
|
152
113
|
function mapError(e) {
|
|
153
114
|
const pg = e;
|
|
154
115
|
if (pg?.code === '23505')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupabasePlatformPermissionStore.js","sourceRoot":"","sources":["../../src/permissions/SupabasePlatformPermissionStore.ts"],"names":[],"mappings":"AAMA,OAAO,
|
|
1
|
+
{"version":3,"file":"SupabasePlatformPermissionStore.js","sourceRoot":"","sources":["../../src/permissions/SupabasePlatformPermissionStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,11 +34,12 @@ export declare class SupabaseStorageProvider implements IStorageProvider {
|
|
|
34
34
|
* `.gh`/`.ghx` source files are always private, regardless of what
|
|
35
35
|
* `classifyAssetPath` says — confidentiality by extension means a
|
|
36
36
|
* path-scheme rename can't silently move sources into the public bucket.
|
|
37
|
-
* Everything else routes by asset class
|
|
37
|
+
* Everything else routes by asset class, and only a `public` class reaches
|
|
38
|
+
* the public bucket: an unclassified path stays private.
|
|
38
39
|
*/
|
|
39
40
|
private bucketFor;
|
|
40
41
|
get(storagePath: string): Promise<Uint8Array | null>;
|
|
41
|
-
put(storagePath: string, data: Uint8Array, contentType?: string): Promise<
|
|
42
|
+
put(storagePath: string, data: Uint8Array, contentType?: string): Promise<string>;
|
|
42
43
|
delete(storagePath: string): Promise<void>;
|
|
43
44
|
deletePrefix(prefix: string): Promise<void>;
|
|
44
45
|
private deletePrefixInBucket;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupabaseStorageProvider.d.ts","sourceRoot":"","sources":["../../src/storage/SupabaseStorageProvider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC7C,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"SupabaseStorageProvider.d.ts","sourceRoot":"","sources":["../../src/storage/SupabaseStorageProvider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC7C,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAID,qBAAa,uBAAwB,YAAW,gBAAgB;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,uBAAuB;gBAcpE,MAAM,EAAE,6BAA6B;IAUjD;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAMX,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAapD,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBjF,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAWnC,oBAAoB;IAYlC,kHAAkH;YACpG,WAAW;IA4BzB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;CAMzC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createClient } from '@supabase/supabase-js';
|
|
2
2
|
import { transcodeImageIfNeeded, classifyAssetPath } from '@selvajs/platform/storage';
|
|
3
|
+
const LIST_PAGE_SIZE = 1000;
|
|
3
4
|
export class SupabaseStorageProvider {
|
|
4
5
|
client;
|
|
5
6
|
publicBucket;
|
|
@@ -34,15 +35,14 @@ export class SupabaseStorageProvider {
|
|
|
34
35
|
* `.gh`/`.ghx` source files are always private, regardless of what
|
|
35
36
|
* `classifyAssetPath` says — confidentiality by extension means a
|
|
36
37
|
* path-scheme rename can't silently move sources into the public bucket.
|
|
37
|
-
* Everything else routes by asset class
|
|
38
|
+
* Everything else routes by asset class, and only a `public` class reaches
|
|
39
|
+
* the public bucket: an unclassified path stays private.
|
|
38
40
|
*/
|
|
39
41
|
bucketFor(storagePath) {
|
|
40
42
|
if (/\.(gh|ghx)$/i.test(storagePath))
|
|
41
43
|
return this.privateBucket;
|
|
42
44
|
const cls = classifyAssetPath(storagePath);
|
|
43
|
-
|
|
44
|
-
return this.privateBucket;
|
|
45
|
-
return this.publicBucket;
|
|
45
|
+
return cls?.class.visibility === 'public' ? this.publicBucket : this.privateBucket;
|
|
46
46
|
}
|
|
47
47
|
async get(storagePath) {
|
|
48
48
|
const bucket = this.bucketFor(storagePath);
|
|
@@ -64,12 +64,14 @@ export class SupabaseStorageProvider {
|
|
|
64
64
|
.from(bucket)
|
|
65
65
|
.upload(transcoded.path, transcoded.data, {
|
|
66
66
|
contentType: transcoded.contentType,
|
|
67
|
-
//
|
|
67
|
+
// `put` upserts, so a changed object needs a new URL: `withCacheBust` is
|
|
68
|
+
// how callers get one. A year is safe only because of that.
|
|
68
69
|
cacheControl: '31536000',
|
|
69
70
|
upsert: true
|
|
70
71
|
});
|
|
71
72
|
if (error)
|
|
72
73
|
throw error;
|
|
74
|
+
return transcoded.path;
|
|
73
75
|
}
|
|
74
76
|
async delete(storagePath) {
|
|
75
77
|
const bucket = this.bucketFor(storagePath);
|
|
@@ -78,6 +80,9 @@ export class SupabaseStorageProvider {
|
|
|
78
80
|
throw error;
|
|
79
81
|
}
|
|
80
82
|
async deletePrefix(prefix) {
|
|
83
|
+
if (prefix.replace(/\/+$/, '') === '') {
|
|
84
|
+
throw new Error(`Refusing to delete the whole store: prefix ${JSON.stringify(prefix)}`);
|
|
85
|
+
}
|
|
81
86
|
// A definition's GUID prefix may have files in either bucket.
|
|
82
87
|
await Promise.all([
|
|
83
88
|
this.deletePrefixInBucket(this.publicBucket, prefix),
|
|
@@ -103,21 +108,27 @@ export class SupabaseStorageProvider {
|
|
|
103
108
|
const stack = [prefix];
|
|
104
109
|
while (stack.length > 0) {
|
|
105
110
|
const dir = stack.pop();
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
for (let offset = 0;; offset += LIST_PAGE_SIZE) {
|
|
112
|
+
const { data, error } = await this.client.storage.from(bucket).list(dir, {
|
|
113
|
+
limit: LIST_PAGE_SIZE,
|
|
114
|
+
offset,
|
|
115
|
+
sortBy: { column: 'name', order: 'asc' }
|
|
116
|
+
});
|
|
117
|
+
if (error)
|
|
118
|
+
throw error;
|
|
119
|
+
const entries = data ?? [];
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
const full = dir ? `${dir}/${entry.name}` : entry.name;
|
|
122
|
+
// storage-js marks folder entries with id === null.
|
|
123
|
+
if (entry.id === null) {
|
|
124
|
+
stack.push(full);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
out.push(full);
|
|
128
|
+
}
|
|
120
129
|
}
|
|
130
|
+
if (entries.length < LIST_PAGE_SIZE)
|
|
131
|
+
break;
|
|
121
132
|
}
|
|
122
133
|
}
|
|
123
134
|
return out;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupabaseStorageProvider.js","sourceRoot":"","sources":["../../src/storage/SupabaseStorageProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AA2BtF,MAAM,OAAO,uBAAuB;IAClB,MAAM,CAAiB;IACvB,YAAY,CAAS;IACrB,aAAa,CAAS;IACtB,aAAa,CAAS;IACtB,gBAAgB,CAAS;IAE1C,MAAM,CAAC,OAAO,CAAC,GAAuC;QACrD,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,CAAC;QACrC,MAAM,cAAc,GAAG,GAAG,CAAC,yBAAyB,CAAC;QACrD,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5E,IAAI,CAAC,cAAc;YAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QAC5F,OAAO,IAAI,uBAAuB,CAAC;YAClC,WAAW;YACX,cAAc;YACd,YAAY,EAAE,GAAG,CAAC,sBAAsB;YACxC,aAAa,EAAE,GAAG,CAAC,uBAAuB;YAC1C,gBAAgB,EAAE,GAAG,CAAC,2BAA2B;SACjD,CAAC,CAAC;IACJ,CAAC;IAED,YAAY,MAAqC;QAChD,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,cAAc,EAAE;YACrE,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE;SACxD,CAAC,CAAC;QACH,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,cAAc,CAAC;QAC1D,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,eAAe,CAAC;QAC7D,IAAI,CAAC,aAAa,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,6BAA6B,IAAI,CAAC,YAAY,EAAE,CAAC;QAC9G,IAAI,CAAC,gBAAgB,GAAG,CAAC,MAAM,CAAC,gBAAgB,IAAI,YAAY,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtF,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"SupabaseStorageProvider.js","sourceRoot":"","sources":["../../src/storage/SupabaseStorageProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { IUserProfileStore, RecentRun, RequestContext, UserManagementResult, UserProfile } from '@selvajs/platform';
|
|
2
2
|
import type { ClientBundle } from '../data/client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Backed by `public.user_profiles` (one row per `auth.users.id`), auto-seeded
|
|
5
|
+
* by the `handle_new_auth_user` trigger on signup — this store never inserts,
|
|
6
|
+
* only reads/updates. Service-role throughout since RLS here is too coarse
|
|
7
|
+
* for server-side flows like the hooks.server.ts profile load; `assertCanAccessProfile`
|
|
8
|
+
* enforces "own profile only" at the app layer instead.
|
|
9
|
+
*/
|
|
3
10
|
export declare class SupabaseUserProfileProvider implements IUserProfileStore {
|
|
4
11
|
private readonly clients;
|
|
5
12
|
constructor(clients: ClientBundle);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupabaseUserProfileProvider.d.ts","sourceRoot":"","sources":["../../src/userProfile/SupabaseUserProfileProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,WAAW,EACX,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"SupabaseUserProfileProvider.d.ts","sourceRoot":"","sources":["../../src/userProfile/SupabaseUserProfileProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,WAAW,EACX,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;GAMG;AAEH,qBAAa,2BAA4B,YAAW,iBAAiB;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY;IAElD,OAAO,CAAC,MAAM;IAIR,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAW5E,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAWrF,aAAa,CAClB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC,oBAAoB,CAAC;IAe1B,cAAc,CACnB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,CAAC;IAkB1B,gBAAgB,CACrB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,CAAC;IAgB1B,SAAS,CACd,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,SAAS,GACZ,OAAO,CAAC,oBAAoB,CAAC;CAgBhC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ProviderError
|
|
1
|
+
import { ProviderError } from '@selvajs/platform';
|
|
2
|
+
import { assertCanAccessProfile, MAX_RECENT_RUNS } from '@selvajs/platform/userProfile';
|
|
2
3
|
/**
|
|
3
4
|
* Backed by `public.user_profiles` (one row per `auth.users.id`), auto-seeded
|
|
4
5
|
* by the `handle_new_auth_user` trigger on signup — this store never inserts,
|
|
5
6
|
* only reads/updates. Service-role throughout since RLS here is too coarse
|
|
6
|
-
* for server-side flows like the hooks.server.ts profile load; `
|
|
7
|
+
* for server-side flows like the hooks.server.ts profile load; `assertCanAccessProfile`
|
|
7
8
|
* enforces "own profile only" at the app layer instead.
|
|
8
9
|
*/
|
|
9
|
-
const MAX_RECENT_RUNS = 20;
|
|
10
10
|
export class SupabaseUserProfileProvider {
|
|
11
11
|
clients;
|
|
12
12
|
constructor(clients) {
|
|
@@ -16,7 +16,7 @@ export class SupabaseUserProfileProvider {
|
|
|
16
16
|
return this.clients.serviceClient;
|
|
17
17
|
}
|
|
18
18
|
async getProfile(ctx, userId) {
|
|
19
|
-
|
|
19
|
+
assertCanAccessProfile(ctx, userId);
|
|
20
20
|
const { data, error } = await this.client()
|
|
21
21
|
.from('user_profiles')
|
|
22
22
|
.select('user_id, display_name, starred_definitions, recent_runs')
|
|
@@ -27,7 +27,7 @@ export class SupabaseUserProfileProvider {
|
|
|
27
27
|
return data ? rowToProfile(data) : null;
|
|
28
28
|
}
|
|
29
29
|
async getProfiles(_ctx, userIds) {
|
|
30
|
-
// No
|
|
30
|
+
// No assertCanAccessProfile: this serves display-name lookups across other users.
|
|
31
31
|
if (userIds.length === 0)
|
|
32
32
|
return [];
|
|
33
33
|
const { data, error } = await this.client()
|
|
@@ -39,7 +39,7 @@ export class SupabaseUserProfileProvider {
|
|
|
39
39
|
return (data ?? []).map(rowToProfile);
|
|
40
40
|
}
|
|
41
41
|
async updateProfile(ctx, userId, patch) {
|
|
42
|
-
|
|
42
|
+
assertCanAccessProfile(ctx, userId);
|
|
43
43
|
const row = {};
|
|
44
44
|
if (patch.displayName !== undefined)
|
|
45
45
|
row.display_name = patch.displayName;
|
|
@@ -55,7 +55,7 @@ export class SupabaseUserProfileProvider {
|
|
|
55
55
|
return data && data.length > 0 ? 'ok' : 'not_found';
|
|
56
56
|
}
|
|
57
57
|
async starDefinition(ctx, userId, definitionId) {
|
|
58
|
-
|
|
58
|
+
assertCanAccessProfile(ctx, userId);
|
|
59
59
|
// Read-modify-write races under concurrent starring; a SECURITY DEFINER
|
|
60
60
|
// RPC using array_append + array_distinct would close it if that matters.
|
|
61
61
|
const existing = await this.getProfile(ctx, userId);
|
|
@@ -74,7 +74,7 @@ export class SupabaseUserProfileProvider {
|
|
|
74
74
|
return data && data.length > 0 ? 'ok' : 'not_found';
|
|
75
75
|
}
|
|
76
76
|
async unstarDefinition(ctx, userId, definitionId) {
|
|
77
|
-
|
|
77
|
+
assertCanAccessProfile(ctx, userId);
|
|
78
78
|
const existing = await this.getProfile(ctx, userId);
|
|
79
79
|
if (!existing)
|
|
80
80
|
return 'not_found';
|
|
@@ -91,7 +91,7 @@ export class SupabaseUserProfileProvider {
|
|
|
91
91
|
return data && data.length > 0 ? 'ok' : 'not_found';
|
|
92
92
|
}
|
|
93
93
|
async recordRun(ctx, userId, run) {
|
|
94
|
-
|
|
94
|
+
assertCanAccessProfile(ctx, userId);
|
|
95
95
|
const existing = await this.getProfile(ctx, userId);
|
|
96
96
|
if (!existing)
|
|
97
97
|
return 'not_found';
|
|
@@ -107,16 +107,6 @@ export class SupabaseUserProfileProvider {
|
|
|
107
107
|
return data && data.length > 0 ? 'ok' : 'not_found';
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
function assertCanAccess(ctx, userId) {
|
|
111
|
-
assertNotShareContext(ctx, 'access user profiles');
|
|
112
|
-
if (ctx.system)
|
|
113
|
-
return;
|
|
114
|
-
if (ctx.userId === userId)
|
|
115
|
-
return;
|
|
116
|
-
if (hasPermission(ctx, 'instance_admin'))
|
|
117
|
-
return;
|
|
118
|
-
throw new ProviderError('Forbidden: cannot access another user’s profile', 403);
|
|
119
|
-
}
|
|
120
110
|
function rowToProfile(row) {
|
|
121
111
|
return {
|
|
122
112
|
userId: row.user_id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupabaseUserProfileProvider.js","sourceRoot":"","sources":["../../src/userProfile/SupabaseUserProfileProvider.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"SupabaseUserProfileProvider.js","sourceRoot":"","sources":["../../src/userProfile/SupabaseUserProfileProvider.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/supabase-provider",
|
|
3
|
-
"version": "0.20.0-beta.
|
|
3
|
+
"version": "0.20.0-beta.6",
|
|
4
4
|
"description": "Supabase (Auth + Postgres + Storage) implementations of @selvajs/platform interfaces",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
"selva-source": "./src/userProfile/index.ts",
|
|
61
61
|
"types": "./dist/userProfile/index.d.ts",
|
|
62
62
|
"import": "./dist/userProfile/index.js"
|
|
63
|
+
},
|
|
64
|
+
"./config": {
|
|
65
|
+
"selva-source": "./src/config.ts",
|
|
66
|
+
"types": "./dist/config.d.ts",
|
|
67
|
+
"import": "./dist/config.js"
|
|
63
68
|
}
|
|
64
69
|
},
|
|
65
70
|
"files": [
|
|
@@ -72,10 +77,11 @@
|
|
|
72
77
|
"dependencies": {
|
|
73
78
|
"@supabase/supabase-js": "^2.118.0",
|
|
74
79
|
"sharp": "^0.35.5",
|
|
75
|
-
"@selvajs/platform": "^0.
|
|
80
|
+
"@selvajs/platform": "^1.0.0-beta.9"
|
|
76
81
|
},
|
|
77
82
|
"devDependencies": {
|
|
78
|
-
"@
|
|
83
|
+
"@electric-sql/pglite": "0.3.16",
|
|
84
|
+
"@types/node": "^24.0.0",
|
|
79
85
|
"supabase": "^2.122.0",
|
|
80
86
|
"typescript": "~6.0.3",
|
|
81
87
|
"vitest": "^5.0.5",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- EXECUTE on `selva` functions matches who calls them
|
|
3
|
+
--
|
|
4
|
+
-- PostgREST exposes `selva` and the anon key is publishable, so the grant is all
|
|
5
|
+
-- that stands between a caller and a SECURITY DEFINER function.
|
|
6
|
+
--
|
|
7
|
+
-- Postgres gives every new function EXECUTE to PUBLIC, and per-schema default
|
|
8
|
+
-- privileges can only add to that. Each new function in `selva` has to revoke
|
|
9
|
+
-- from public itself: function-privileges.test.ts fails until it does.
|
|
10
|
+
-- ============================================================================
|
|
11
|
+
|
|
12
|
+
alter default privileges in schema selva revoke all on functions from anon, authenticated;
|
|
13
|
+
|
|
14
|
+
-- Server-only: the app reaches these through the service-role client. PUBLIC is
|
|
15
|
+
-- listed because anon and authenticated inherit from it. Revoking on a trigger
|
|
16
|
+
-- function is safe: Postgres checks EXECUTE when a trigger is created, not when
|
|
17
|
+
-- it fires.
|
|
18
|
+
revoke all on function
|
|
19
|
+
selva.claim_first_instance_admin(uuid, text[]),
|
|
20
|
+
selva.handle_new_auth_user(),
|
|
21
|
+
selva.migration_head(),
|
|
22
|
+
selva.redact_audit_event_email(text),
|
|
23
|
+
selva.set_platform_permissions(uuid, text[]),
|
|
24
|
+
selva.set_updated_at(),
|
|
25
|
+
selva.sync_auth_user_disabled(),
|
|
26
|
+
selva.try_increment_share_link_solve_count(uuid)
|
|
27
|
+
from public, anon, authenticated;
|
|
28
|
+
|
|
29
|
+
grant execute on function
|
|
30
|
+
selva.claim_first_instance_admin(uuid, text[]),
|
|
31
|
+
selva.handle_new_auth_user(),
|
|
32
|
+
selva.migration_head(),
|
|
33
|
+
selva.redact_audit_event_email(text),
|
|
34
|
+
selva.set_platform_permissions(uuid, text[]),
|
|
35
|
+
selva.set_updated_at(),
|
|
36
|
+
selva.sync_auth_user_disabled(),
|
|
37
|
+
selva.try_increment_share_link_solve_count(uuid)
|
|
38
|
+
to service_role;
|
|
39
|
+
|
|
40
|
+
-- Solves and version uploads always carry a signed-in user or the service role.
|
|
41
|
+
revoke all on function
|
|
42
|
+
selva.increment_run_count(uuid),
|
|
43
|
+
selva.reserve_next_version_number(uuid)
|
|
44
|
+
from public, anon;
|