| … | … | @@ -35,16 +35,51 @@ export class HyperOrgs { |
| 35 | 35 | */ |
| 36 | 36 | constructor (corestore, options = {}) { |
| 37 | 37 | this._corestore = corestore.namespace(options.namespace || 'orgs') |
| 38 | | // name -> { room: Room, role: string, joined: boolean } |
| 38 | // name -> { room: Room, role: string, created: boolean } |
| 39 | 39 | this._orgs = new Map() |
| 40 | // Local append-only index (NOT replicated): which orgs this instance |
| 41 | // tracks + how to re-open each Room on restart. {op,name,bootstrapKey, |
| 42 | // role,created}. Last-write-wins per name; op:'untrack' drops. |
| 43 | this._index = null |
| 40 | 44 | this._opened = false |
| 41 | 45 | } |
| 42 | 46 | |
| 43 | 47 | async ready () { |
| 48 | if (this._opened) return |
| 49 | this._index = this._corestore.namespace('orgs-index').get({ name: 'log', valueEncoding: 'json' }) |
| 50 | await this._index.ready() |
| 51 | // Replay the index → re-open tracked org Rooms. |
| 52 | const tracked = new Map() |
| 53 | for (let i = 0; i < this._index.length; i++) { |
| 54 | const e = await this._index.get(i) |
| 55 | if (!e || !e.name) continue |
| 56 | if (e.op === 'untrack') tracked.delete(e.name) |
| 57 | else tracked.set(e.name, e) |
| 58 | } |
| 59 | for (const e of tracked.values()) { |
| 60 | await this._openOrgRoom(e.name, e.bootstrapKey, { created: !!e.created, role: e.role || 'reader' }) |
| 61 | } |
| 44 | 62 | this._opened = true |
| 45 | | // TODO(daemon): re-open known org Rooms from a local persistence |
| 46 | | // index here so orgs survive a restart. In-process callers create |
| 47 | | // or join within one instance lifetime, so the Map suffices. |
| 63 | } |
| 64 | |
| 65 | // Open (or re-open) an org's Room. Created orgs use namespace |
| 66 | // 'org/<name>' (creator's local writer); joined orgs use |
| 67 | // 'org-join/<key16>'. The namespace must match the original so the |
| 68 | // local writer identity is stable across restarts. |
| 69 | async _openOrgRoom (name, bootstrapKey, { created, role }) { |
| 70 | const namespace = created ? 'org/' + name : 'org-join/' + bootstrapKey.slice(0, 16) |
| 71 | const room = new Room(this._corestore, { schema: orgsSchema, namespace, bootstrapKey }) |
| 72 | await room.ready() |
| 73 | this._orgs.set(name, { room, role, created }) |
| 74 | return room |
| 75 | } |
| 76 | |
| 77 | async _track (name, bootstrapKey, role, created) { |
| 78 | if (this._index) await this._index.append({ op: 'track', name, bootstrapKey, role, created }) |
| 79 | } |
| 80 | |
| 81 | async _untrack (name) { |
| 82 | if (this._index) await this._index.append({ op: 'untrack', name }) |
| 48 | 83 | } |
| 49 | 84 | |
| 50 | 85 | async sync () { |
| … | … | @@ -104,7 +139,7 @@ export class HyperOrgs { |
| 104 | 139 | bootstrapKey: null |
| 105 | 140 | }) |
| 106 | 141 | await room.ready() |
| 107 | | this._orgs.set(name, { room, role: 'admin', joined: false }) |
| 142 | this._orgs.set(name, { room, role: 'admin', created: true }) |
| 108 | 143 | |
| 109 | 144 | await room.append({ |
| 110 | 145 | type: 'create-org', |
| … | … | @@ -119,6 +154,7 @@ export class HyperOrgs { |
| 119 | 154 | timestamp: Date.now() |
| 120 | 155 | }) |
| 121 | 156 | await room.base.update() |
| 157 | await this._track(name, room.keyHex, 'admin', true) |
| 122 | 158 | return this.get(name) |
| 123 | 159 | } |
| 124 | 160 | |
| … | … | @@ -159,6 +195,7 @@ export class HyperOrgs { |
| 159 | 195 | const e = this._orgs.get(name) |
| 160 | 196 | if (e) { try { await e.room.close() } catch {} } |
| 161 | 197 | this._orgs.delete(name) |
| 198 | await this._untrack(name) |
| 162 | 199 | } |
| 163 | 200 | |
| 164 | 201 | /** Orgs this instance tracks (created or joined). */ |
| … | … | @@ -230,7 +267,8 @@ export class HyperOrgs { |
| 230 | 267 | }) |
| 231 | 268 | await room.ready() |
| 232 | 269 | await room.base.update() |
| 233 | | this._orgs.set(name, { room, role, joined: true }) |
| 270 | this._orgs.set(name, { room, role, created: false }) |
| 271 | await this._track(name, decoded.bootstrapKey, role, false) |
| 234 | 272 | return { name, writerKey: room.localKeyHex, role } |
| 235 | 273 | } |
| 236 | 274 | |