From 29e868bd22d5fe6712ea6d52327236b61b3c78bc Mon Sep 17 00:00:00 2001 From: balzack Date: Thu, 28 Nov 2024 09:55:44 -0800 Subject: [PATCH] adding media files interface --- app/client/web/src/MediaFiles.ts | 12 +++++++ .../web/src/context/useAppContext.hook.ts | 3 +- app/sdk/src/contact.ts | 9 +++-- app/sdk/src/files.ts | 4 +++ app/sdk/src/focus.ts | 36 ++++++++++++++++++- app/sdk/src/index.ts | 16 +++++---- app/sdk/src/items.ts | 6 ++-- app/sdk/src/session.ts | 9 +++-- app/sdk/src/stream.ts | 9 +++-- app/sdk/src/types.ts | 2 +- 10 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 app/client/web/src/MediaFiles.ts create mode 100644 app/sdk/src/files.ts diff --git a/app/client/web/src/MediaFiles.ts b/app/client/web/src/MediaFiles.ts new file mode 100644 index 00000000..247966c5 --- /dev/null +++ b/app/client/web/src/MediaFiles.ts @@ -0,0 +1,12 @@ +import { Files } from 'databag-client-sdk' + +export class MediaFiles implements Files { + public async read(source: any): Promise<{ size: number, getData: (position: number, length: number)=>Promise }> { + return { size: 0, getData: async (position: number, length: number)=>('') }; + } + + public async write(): Promise<{ setData: (data: string)=>Promise, getPath: ()=>Promise }> { + return { setData: async (data: string)=>{}, getPath: async ()=>('') }; + } +} + diff --git a/app/client/web/src/context/useAppContext.hook.ts b/app/client/web/src/context/useAppContext.hook.ts index 0cbea085..993bafef 100644 --- a/app/client/web/src/context/useAppContext.hook.ts +++ b/app/client/web/src/context/useAppContext.hook.ts @@ -2,8 +2,9 @@ import { useState, useEffect, useRef } from 'react' import { DatabagSDK, Session, Focus } from 'databag-client-sdk' import { SessionStore } from '../SessionStore' import { WebCrypto } from '../WebCrypto' +import { MediaFiles } from '../MediaFiles' -const databag = new DatabagSDK({ tagBatch: 32, topicBatch: 32, articleTypes: [], channelTypes: ['sealed', 'superbasic'] }, new WebCrypto()) +const databag = new DatabagSDK({ tagBatch: 32, topicBatch: 32, articleTypes: [], channelTypes: ['sealed', 'superbasic'] }, new WebCrypto(), new MediaFiles()) export function useAppContext() { const sdk = useRef(databag) diff --git a/app/sdk/src/contact.ts b/app/sdk/src/contact.ts index d09c0e13..407f379b 100644 --- a/app/sdk/src/contact.ts +++ b/app/sdk/src/contact.ts @@ -8,6 +8,7 @@ import type { ArticleDetail, ChannelSummary, ChannelDetail, CardProfile, CardDet import { defaultCardItem, defaultChannelItem } from './items'; import { Store } from './store'; import { Crypto } from './crypto'; +import { Files } from './files'; import { getCards } from './net/getCards'; import { getCardProfile } from './net/getCardProfile'; import { getCardDetail } from './net/getCardDetail'; @@ -51,6 +52,7 @@ export class ContactModule implements Contact { private focus: FocusModule | null; private crypto: Crypto | null; + private files: Files | null; private store: Store; private revision: number; private nextRevision: number | null; @@ -72,7 +74,7 @@ export class ContactModule implements Contact { // view of channels private channelEntries: Map>; - constructor(log: Logging, store: Store, crypto: Crypto | null, guid: string, token: string, node: string, secure: boolean, articleTypes: string[], channelTypes: string[]) { + constructor(log: Logging, store: Store, crypto: Crypto | null, files: Files | null, guid: string, token: string, node: string, secure: boolean, articleTypes: string[], channelTypes: string[]) { this.guid = guid; this.token = token; this.node = node; @@ -80,6 +82,7 @@ export class ContactModule implements Contact { this.log = log; this.store = store; this.crypto = crypto; + this.files = files; this.emitter = new EventEmitter(); this.articleTypes = articleTypes; this.channelTypes = channelTypes; @@ -618,9 +621,9 @@ export class ContactModule implements Contact { const channelKey = channelEntry.item.channelKey; const sealEnabled = Boolean(this.seal); const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(node); - this.focus = new FocusModule(this.log, this.store, this.crypto, cardId, channelId, this.guid, { node, secure: !insecure, token: `${guid}.${token}` }, channelKey, sealEnabled, revision); + this.focus = new FocusModule(this.log, this.store, this.crypto, this.files, cardId, channelId, this.guid, { node, secure: !insecure, token: `${guid}.${token}` }, channelKey, sealEnabled, revision); } else { - this.focus = new FocusModule(this.log, this.store, this.crypto, cardId, channelId, this.guid, null, null, false, 0); + this.focus = new FocusModule(this.log, this.store, this.crypto, this.files, cardId, channelId, this.guid, null, null, false, 0); } return this.focus; } diff --git a/app/sdk/src/files.ts b/app/sdk/src/files.ts new file mode 100644 index 00000000..ec081e06 --- /dev/null +++ b/app/sdk/src/files.ts @@ -0,0 +1,4 @@ +export interface Files { + read(source: any): Promise<{ size: number, getData: (position: number, length: number)=>Promise }>; + write(): Promise<{ setData: (data: string)=>Promise, getPath: ()=>Promise }>; +} diff --git a/app/sdk/src/focus.ts b/app/sdk/src/focus.ts index eaa1b8f5..16b293ea 100644 --- a/app/sdk/src/focus.ts +++ b/app/sdk/src/focus.ts @@ -5,6 +5,8 @@ import type { Topic, Asset, AssetSource, Participant } from './types'; import type { Logging } from './logging'; import { Store } from './store'; import { Crypto } from './crypto'; +import { Files } from './files'; +import { Files } from './files'; import { HostingMode } from './types'; import { defaultTopicItem } from './items'; import { getChannelTopics } from './net/getChannelTopics'; @@ -27,6 +29,7 @@ export class FocusModule implements Focus { private log: Logging; private emitter: EventEmitter; private crypto: Crypto | null; + private files: Files | null; private store: Store; private guid: string; private connection: { node: string; secure: boolean; token: string } | null; @@ -46,13 +49,14 @@ export class FocusModule implements Focus { // view of topics private topicEntries: Map; - constructor(log: Logging, store: Store, crypto: Crypto | null, cardId: string | null, channelId: string, guid: string, connection: { node: string; secure: boolean; token: string } | null, channelKey: string, sealEnabled: boolean, revision: number) { + constructor(log: Logging, store: Store, crypto: Crypto | null, files: Files | null, cardId: string | null, channelId: string, guid: string, connection: { node: string; secure: boolean; token: string } | null, channelKey: string, sealEnabled: boolean, revision: number) { this.cardId = cardId; this.channelId = channelId; this.log = log; this.emitter = new EventEmitter(); this.store = store; this.crypto = crypto; + this.files = files; this.guid = guid; this.connection = connection; this.channelKey = channelKey; @@ -293,6 +297,8 @@ export class FocusModule implements Focus { // encrypt // add confirmed topic + + if (files.length == 0) { if (sealed) { const decrypted = subject([]); @@ -311,6 +317,34 @@ export class FocusModule implements Focus { } } else { const topicId = await this.addRemoteChannelTopic(type, {}, false); + if (sealed) { + const { sealEnabled, channelKey, crypto } = this; + if (!sealEnabled || !channelKey || !crypto) { + throw new Error('encryption not set'); + } + const assetContext = [] as { assetId: string, context: string }[]; + const assetItems = [] as AssetItem[]; + for (const asset of assets) { + for (const transform of asset.transforms) { + if (transform.type === TransformType.Thumb && transform.thumb) { + const assetItem = { + assetId: `${assetList.size}`, + mimeType: 'image', + encrytped: true, + hosting: HostingMode.inline, + inline: await transform.thumb(), + } + const { assetId, context } = assetItem; + assetContext.push({ assetId, context }); + assetItems.push(assetItem); + } else if (transform.type === TransformType.Copy) { + } else { + throw new Error('transform not supported') + } + } + } + } else { + } for (const asset of files) { const upload = await this.uploadFile(asset.source, ['ithumb;photo', 'ilg;photo'], topicId, (progress: number) => { diff --git a/app/sdk/src/index.ts b/app/sdk/src/index.ts index 4f89146a..dd9f71b4 100644 --- a/app/sdk/src/index.ts +++ b/app/sdk/src/index.ts @@ -15,23 +15,27 @@ import type { Session, Node, Contributor } from './api'; import type { Params, SessionParams } from './types'; import type { Login } from './entities'; import type { Crypto } from './crypto'; +import type { Files } from './files'; import type { WebStore, SqlStore } from './store'; export * from './api'; export * from './types'; export { WebStore, SqlStore } from './store'; export { Crypto } from './crypto'; +export { Files } from './files'; export class DatabagSDK { private log: Logging; private crypto: Crypto | null; + private files: Files | null; private store: Store; private params: Params; - constructor(params: Params, crypto?: Crypto, log?: Logging) { + constructor(params: Params, crypto?: Crypto, files?: Files, log?: Logging) { this.store = new NoStore(); this.params = params; this.crypto = crypto ? crypto : null; + this.files = files ? files : null; this.log = log ? log : new ConsoleLogging(); this.log.info('databag sdk'); } @@ -40,14 +44,14 @@ export class DatabagSDK { const { articleTypes, channelTypes } = this.params; this.store = new OfflineStore(this.log, sql); const login = await this.store.init(); - return login ? new SessionModule(this.store, this.crypto, this.log, login.guid, login.token, login.node, login.secure, login.timestamp, articleTypes, channelTypes) : null; + return login ? new SessionModule(this.store, this.crypto, this.log, this.files, login.guid, login.token, login.node, login.secure, login.timestamp, articleTypes, channelTypes) : null; } public async initOnlineStore(web: WebStore): Promise { const { articleTypes, channelTypes } = this.params; this.store = new OnlineStore(this.log, web); const login = await this.store.init(); - return login ? new SessionModule(this.store, this.crypto, this.log, login.guid, login.token, login.node, login.secure, login.timestamp, articleTypes, channelTypes) : null; + return login ? new SessionModule(this.store, this.crypto, this.log, this.files, login.guid, login.token, login.node, login.secure, login.timestamp, articleTypes, channelTypes) : null; } public async available(node: string, secure: boolean): Promise { @@ -71,7 +75,7 @@ export class DatabagSDK { pushSupported, }; await this.store.setLogin(login); - return new SessionModule(this.store, this.crypto, this.log, guid, appToken, node, secure, created, articleTypes, channelTypes); + return new SessionModule(this.store, this.crypto, this.log, this.files, guid, appToken, node, secure, created, articleTypes, channelTypes); } public async access(node: string, secure: boolean, token: string, params: SessionParams): Promise { @@ -87,7 +91,7 @@ export class DatabagSDK { pushSupported, }; await this.store.setLogin(login); - return new SessionModule(this.store, this.crypto, this.log, guid, appToken, node, secure, created, articleTypes, channelTypes); + return new SessionModule(this.store, this.crypto, this.log, this.files, guid, appToken, node, secure, created, articleTypes, channelTypes); } public async create(handle: string, password: string, node: string, secure: boolean, token: string | null, params: SessionParams): Promise { @@ -104,7 +108,7 @@ export class DatabagSDK { pushSupported, }; await this.store.setLogin(login); - return new SessionModule(this.store, this.crypto, this.log, guid, appToken, node, secure, created, articleTypes, channelTypes); + return new SessionModule(this.store, this.crypto, this.log, this.files, guid, appToken, node, secure, created, articleTypes, channelTypes); } public async remove(session: Session): Promise { diff --git a/app/sdk/src/items.ts b/app/sdk/src/items.ts index fbdec6c9..66f9a891 100644 --- a/app/sdk/src/items.ts +++ b/app/sdk/src/items.ts @@ -171,11 +171,11 @@ export type TopicItem = { }; export type AssetItem = { - assetIndex: number; + assetId: string; mimeType: string; - encrypted: boolean; - hosting: string; extension: string; + encrypted: boolean; + hosting: HostingMode; split?: { blockId: string, blockIv: string }[]; basic?: string; inline?: string; diff --git a/app/sdk/src/session.ts b/app/sdk/src/session.ts index 5b2c4b7b..cee8c2a7 100644 --- a/app/sdk/src/session.ts +++ b/app/sdk/src/session.ts @@ -18,11 +18,13 @@ import { Call } from './types'; import { Store } from './store'; import type { Logging } from './logging'; import type { Crypto } from './crypto'; +import type { Files } from './files'; export class SessionModule implements Session { private emitter: EventEmitter; private store: Store; private crypto: Crypto | null; + private files: Files | null; private log: Logging; private guid: string; private token: string; @@ -42,11 +44,12 @@ export class SessionModule implements Session { private channelTypes: string[]; private articleTypes: string[]; - constructor(store: Store, crypto: Crypto | null, log: Logging, guid: string, token: string, node: string, secure: boolean, loginTimestamp: number, articleTypes: string[], channelTypes: string[]) { + constructor(store: Store, crypto: Crypto | null, log: Logging, files: Files | null, guid: string, token: string, node: string, secure: boolean, loginTimestamp: number, articleTypes: string[], channelTypes: string[]) { log.info('new databag session'); this.store = store; this.crypto = crypto; + this.files = files; this.log = log; this.guid = guid; this.token = token; @@ -60,10 +63,10 @@ export class SessionModule implements Session { this.identity = new IdentityModule(log, this.store, guid, token, node, secure); this.settings = new SettingsModule(log, this.store, this.crypto, guid, token, node, secure); - this.contact = new ContactModule(log, this.store, this.crypto, guid, token, node, secure, articleTypes, channelTypes); + this.contact = new ContactModule(log, this.store, this.crypto, this.files, guid, token, node, secure, articleTypes, channelTypes); this.alias = new AliasModule(log, this.settings, this.store, guid, token, node, secure); this.attribute = new AttributeModule(log, this.settings, this.store, guid, token, node, secure); - this.stream = new StreamModule(log, this.store, this.crypto, guid, token, node, secure, channelTypes); + this.stream = new StreamModule(log, this.store, this.crypto, this.files, guid, token, node, secure, channelTypes); this.content = new ContentModule(log, this.crypto, this.contact, this.stream); this.ring = new RingModule(log); this.connection = new Connection(log, token, node, secure); diff --git a/app/sdk/src/stream.ts b/app/sdk/src/stream.ts index 3be7fbf6..a9723b55 100644 --- a/app/sdk/src/stream.ts +++ b/app/sdk/src/stream.ts @@ -6,6 +6,7 @@ import type { ChannelItem } from './items'; import type { Channel, Topic, Asset, Tag, Participant } from './types'; import { Store } from './store'; import { Crypto } from './crypto'; +import { Files } from './files'; import { addChannel } from './net/addChannel'; import { removeChannel } from './net/removeChannel'; import { getChannels } from './net/getChannels'; @@ -26,6 +27,7 @@ export class StreamModule { private log: Logging; private store: Store; private crypto: Crypto | null; + private files: Files | null; private guid: string; private token: string; private node: string; @@ -44,7 +46,7 @@ export class StreamModule { // view of channels private channelEntries: Map; - constructor(log: Logging, store: Store, crypto: Crypto | null, guid: string, token: string, node: string, secure: boolean, channelTypes: string[]) { + constructor(log: Logging, store: Store, crypto: Crypto | null, files: Files | null, guid: string, token: string, node: string, secure: boolean, channelTypes: string[]) { this.guid = guid; this.token = token; this.node = node; @@ -52,6 +54,7 @@ export class StreamModule { this.log = log; this.store = store; this.crypto = crypto; + this.files = files; this.focus = null; this.seal = null; this.unsealAll = false; @@ -376,10 +379,10 @@ export class StreamModule { const channelKey = entry.item.channelKey; const sealEnabled = Boolean(this.seal); const revision = entry.item.summary.revision; - this.focus = new FocusModule(this.log, this.store, this.crypto, null, channelId, this.guid, { node, secure, token }, channelKey, sealEnabled, revision); + this.focus = new FocusModule(this.log, this.store, this.crypto, this.files, null, channelId, this.guid, { node, secure, token }, channelKey, sealEnabled, revision); } else { - this.focus = new FocusModule(this.log, this.store, this.crypto, cardId, channelId, this.guid, null, null, false, 0); + this.focus = new FocusModule(this.log, this.store, this.crypto, this.files, cardId, channelId, this.guid, null, null, false, 0); } return this.focus; diff --git a/app/sdk/src/types.ts b/app/sdk/src/types.ts index a048f830..08634367 100644 --- a/app/sdk/src/types.ts +++ b/app/sdk/src/types.ts @@ -128,7 +128,7 @@ export type AssetSource = { mimeType: string; extension: string; source: any; - transform: {type: TransformType, context: any, position?: number, thumb?: ()=>Promise}[], + transforms: {type: TransformType, context: any, position?: number, thumb?: ()=>Promise}[], } export type Asset = {