Skip to content

Commit 7d32bcb

Browse files
committed
added scripts and issues
1 parent f64631e commit 7d32bcb

4 files changed

Lines changed: 180 additions & 0 deletions

File tree

backend/TESTING.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,28 @@ deno run -A backend/scripts/activate_subscription.ts
105105
- First login (with active subscription): `POST /session` returns `{ api_key: "ext_..." }` once.
106106
- Manual issue (admin): `POST /admin/users/:id/keys` → returns `{ key: "ext_..." }` (plaintext once).
107107

108+
### Helper script (login and issue key)
109+
```
110+
export SUPABASE_URL=https://lkiyoqrviddogkizywgd.supabase.co
111+
export SUPABASE_ANON_KEY=<anon key>
112+
export EMAIL=user@example.com
113+
export PASSWORD=secret
114+
export BASE_URL=https://lkiyoqrviddogkizywgd.functions.supabase.co
115+
deno run -A backend/scripts/issue_custom_key.ts
116+
```
117+
If you see “Subscription required”, activate a test subscription first using `backend/scripts/activate_subscription.ts`.
118+
119+
### Helper script (login and first-login session key)
120+
```
121+
export SUPABASE_URL=https://lkiyoqrviddogkizywgd.supabase.co
122+
export SUPABASE_ANON_KEY=<anon key>
123+
export EMAIL=user@example.com
124+
export PASSWORD=secret
125+
export BASE_URL=https://lkiyoqrviddogkizywgd.functions.supabase.co
126+
deno run -A backend/scripts/login_and_session_key.ts
127+
```
128+
This returns `SESSION_TOKEN` and, if eligible (active subscription and no key yet), `API_KEY` once.
129+
108130
## 1:1 OpenAI Gateway (no DNS)
109131
- Base: `https://lkiyoqrviddogkizywgd.functions.supabase.co/openai`
110132
- Use `Authorization: Bearer ext_...` (custom key) and call `/v1/*` endpoints as usual.

backend/USAGE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ Set in app config (`Config.plist` or environment):
4242
- Manual (admin): `POST /admin/users/:id/keys` returns a plaintext `ext_…` key once.
4343
- Users can view masked keys via `GET /key`.
4444

