@profullstack/nichedb 0.50.0 → 0.52.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 +1 -1
- package/src/index.js +173 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { homedir, tmpdir } from 'node:os';
|
|
|
15
15
|
import { join } from 'node:path';
|
|
16
16
|
import { createInterface } from 'node:readline';
|
|
17
17
|
|
|
18
|
-
export const VERSION = '0.
|
|
18
|
+
export const VERSION = '0.52.0';
|
|
19
19
|
const GEO_FLAGS = ['lat', 'long', 'radius', 'unit', 'bbox', 'sort', 'offset'];
|
|
20
20
|
const DEFAULT_API = process.env.NICHEDB_API ?? 'https://nichedb.dev';
|
|
21
21
|
const CONFIG_DIR = join(process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'nichedb');
|
|
@@ -176,6 +176,14 @@ export const COMMANDS = [
|
|
|
176
176
|
summary: 'The feeds your key follows.',
|
|
177
177
|
options: ['--json'],
|
|
178
178
|
},
|
|
179
|
+
{
|
|
180
|
+
name: 'webhooks',
|
|
181
|
+
usage:
|
|
182
|
+
'webhooks [list] | add <url> [--events a,b] [--description …] | rm|test|secret|rotate|enable|disable <id> | deliveries [<id>]',
|
|
183
|
+
summary:
|
|
184
|
+
'Account webhooks (key required): signed Standard Webhooks POSTs for feed.item.published, feed.followed and more.',
|
|
185
|
+
options: ['--events', '--description', '--limit', '--json'],
|
|
186
|
+
},
|
|
179
187
|
{
|
|
180
188
|
name: 'rss',
|
|
181
189
|
usage: 'rss <feed>',
|
|
@@ -321,6 +329,26 @@ export const COMMANDS = [
|
|
|
321
329
|
summary: 'Take a handle: your URL becomes /c/profiles/<handle> (key required).',
|
|
322
330
|
options: [],
|
|
323
331
|
},
|
|
332
|
+
{
|
|
333
|
+
name: 'redeem',
|
|
334
|
+
usage: 'redeem <code>',
|
|
335
|
+
summary: 'Redeem a credit code for days of Premium (key required).',
|
|
336
|
+
options: ['--json'],
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: 'invites',
|
|
340
|
+
usage: 'invites [create [--note …] | revoke <id>]',
|
|
341
|
+
summary:
|
|
342
|
+
'Your invites: how many are left, and each one. create makes one (its link is shown once); revoke withdraws an unused one (key required).',
|
|
343
|
+
options: ['--note <who it is for>', '--json'],
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: 'credits mint',
|
|
347
|
+
usage: 'credits mint --days <n> [--label …] [--max <n>] [--expires <days>]',
|
|
348
|
+
summary:
|
|
349
|
+
'Admin: mint a code worth n days of Premium. The code is shown once. Also credits list|revoke <id>.',
|
|
350
|
+
options: ['--days <n>', '--label <text>', '--max <redemptions>', '--expires <days>', '--json'],
|
|
351
|
+
},
|
|
324
352
|
{
|
|
325
353
|
name: 'login',
|
|
326
354
|
usage: 'login [--api <url>] [--key <ndb_…>]',
|
|
@@ -847,6 +875,85 @@ export async function run(
|
|
|
847
875
|
for (const f of feeds) out(`${pad(f.slug, 30)} ${f.name}`);
|
|
848
876
|
return 0;
|
|
849
877
|
}
|
|
878
|
+
case 'webhooks':
|
|
879
|
+
case 'webhook': {
|
|
880
|
+
const [sub = 'list', arg] = rest;
|
|
881
|
+
const base = '/api/v1/webhooks';
|
|
882
|
+
const id = () => {
|
|
883
|
+
if (!arg) throw new Error(`webhooks ${sub} <id>`);
|
|
884
|
+
return `${base}/${encodeURIComponent(arg)}`;
|
|
885
|
+
};
|
|
886
|
+
const show = (r, text) => out(json ? JSON.stringify(r, null, 2) : text);
|
|
887
|
+
if (sub === 'list' || sub === 'ls') {
|
|
888
|
+
const r = await client.get(base);
|
|
889
|
+
if (json) {
|
|
890
|
+
show(r);
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
893
|
+
for (const e of r.endpoints)
|
|
894
|
+
out(
|
|
895
|
+
`${pad(e.id, 28)} ${pad(e.enabled ? 'on' : 'off', 4)} ${pad(e.events.join(','), 24)} ${e.url}`,
|
|
896
|
+
);
|
|
897
|
+
if (r.endpoints.length === 0) out('(no webhooks)');
|
|
898
|
+
out(`\nevents: ${r.event_types.join(', ')}`);
|
|
899
|
+
return 0;
|
|
900
|
+
}
|
|
901
|
+
if (sub === 'add' || sub === 'create') {
|
|
902
|
+
if (!arg) throw new Error('webhooks add <url> [--events a,b]');
|
|
903
|
+
const r = await client.post(base, {
|
|
904
|
+
url: arg,
|
|
905
|
+
events: csv(flags.events),
|
|
906
|
+
description: typeof flags.description === 'string' ? flags.description : '',
|
|
907
|
+
});
|
|
908
|
+
show(
|
|
909
|
+
r,
|
|
910
|
+
`${r.endpoint.id} ${r.endpoint.url}\nsecret: ${r.secret}\n(also: nichedb webhooks secret ${r.endpoint.id})`,
|
|
911
|
+
);
|
|
912
|
+
return 0;
|
|
913
|
+
}
|
|
914
|
+
if (sub === 'rm' || sub === 'delete') {
|
|
915
|
+
show(await client.del(id()), `Removed ${arg}.`);
|
|
916
|
+
return 0;
|
|
917
|
+
}
|
|
918
|
+
if (sub === 'test') {
|
|
919
|
+
const r = await client.post(`${id()}/test`, {});
|
|
920
|
+
show(
|
|
921
|
+
r,
|
|
922
|
+
r.ok ? `Delivered (HTTP ${r.status}).` : `Failed: ${r.error ?? `HTTP ${r.status}`}`,
|
|
923
|
+
);
|
|
924
|
+
return r.ok ? 0 : 1;
|
|
925
|
+
}
|
|
926
|
+
if (sub === 'secret') {
|
|
927
|
+
const r = await client.get(`${id()}/secret`);
|
|
928
|
+
show(r, r.secret);
|
|
929
|
+
return 0;
|
|
930
|
+
}
|
|
931
|
+
if (sub === 'rotate') {
|
|
932
|
+
const r = await client.post(`${id()}/rotate`, {});
|
|
933
|
+
show(r, `${r.secret}\n(the old secret has stopped working)`);
|
|
934
|
+
return 0;
|
|
935
|
+
}
|
|
936
|
+
if (sub === 'enable' || sub === 'disable') {
|
|
937
|
+
const r = await client.patch(id(), { enabled: sub === 'enable' });
|
|
938
|
+
show(r, `${r.id} ${r.enabled ? 'on' : 'off'}`);
|
|
939
|
+
return 0;
|
|
940
|
+
}
|
|
941
|
+
if (sub === 'deliveries') {
|
|
942
|
+
const limit = flags.limit ? `?limit=${Number(flags.limit)}` : '';
|
|
943
|
+
const r = await client.get(`${arg ? `${id()}/deliveries` : `${base}/deliveries`}${limit}`);
|
|
944
|
+
if (json) {
|
|
945
|
+
show(r);
|
|
946
|
+
return 0;
|
|
947
|
+
}
|
|
948
|
+
for (const d of r.deliveries)
|
|
949
|
+
out(
|
|
950
|
+
`${pad(d.id, 28)} ${pad(d.status, 9)} ${pad(d.last_status ?? '-', 4)} ${pad(d.event_type, 22)} ${when(d.created_at)} ${d.last_error ?? ''}`,
|
|
951
|
+
);
|
|
952
|
+
if (r.deliveries.length === 0) out('(no deliveries)');
|
|
953
|
+
return 0;
|
|
954
|
+
}
|
|
955
|
+
throw new Error('webhooks list|add|rm|test|secret|rotate|enable|disable|deliveries');
|
|
956
|
+
}
|
|
850
957
|
case 'rss': {
|
|
851
958
|
const url = `${client.base}/f/${rest[0]}.rss`;
|
|
852
959
|
if (!flags.fetch) {
|
|
@@ -1281,6 +1388,71 @@ export async function run(
|
|
|
1281
1388
|
out(await client.text(`/api/v1/profiles/${encodeURIComponent(sub)}/openprofile.md`));
|
|
1282
1389
|
return 0;
|
|
1283
1390
|
}
|
|
1391
|
+
case 'redeem': {
|
|
1392
|
+
const code = rest.join(' ').trim();
|
|
1393
|
+
if (!code) throw new Error('redeem <code>');
|
|
1394
|
+
const r = await client.post('/api/v1/credits/redeem', { code });
|
|
1395
|
+
out(json ? JSON.stringify(r, null, 2) : `Redeemed: ${r.days} days of Premium added.`);
|
|
1396
|
+
return 0;
|
|
1397
|
+
}
|
|
1398
|
+
case 'invites': {
|
|
1399
|
+
const [sub, arg] = rest;
|
|
1400
|
+
if (sub === 'create') {
|
|
1401
|
+
const r = await client.post('/api/v1/invites', { note: flags.note ?? '' });
|
|
1402
|
+
out(json ? JSON.stringify(r.invite, null, 2) : `${r.invite.url}\n(shown once)`);
|
|
1403
|
+
return 0;
|
|
1404
|
+
}
|
|
1405
|
+
if (sub === 'revoke') {
|
|
1406
|
+
if (!arg) throw new Error('invites revoke <id>');
|
|
1407
|
+
await client.post(`/api/v1/invites/${encodeURIComponent(arg)}/revoke`, {});
|
|
1408
|
+
out(`Withdrew ${arg}.`);
|
|
1409
|
+
return 0;
|
|
1410
|
+
}
|
|
1411
|
+
const r = await client.get('/api/v1/invites');
|
|
1412
|
+
if (json) {
|
|
1413
|
+
out(JSON.stringify(r, null, 2));
|
|
1414
|
+
return 0;
|
|
1415
|
+
}
|
|
1416
|
+
out(
|
|
1417
|
+
`${r.quota.left} of ${r.quota.total} invites left · ${r.bonus_days} days of Premium each when one is used`,
|
|
1418
|
+
);
|
|
1419
|
+
for (const i of r.invites) out(`${pad(i.id, 18)} ${pad(i.status, 8)} ${i.note}`);
|
|
1420
|
+
return 0;
|
|
1421
|
+
}
|
|
1422
|
+
case 'credits': {
|
|
1423
|
+
const [sub, arg] = rest;
|
|
1424
|
+
if (sub === 'mint') {
|
|
1425
|
+
const r = await client.post('/api/v1/admin/credits', {
|
|
1426
|
+
days: Number(flags.days),
|
|
1427
|
+
label: flags.label ?? '',
|
|
1428
|
+
max: flags.max ? Number(flags.max) : 1,
|
|
1429
|
+
expires_days: flags.expires ? Number(flags.expires) : null,
|
|
1430
|
+
});
|
|
1431
|
+
out(
|
|
1432
|
+
json
|
|
1433
|
+
? JSON.stringify(r, null, 2)
|
|
1434
|
+
: `${r.code}\n${r.days} days of Premium · ${r.max_redemptions} use(s) · id ${r.id}`,
|
|
1435
|
+
);
|
|
1436
|
+
return 0;
|
|
1437
|
+
}
|
|
1438
|
+
if (sub === 'revoke') {
|
|
1439
|
+
if (!arg) throw new Error('credits revoke <id>');
|
|
1440
|
+
await client.post(`/api/v1/admin/credits/${encodeURIComponent(arg)}/revoke`, {});
|
|
1441
|
+
out(`Revoked ${arg}.`);
|
|
1442
|
+
return 0;
|
|
1443
|
+
}
|
|
1444
|
+
if (sub && sub !== 'list') throw new Error('credits mint|list|revoke <id>');
|
|
1445
|
+
const { codes } = await client.get('/api/v1/admin/credits');
|
|
1446
|
+
if (json) {
|
|
1447
|
+
out(JSON.stringify(codes, null, 2));
|
|
1448
|
+
return 0;
|
|
1449
|
+
}
|
|
1450
|
+
for (const c of codes)
|
|
1451
|
+
out(
|
|
1452
|
+
`${pad(c.id, 18)} ${pad(`${c.days}d`, 6)} ${pad(`${c.redemptions}/${c.max_redemptions}`, 8)} ${c.revoked_at ? 'revoked ' : ''}${c.label}`,
|
|
1453
|
+
);
|
|
1454
|
+
return 0;
|
|
1455
|
+
}
|
|
1284
1456
|
case 'mcp':
|
|
1285
1457
|
return serveMcp({ client, stdin, stdout });
|
|
1286
1458
|
default:
|