|
| 1 | +import { AppsStorageManager } from './AppsStorageManager.js' |
| 2 | +import { AdmissionMode, LookupFormula, LookupQuestion, LookupService, OutputAdmittedByTopic, OutputSpent, SpendNotificationMode } from '@bsv/overlay' |
| 3 | +import { PushDrop, Utils } from '@bsv/sdk' |
| 4 | +import { Db } from 'mongodb' |
| 5 | +import { AppCatalogQuery, PublishedAppMetadata } from './types.js' |
| 6 | + |
| 7 | +class AppsLookupService implements LookupService { |
| 8 | + readonly admissionMode: AdmissionMode = 'locking-script' |
| 9 | + readonly spendNotificationMode: SpendNotificationMode = 'none' |
| 10 | + |
| 11 | + private static readonly TOPIC = 'tm_apps' |
| 12 | + private static readonly SERVICE_ID = 'ls_apps' |
| 13 | + |
| 14 | + constructor(public storageManager: AppsStorageManager) { } |
| 15 | + |
| 16 | + async outputAdmittedByTopic(payload: OutputAdmittedByTopic): Promise<void> { |
| 17 | + if (payload.mode !== 'locking-script') throw new Error('Invalid payload') |
| 18 | + const { txid, outputIndex, topic, lockingScript } = payload |
| 19 | + if (topic !== AppsLookupService.TOPIC) return |
| 20 | + |
| 21 | + const decoded = PushDrop.decode(lockingScript) |
| 22 | + if (decoded.fields.length !== 2) throw new Error('App token must have exactly one metadata field + signature') |
| 23 | + |
| 24 | + const metadataJSON = Utils.toUTF8(decoded.fields[0]) |
| 25 | + let metadata: PublishedAppMetadata |
| 26 | + try { |
| 27 | + metadata = JSON.parse(metadataJSON) |
| 28 | + } catch { |
| 29 | + throw new Error('Metadata field is not valid JSON') |
| 30 | + } |
| 31 | + if (metadata == null) throw new Error('App token must contain valid metadata') |
| 32 | + |
| 33 | + await this.storageManager.storeRecord(txid, outputIndex, metadata) |
| 34 | + } |
| 35 | + |
| 36 | + async outputSpent(payload: OutputSpent): Promise<void> { |
| 37 | + if (payload.mode !== 'none') throw new Error('Invalid payload') |
| 38 | + const { topic, txid, outputIndex } = payload |
| 39 | + if (topic !== AppsLookupService.TOPIC) return |
| 40 | + await this.storageManager.deleteRecord(txid, outputIndex) |
| 41 | + } |
| 42 | + |
| 43 | + async outputEvicted(txid: string, outputIndex: number): Promise<void> { |
| 44 | + await this.storageManager.deleteRecord(txid, outputIndex) |
| 45 | + } |
| 46 | + |
| 47 | + async lookup(question: LookupQuestion): Promise<LookupFormula> { |
| 48 | + if (question.query === undefined || question.query === null) throw new Error('A valid query must be provided!') |
| 49 | + if (question.service !== AppsLookupService.SERVICE_ID) throw new Error('Lookup service not supported!') |
| 50 | + |
| 51 | + const query = (question.query as AppCatalogQuery) |
| 52 | + |
| 53 | + if (query.domain) return await this.storageManager.findByDomain(query.domain, query.limit, query.skip, query.sortOrder) |
| 54 | + if (query.publisher) return await this.storageManager.findByPublisher(query.publisher, query.limit, query.skip, query.sortOrder) |
| 55 | + if (query.tags?.length) return await this.storageManager.findByTags(query.tags, query.limit, query.skip, query.sortOrder) |
| 56 | + if (query.category) return await this.storageManager.findByCategory(query.category, query.limit, query.skip, query.sortOrder) |
| 57 | + if (query.name) return await this.storageManager.findByNameFuzzy(query.name, query.limit, query.skip, query.sortOrder) |
| 58 | + if (query.outpoint) return await this.storageManager.findByOutpoint(query.outpoint) |
| 59 | + |
| 60 | + return await this.storageManager.findAllApps(query.limit, query.skip, query.sortOrder) |
| 61 | + } |
| 62 | + |
| 63 | + async getDocumentation(): Promise<string> { |
| 64 | + return 'Apps Lookup Service: find published Metanet Apps.' |
| 65 | + } |
| 66 | + |
| 67 | + async getMetaData(): Promise<{ |
| 68 | + name: string |
| 69 | + shortDescription: string |
| 70 | + iconURL?: string |
| 71 | + version?: string |
| 72 | + informationURL?: string |
| 73 | + }> { |
| 74 | + return { |
| 75 | + name: 'Apps Lookup Service', |
| 76 | + shortDescription: 'Find published Metanet Apps with ease.' |
| 77 | + } |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +export default (db: Db): AppsLookupService => new AppsLookupService(new AppsStorageManager(db)) |
0 commit comments