45+
### CLI helper: login and issue a key
46+
```
47+
export SUPABASE_URL=https://lkiyoqrviddogkizywgd.supabase.co
48+
export SUPABASE_ANON_KEY=<anon key>
49+
export EMAIL=user@example.com
50+
export PASSWORD=secret
51+
export BASE_URL=https://lkiyoqrviddogkizywgd.functions.supabase.co
52+
deno run -A backend/scripts/issue_custom_key.ts
53+
```
54+
If it reports “Subscription required”, activate a test subscription via `backend/scripts/activate_subscription.ts`.
55+
56+
### CLI helper: login and receive first-login key via /session
57+
```
58+
export SUPABASE_URL=https://lkiyoqrviddogkizywgd.supabase.co
59+
export SUPABASE_ANON_KEY=<anon key>
60+
export EMAIL=user@example.com
61+
export PASSWORD=secret
62+
export BASE_URL=https://lkiyoqrviddogkizywgd.functions.supabase.co
63+
deno run -A backend/scripts/login_and_session_key.ts
64+
```
65+
The script prints `SESSION_TOKEN` and, if eligible, `API_KEY` (store in Config.plist).
66+
4567
## 6) Admin Operations (JWT with `profiles.is_admin = true`)
4668
- Aggregates: `GET /admin`
4769
- Users list: `GET /admin/users?limit=100`
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Login with email/password and issue a custom API key via /key
2+
// Env required:
3+
// SUPABASE_URL=https://<ref>.supabase.co
4+
// SUPABASE_ANON_KEY=...
5+
// EMAIL=user@example.com
6+
// PASSWORD=secret
7+
// BASE_URL=https://<ref>.functions.supabase.co # backend base (no /openai)
8+
// Usage:
9+
// deno run -A backend/scripts/issue_custom_key.ts
10+
11+
const supaUrl = Deno.env.get('SUPABASE_URL');
12+
const anonKey = Deno.env.get('SUPABASE_ANON_KEY');
13+
const email = Deno.env.get('EMAIL');
14+
const password = Deno.env.get('PASSWORD');
15+
const base = (Deno.env.get('BASE_URL') || '').replace(/\/$/, '');
16+
17+
if (!supaUrl || !anonKey || !email || !password || !base) {
18+
console.error('Missing env. Need SUPABASE_URL, SUPABASE_ANON_KEY, EMAIL, PASSWORD, BASE_URL');
19+
Deno.exit(1);
20+
}
21+
22+
const tokenEndpoint = `${supaUrl.replace(/\/$/, '')}/auth/v1/token?grant_type=password`;
23+
const authRes = await fetch(tokenEndpoint, {
24+
method: 'POST',
25+
headers: { 'Content-Type': 'application/json', apikey: anonKey },
26+
body: JSON.stringify({ email, password }),
27+
});
28+
if (!authRes.ok) {
29+
console.error('Auth failed:', authRes.status, await authRes.text());
30+
Deno.exit(1);
31+
}
32+
const auth = await authRes.json();
33+
const accessToken: string | undefined = auth?.access_token;
34+
const userId: string | undefined = auth?.user?.id;
35+
if (!accessToken) {
36+
console.error('No access_token in auth response');
37+
console.log(JSON.stringify(auth, null, 2));
38+
Deno.exit(1);
39+
}
40+
console.log('Logged in as:', userId || '(unknown)');
41+
42+
// Attempt to issue a new custom API key
43+
const keyEndpoint = `${base}/key`;
44+
const keyRes = await fetch(keyEndpoint, {
45+
method: 'POST',
46+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` },
47+
});
48+
49+
const bodyText = await keyRes.text();
50+
if (keyRes.status === 402) {
51+
console.error('Subscription required to issue API key.');
52+
console.error('Tip: activate a test subscription via backend/scripts/activate_subscription.ts');
53+
Deno.exit(2);
54+
}
55+
if (!keyRes.ok) {
56+
console.error('Issuing key failed:', keyRes.status, bodyText);
57+
Deno.exit(1);
58+
}
59+
60+
try {
61+
const data = JSON.parse(bodyText);
62+
if (data?.key && data?.prefix) {
63+
console.log('Issued custom API key:');
64+
console.log('KEY=', data.key);
65+
console.log('PREFIX=', data.prefix);
66+
} else {
67+
console.log('Response:', data);
68+
}
69+
} catch {
70+
console.log('Response (raw):', bodyText);
71+
}
72+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Login with email/password and call /session to receive first-login api_key (if active subscription)
2+
// Env required:
3+
// SUPABASE_URL=https://<ref>.supabase.co
4+
// SUPABASE_ANON_KEY=...
5+
// EMAIL=user@example.com
6+
// PASSWORD=secret
7+
// BASE_URL=https://<ref>.functions.supabase.co # backend base (no /openai)
8+
// Usage:
9+
// deno run -A backend/scripts/login_and_session_key.ts
10+
11+
const supaUrl = Deno.env.get('SUPABASE_URL');
12+
const anonKey = Deno.env.get('SUPABASE_ANON_KEY');
13+
const email = Deno.env.get('EMAIL');
14+
const password = Deno.env.get('PASSWORD');
15+
const base = (Deno.env.get('BASE_URL') || '').replace(/\/$/, '');
16+
17+
if (!supaUrl || !anonKey || !email || !password || !base) {
18+
console.error('Missing env. Need SUPABASE_URL, SUPABASE_ANON_KEY, EMAIL, PASSWORD, BASE_URL');
19+
Deno.exit(1);
20+
}
21+
22+
const tokenEndpoint = `${supaUrl.replace(/\/$/, '')}/auth/v1/token?grant_type=password`;
23+
const authRes = await fetch(tokenEndpoint, {
24+
method: 'POST',
25+
headers: { 'Content-Type': 'application/json', apikey: anonKey },
26+
body: JSON.stringify({ email, password }),
27+
});
28+
if (!authRes.ok) {
29+
console.error('Auth failed:', authRes.status, await authRes.text());
30+
Deno.exit(1);
31+
}
32+
const auth = await authRes.json();
33+
const accessToken: string | undefined = auth?.access_token;
34+
const userId: string | undefined = auth?.user?.id;
35+
if (!accessToken) {
36+
console.error('No access_token in auth response');
37+
console.log(JSON.stringify(auth, null, 2));
38+
Deno.exit(1);
39+
}
40+
console.log('Logged in as:', userId || '(unknown)');
41+
42+
// Call /session (Supabase Edge Function) with Authorization: Bearer <JWT>
43+
const sessRes = await fetch(`${base}/session`, {
44+
method: 'POST',
45+
headers: { Authorization: `Bearer ${accessToken}` },
46+
});
47+
const sessBody = await sessRes.text();
48+
if (!sessRes.ok) {
49+
console.error('Session failed:', sessRes.status, sessBody);
50+
Deno.exit(1);
51+
}
52+
try {
53+
const data = JSON.parse(sessBody);
54+
console.log('SESSION_TOKEN=', data?.token || '(missing)');
55+
if (data?.api_key) {
56+
console.log('API_KEY=', data.api_key);
57+
console.log('(Store API_KEY in Config.plist for the app)');
58+
} else {
59+
console.log('No api_key returned (either already issued or subscription inactive).');
60+
}
61+
} catch {
62+
console.log('Session response (raw):', sessBody);
63+
}
64+

0 commit comments

Comments
 (0)