run402 1.38.0 → 1.39.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/lib/projects.mjs +52 -1
- package/package.json +1 -1
package/lib/projects.mjs
CHANGED
|
@@ -17,7 +17,10 @@ Subcommands:
|
|
|
17
17
|
rest [id] <table> [params] Query a table via the REST API (PostgREST)
|
|
18
18
|
usage [id] Show compute/storage usage for a project
|
|
19
19
|
schema [id] Inspect the database schema
|
|
20
|
-
rls [id] <template> <tables_json>
|
|
20
|
+
rls [id] <template> <tables_json> ⚠ DEPRECATED (sunset 2026-05-23) - use 'apply-expose' instead
|
|
21
|
+
apply-expose [id] <manifest_json> Apply a declarative authorization manifest (supersedes 'rls')
|
|
22
|
+
apply-expose [id] --file <path> Apply a manifest from a JSON file
|
|
23
|
+
get-expose [id] Get the current authorization manifest
|
|
21
24
|
delete [id] Immediately and irreversibly delete a project (cascade purge) and remove from local state
|
|
22
25
|
pin [id] Pin a project (prevents expiry/GC)
|
|
23
26
|
promote-user [id] <email> Promote a user to project_admin role
|
|
@@ -37,6 +40,8 @@ Examples:
|
|
|
37
40
|
run402 projects usage abc123
|
|
38
41
|
run402 projects schema abc123
|
|
39
42
|
run402 projects rls abc123 public_read_authenticated_write '[{"table":"posts"}]'
|
|
43
|
+
run402 projects apply-expose abc123 --file manifest.json
|
|
44
|
+
run402 projects get-expose abc123
|
|
40
45
|
run402 projects keys abc123
|
|
41
46
|
run402 projects delete abc123
|
|
42
47
|
|
|
@@ -52,6 +57,14 @@ Notes:
|
|
|
52
57
|
public_read_authenticated_write anyone reads; any authenticated user writes any row
|
|
53
58
|
public_read_write_UNRESTRICTED fully open (anon_key writes); use 'run402 deploy' with a manifest
|
|
54
59
|
that includes "i_understand_this_is_unrestricted": true
|
|
60
|
+
- 'rls' is deprecated (sunset 2026-05-23) — migrate to 'apply-expose'.
|
|
61
|
+
The expose manifest declares the full authorization surface (tables, views,
|
|
62
|
+
RPCs) in one convergent call. Tables not listed with expose:true are dark
|
|
63
|
+
by default. Sample manifest:
|
|
64
|
+
{"version":"1",
|
|
65
|
+
"tables":[{"name":"posts","expose":true,"policy":"user_owns_rows","owner_column":"user_id","force_owner_on_insert":true}],
|
|
66
|
+
"views":[],
|
|
67
|
+
"rpcs":[]}
|
|
55
68
|
`;
|
|
56
69
|
|
|
57
70
|
const SUB_HELP = {
|
|
@@ -169,6 +182,42 @@ async function rls(projectId, template, tablesJson) {
|
|
|
169
182
|
console.log(JSON.stringify(data, null, 2));
|
|
170
183
|
}
|
|
171
184
|
|
|
185
|
+
async function applyExpose(projectId, args = []) {
|
|
186
|
+
const p = findProject(projectId);
|
|
187
|
+
let file = null;
|
|
188
|
+
let inline = null;
|
|
189
|
+
for (let i = 0; i < args.length; i++) {
|
|
190
|
+
if (args[i] === "--file" && args[i + 1]) { file = args[++i]; }
|
|
191
|
+
else if (!inline && !args[i].startsWith("--")) { inline = args[i]; }
|
|
192
|
+
}
|
|
193
|
+
const raw = file ? readFileSync(file, "utf-8") : inline;
|
|
194
|
+
if (!raw) {
|
|
195
|
+
console.error(JSON.stringify({ status: "error", message: "Missing manifest. Provide inline JSON or use --file <path>" }));
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
let manifest;
|
|
199
|
+
try { manifest = JSON.parse(raw); }
|
|
200
|
+
catch { console.error(JSON.stringify({ status: "error", message: "Invalid JSON for manifest" })); process.exit(1); }
|
|
201
|
+
const res = await fetch(`${API}/projects/v1/admin/${projectId}/expose`, {
|
|
202
|
+
method: "POST",
|
|
203
|
+
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
204
|
+
body: JSON.stringify(manifest),
|
|
205
|
+
});
|
|
206
|
+
const data = await res.json();
|
|
207
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
208
|
+
console.log(JSON.stringify(data, null, 2));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function getExpose(projectId) {
|
|
212
|
+
const p = findProject(projectId);
|
|
213
|
+
const res = await fetch(`${API}/projects/v1/admin/${projectId}/expose`, {
|
|
214
|
+
headers: { "Authorization": `Bearer ${p.service_key}` },
|
|
215
|
+
});
|
|
216
|
+
const data = await res.json();
|
|
217
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
218
|
+
console.log(JSON.stringify(data, null, 2));
|
|
219
|
+
}
|
|
220
|
+
|
|
172
221
|
async function list() {
|
|
173
222
|
const store = loadKeyStore();
|
|
174
223
|
const entries = Object.entries(store.projects);
|
|
@@ -335,6 +384,8 @@ export async function run(sub, args) {
|
|
|
335
384
|
case "usage": { const { projectId } = resolvePositionalProject(args); await usage(projectId); break; }
|
|
336
385
|
case "schema": { const { projectId } = resolvePositionalProject(args); await schema(projectId); break; }
|
|
337
386
|
case "rls": { const { projectId, rest } = resolvePositionalProject(args); await rls(projectId, rest[0], rest[1]); break; }
|
|
387
|
+
case "apply-expose": { const { projectId, rest } = resolvePositionalProject(args); await applyExpose(projectId, rest); break; }
|
|
388
|
+
case "get-expose": { const { projectId } = resolvePositionalProject(args); await getExpose(projectId); break; }
|
|
338
389
|
case "delete": { const { projectId } = resolvePositionalProject(args); await deleteProject(projectId); break; }
|
|
339
390
|
case "pin": { const { projectId } = resolvePositionalProject(args); await pin(projectId); break; }
|
|
340
391
|
case "promote-user": { const { projectId, rest } = resolvePositionalProject(args); await promoteUser(projectId, rest[0]); break; }
|
package/package.json
CHANGED