@1sat/cli 0.0.119 → 0.0.121
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/dist/cli.js +689 -61
- package/package.json +5 -4
- package/src/commands/permissions.ts +269 -0
- package/src/commands/serve.ts +25 -62
- package/src/config.ts +5 -4
- package/src/context.ts +21 -1
- package/src/help.ts +82 -1
- package/src/main.ts +5 -0
- package/src/wallet-api/admin.ts +107 -0
- package/src/wallet-api/deny.ts +194 -0
- package/src/wallet-api/endpoint.ts +108 -0
- package/src/wallet-api/grants.ts +238 -0
- package/src/wallet-api/permission-store.ts +106 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-backed `IPermissionStore` for `1sat serve wallet-api`.
|
|
3
|
+
*
|
|
4
|
+
* One JSON document per wallet (`<dataDir>/permissions-<chain>.json`, mode
|
|
5
|
+
* 0600) keyed by the canonical permission key string, mirroring the
|
|
6
|
+
* IndexedDB store in `@1sat/wallet-browser`: `putGrant` upserts by key,
|
|
7
|
+
* `findGrant` is an exact-key lookup, `listGrants` filters by originator
|
|
8
|
+
* and/or type, and `deleteAllForOriginator` returns how many grants it
|
|
9
|
+
* removed. Every operation reads the file fresh and writes it back
|
|
10
|
+
* atomically (temp file + rename), so several instances over the same path
|
|
11
|
+
* observe each other's writes.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
existsSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
readFileSync,
|
|
18
|
+
renameSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
} from 'node:fs'
|
|
21
|
+
import { dirname, join } from 'node:path'
|
|
22
|
+
import {
|
|
23
|
+
type IPermissionStore,
|
|
24
|
+
type ListGrantsFilter,
|
|
25
|
+
type PermissionKey,
|
|
26
|
+
type StoredGrant,
|
|
27
|
+
permissionKeyToString,
|
|
28
|
+
} from '@1sat/wallet'
|
|
29
|
+
|
|
30
|
+
const FILE_VERSION = 1
|
|
31
|
+
|
|
32
|
+
interface PermissionFile {
|
|
33
|
+
version: number
|
|
34
|
+
grants: Record<string, StoredGrant>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Where the CLI keeps wallet-api grants for a chain. */
|
|
38
|
+
export function permissionStorePath(dataDir: string, chain: string): string {
|
|
39
|
+
return join(dataDir, `permissions-${chain}.json`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class FilePermissionStore implements IPermissionStore {
|
|
43
|
+
constructor(private readonly path: string) {}
|
|
44
|
+
|
|
45
|
+
async findGrant(key: PermissionKey): Promise<StoredGrant | null> {
|
|
46
|
+
return this.load().grants[permissionKeyToString(key)] ?? null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async putGrant(grant: StoredGrant): Promise<void> {
|
|
50
|
+
const file = this.load()
|
|
51
|
+
file.grants[permissionKeyToString(grant.key)] = grant
|
|
52
|
+
this.save(file)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async deleteGrant(key: PermissionKey): Promise<void> {
|
|
56
|
+
const file = this.load()
|
|
57
|
+
const id = permissionKeyToString(key)
|
|
58
|
+
if (!(id in file.grants)) return
|
|
59
|
+
delete file.grants[id]
|
|
60
|
+
this.save(file)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async deleteAllForOriginator(originator: string): Promise<number> {
|
|
64
|
+
const file = this.load()
|
|
65
|
+
let removed = 0
|
|
66
|
+
for (const [id, grant] of Object.entries(file.grants)) {
|
|
67
|
+
if (grant.key.originator === originator) {
|
|
68
|
+
delete file.grants[id]
|
|
69
|
+
removed++
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (removed > 0) this.save(file)
|
|
73
|
+
return removed
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async listGrants(filter?: ListGrantsFilter): Promise<StoredGrant[]> {
|
|
77
|
+
const out: StoredGrant[] = []
|
|
78
|
+
for (const grant of Object.values(this.load().grants)) {
|
|
79
|
+
if (filter?.originator && grant.key.originator !== filter.originator)
|
|
80
|
+
continue
|
|
81
|
+
if (filter?.type && grant.key.type !== filter.type) continue
|
|
82
|
+
out.push(grant)
|
|
83
|
+
}
|
|
84
|
+
return out
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private load(): PermissionFile {
|
|
88
|
+
if (!existsSync(this.path)) return { version: FILE_VERSION, grants: {} }
|
|
89
|
+
const raw = readFileSync(this.path, 'utf8')
|
|
90
|
+
const parsed = JSON.parse(raw) as Partial<PermissionFile>
|
|
91
|
+
if (parsed.version !== FILE_VERSION) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Unsupported permission store version ${String(parsed.version)} in ${this.path}`,
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
return { version: FILE_VERSION, grants: parsed.grants ?? {} }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private save(file: PermissionFile): void {
|
|
100
|
+
const dir = dirname(this.path)
|
|
101
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 })
|
|
102
|
+
const tmp = `${this.path}.${process.pid}.tmp`
|
|
103
|
+
writeFileSync(tmp, `${JSON.stringify(file, null, 2)}\n`, { mode: 0o600 })
|
|
104
|
+
renameSync(tmp, this.path)
|
|
105
|
+
}
|
|
106
|
+
}
|