fixing unit tests

This commit is contained in:
balzack 2025-03-01 17:47:00 -08:00
parent 326794dbde
commit b5b6bc032c
19 changed files with 186 additions and 101 deletions

View File

@ -1,5 +1,6 @@
import { EventEmitter } from 'eventemitter3';
import type { Contact } from '../src/api';
import type { Contact, Link } from '../src/api';
import { MockLinkModule } from './link';
import type { Card, Channel, Article, Topic, Asset, Tag, Profile, Participant} from '../src/types';
export class MockContactModule implements Contact {
@ -20,6 +21,14 @@ export class MockContactModule implements Contact {
this.emitter.off('card', ev);
}
public addLoadedListener(ev: (loaded: boolean) => void): void {
this.emitter.on('loaded', ev);
}
public removeLoadedListener(ev: (loaded: boolean) => void): void {
this.emitter.off('loaded', ev);
}
public close(): void {
}
@ -216,5 +225,9 @@ export class MockContactModule implements Contact {
public removeChannelListener(ev: (arg: { cardId: string | null; channels: Channel[] }) => void): void {
}
public async callCard(cardId: string): Promise<Link> {
return new MockLinkModule();
}
}

34
app/sdk/__mocks__/link.ts Normal file
View File

@ -0,0 +1,34 @@
import { EventEmitter } from 'eventemitter3';
import { type Link } from '../src/api';
export class MockLinkModule implements Link {
private emitter: EventEmitter;
constructor() {
this.emitter = new EventEmitter();
}
public setStatusListener(ev: (status: string) => Promise<void>): void {
}
public clearStatusListener(): void {
}
public setMessageListener(ev: (message: any) => Promise<void>): void {
}
public clearMessageListener(): void {
}
public getIce(): { urls: string; username: string; credential: string }[] {
return [];
}
public async sendMessage(message: any): Promise<void> {
}
public async close(): Promise<void> {
}
}

View File

@ -1,46 +1,31 @@
import { EventEmitter } from 'eventemitter3';
import type { Ring } from '../src/api';
import type { Ring, Link } from '../src/api';
import type { Call } from '../src/types';
import { MockLinkModule } from './link';
export class MockRingModule implements Ring {
public call: Call | null;
private emitter: EventEmitter;
constructor() {
this.call = null;
this.emitter = new EventEmitter();
}
public addCallingListener(ev: (calls: Call[]) => void): void {
this.emitter.on('calling', ev);
public addRingingListener(ev: (calls: { cardId: string, callId: string }[]) => void): void {
}
public removeCallingListener(ev: (calls: Call[]) => void): void {
this.emitter.off('calling', ev);
}
public addCallListener(ev: (call: Call | null) => void): void {
this.emitter.on('call', ev);
}
public removeCallListener(ev: (call: Call | null) => void): void {
this.emitter.off('call', ev);
public removeRingingListener(ev: (calls: { cardId: string, callId: string }[]) => void): void {
}
public ring(call: Call): void {
this.call = call;
}
public accept(callId: string): void {
public async accept(cardId: string, callId: string, contactNode: string): Promise<Link> {
return new MockLinkModule();
}
public ignore(callId: string): void {
public async decline(cardId: string, callId: string, contactNode: string): Promise<void> {
}
public decline(callId: string): void {
}
public close(): void {
public async ignore(cardId: string, callId: string): Promise<void> {
}
}

View File

@ -1,6 +1,6 @@
import { EventEmitter } from 'eventemitter3';
import { type Settings } from '../src/api';
import type { Config } from '../src/types';
import { type Config } from '../src/types';
export class MockSettingsModule implements Settings {
@ -78,5 +78,17 @@ export class MockSettingsModule implements Settings {
public async setLogin(username: string, password: string): Promise<void> {
}
public async getBlockedCards(): Promise<{ cardId: string, timestamp: number}[]> {
return [];
}
public async getBlockedChannels(): Promise<{ cardId: string|null, channelId: string, timestamp: number }[]> {
return [];
}
public async getBlockedTopics(): Promise<{ cardId: string|null, channelId: string, topicId: string, timestamp: number }[]> {
return [];
}
}

View File

@ -26,6 +26,14 @@ export class MockStreamModule {
this.emitter.off('channel', ev);
}
public addLoadedListener(ev: (loaded: boolean) => void): void {
this.emitter.on('loaded', ev);
}
public removeLoadedListener(ev: (loaded: boolean) => void): void {
this.emitter.off('loaded', ev);
}
public async setBlockedChannel(channelId: string, blocked: boolean): Promise<void> {
}

View File

@ -89,7 +89,8 @@ jest.mock('../src/net/fetchUtil', () => {
return Promise.resolve({ status: 200, json: () => JSON.parse('{"keyType": "RSA2048", "message": "eyJndWlkIjoiMDAzNWQ2ZmZkMzQyMThhMTJiNmYzYzY3ZWQyYzIwZjRlZWUwNmRjMzZlZWQwMzcxNWJiZjQ2ZjA0MzY2NTExYyIsInRpbWVzdGFtcCI6MTcyOTkwMjg4NCwibWVzc2FnZVR5cGUiOiJpZGVudGl0eSIsInZhbHVlIjoie1wicmV2aXNpb25cIjoxLFwiaGFuZGxlXCI6XCIxMjN0dHR0cnJcIixcInZlcnNpb25cIjpcIjAuMS4wXCIsXCJub2RlXCI6XCJiYWx6YWNrLmNvcmVkYi5vcmdcIixcInNlYWxcIjpcIlwifSJ9", "publicKey": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBcmI1Zjd4a0NmODhlazhhQ05paVgKWlpJOCt3dlArSzZBUG85OWZCM3hBWWRGVi84djZzSTdCY3lnWXYxVC9QbzI2UmpPdEZjcVRRV1ZZZlBiTlZxVgpQQ1VSblJwWkVnQWVjSGdqUmNzSjF3cWJZQWZRSDhhRGR4aGE4enRRcS9ZVlVzRCtkRU5IamNYV0NCSW04eDVzCjZ2NXo1aS9BeFRyR2I4YlgybDAydUxaNmVIYjNuOHhUQkRMa2VPUzJBaFZWWW0zSVc3bjhrQUJnT2dQY1Y1QS8Ka1B2LzFWbnJ4OTd6Q2VNaHdYd3Y0SEpxTldGR3dvaUlTNzJXU2hCcWk5N2ZSRHlOSVJ4SVJrMDFRdlJYL3hMcQpuM3R0WWN1cWdYN0NoUGhyRHpwRVlKOUZKT0ZaQSt1TVF2K1NmZVg4YlQzcGEzV2hXNFFHZ3BMNXhuM0NuMTlkCkYwVG1laWVLSTUyR0lPejRLV1R2alJDZHc2YW5vTGJNWW0xUzM4emhpbHZ4YWlZWTZ4WXVpNXJneWU2bzJlakgKZGphV0kzeTRaZVJ1UnUxTEgzd1U2UGhzbGhDZnJrU05FVFIzRkd2RWRKNzRzdVFEVmtnakF2R3BqaC9FUlkyUApZYmE1L254WlJVRGF4bjFjcURzOU0wUk5pMHp0R2d3dVJPSE1WeHFNMEFONko2TnJlL3F0dTFGbm5SMVllMmQ5CnRHVDVLSXRaZUN2dzY0Y3U2SFhMZWFzcXg3b2kwbU5BT0RoQWUrMzlXbFN1VEE1YWNyR3YydFpUSldPY0I5TmcKNlpGb2hzY3ErbHdVZHM3ek1lVkdXU1VQWmtnZUxrTk9rT0ZlWVNuVk4wc2RaVHFQMllTU0hDU0hxS0dtaHlhMQpXRlNPMTBxaTZjQWhheldraWMyR0Zwa0NBd0VBQVE9PQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCg==", "signature": "gj5NKLzgF5HHWthu47ofuEhkhpOiP4CJ5QNG65VmuqL05Mu7dUef5Nxp6BacCIJoDb3GdYbHI/UBj0Ns4gBsMihOkwIMCav/P0FdvLYZQrpaNf6t6PUI2c4xW/w3gZ/5IrJiUmWE+PKYTjMjUlroc1gHAXIyGG2vs152HT2uMjB/kGKMU1nxvjABAN+khhw7h0iW3EBKffKRTeAsRjUw6YIXwmeYEM7MP8zrISkKquIScf4yxDM2iZWC0DJOvGa4XANqkLKLPNL11u7hBXt2Ovj++U5eQsYSXcn1IDyhwlgRyRzuNEayZJnpbCCyXybEIaty+bf0wdq5nVWi1E4ju4wY+Z1pV5lsXtuKyxA/GY4Zk3QMTwx4dz2tWPDQYa35VUeyxhm5U5iMWdFG+nJuhPT0IhajLWrrTzQA5xXCzb5Da/ae0FrS3w3opATKNKxDpl0P5gjCQ1Xbku7VoUYaQQ6JaTdxNV/eKNCmcDcCDnZoEsE0Mp1hcqf8nT6YVvIVJG5luhv5TWEmTLBgZsWfUreaUkz/DJV+0fLwfL6oZuRkEx3aZnU1BfWtsS1ecgVJ93Q73sGohnUN9EaeR4ruviMrb4x1lS1IHGFuKooGqChukuTbKxlBkeqABiMsVkvme846cpFWijv/CnK6yoDhJA9lHaugjI+KgapYZUOwJOg=", "signatureType": "PKCS1v15"}') });
}
else if (url === 'http://test_url/contact/cards?agent=test_token' && options.method === 'POST') {
return Promise.resolve({ status: 200, json: () => JSON.parse(options.body).message.substring(0,9) });
const id = JSON.parse(options.body).message.substring(0,9);
return Promise.resolve({ status: 200, json: () => ({ id }) });
}
else {
return Promise.resolve({ status: 200, json: () => [] });

View File

@ -52,7 +52,7 @@ const getChannel = (subject: string, message: string, revision: number) => {
lastTopic: {
guid: 'guid1',
dataType: 'test',
data: JSON.stringify({ message: message }),
data: JSON.stringify({ text: message }),
created: 2,
updated: 2,
status: 'ready',
@ -107,7 +107,7 @@ const getChannelSummary = (message: string) => {
lastTopic: {
guid: 'guid1',
dataType: 'test',
data: JSON.stringify({ message: message }),
data: JSON.stringify({ text: message }),
created: 2,
updated: 2,
status: 'ready',
@ -172,11 +172,11 @@ test('received contact updates', async () => {
await waitFor(() => cardChannels.get('C000A')?.length === 1);
await waitFor(() => cardChannels.get('C000A')?.[0].data.subject === 'test_subject_0');
await waitFor(() => cardChannels.get('C000A')?.[0].lastTopic.data.message === 'test_message_0');
await waitFor(() => cardChannels.get('C000A')?.[0].lastTopic.data.text === 'test_message_0');
await contact.setRevision(2);
await waitFor(() => cardChannels.get('C000A')?.[0].data.subject === 'test_subject_1');
await waitFor(() => cardChannels.get('C000A')?.[0].lastTopic.data.message === 'test_message_1');
await waitFor(() => cardChannels.get('C000A')?.[0].lastTopic.data.text === 'test_message_1');
await contact.setRevision(3);
await waitFor(() => cardChannels.get('C000A')?.length === 0);
@ -199,11 +199,11 @@ test('received stream updates', async () => {
await stream.setRevision(1);
await waitFor(() => streamChannels.get(null)?.length === 1);
await waitFor(() => streamChannels.get(null)?.[0].data.subject === 'test_subject_0');
await waitFor(() => streamChannels.get(null)?.[0].lastTopic.data.message === 'test_message_0');
await waitFor(() => streamChannels.get(null)?.[0].lastTopic.data.text === 'test_message_0');
await stream.setRevision(2);
await waitFor(() => streamChannels.get(null)?.[0].data.subject === 'test_subject_1');
await waitFor(() => streamChannels.get(null)?.[0].lastTopic.data.message === 'test_message_1');
await waitFor(() => streamChannels.get(null)?.[0].lastTopic.data.text === 'test_message_1');
await stream.setRevision(3);
await waitFor(() => streamChannels.get(null)?.length === 0);

View File

@ -106,7 +106,6 @@ test('allocates session correctly', async () => {
mockConnection.emitRevision({ account: 3, profile: 3, article: 3, group: 3, channel: 3, card: 3});
mockConnection.emitRing({ cardId: '', callId: 'test', calleeToken: '', ice: []});
await waitFor(() => (status === 'connected'));
await waitFor(() => (mockRing.call?.callId === 'test'));
await waitFor(() => (mockSettings.revision == 3));
await waitFor(() => (mockIdentity.revision == 3));
await waitFor(() => (mockStream.revision == 3));

View File

@ -29,8 +29,8 @@ export interface Link {
}
export interface Ring {
addRingingListener(ev: (calls: Call[]) => void): void;
removeRingingListener(ev: (calls: Call[]) => void): void;
addRingingListener(ev: (calls: { cardId: string, callId: string }[]) => void): void
removeRingingListener(ev: (calls: { cardId: string, callId: string }[]) => void): void
accept(cardId: string, callId: string, contactNode: string): Promise<Link>;
decline(cardId: string, callId: string, contactNode: string): Promise<void>;
@ -55,7 +55,7 @@ export interface Settings {
getBlockedCards(): Promise<{cardId: string, timestamp: number}[]>;
getBlockedChannels(): Promise<{cardId: string | null, channelId: string, timestamp: number}[]>;
getBlockedTopicis(): Promise<{cardId: string | null, channelId: string, topicId: string, timestamp: number}[]>;
getBlockedTopics(): Promise<{cardId: string | null, channelId: string, topicId: string, timestamp: number}[]>;
addConfigListener(ev: (config: Config) => void): void;
removeConfigListener(ev: (config: Config) => void): void;

View File

@ -1,5 +1,5 @@
import { EventEmitter } from 'eventemitter3';
import type { Contact, Focus } from './api';
import type { Contact, Focus, Link } from './api';
import { Logging } from './logging';
import { FocusModule } from './focus';
import { LinkModule } from './link';
@ -109,7 +109,7 @@ export class ContactModule implements Contact {
this.blockedCardChannel = new Set<string>();
this.read = new Map<string, number>();
this.offsyncProfileCard = new Map<string, number>();
this.offsyncArticleCard = new Map<string, nubmer>();
this.offsyncArticleCard = new Map<string, number>();
this.offsyncChannelCard = new Map<string, number>();
this.revision = 0;
@ -656,7 +656,8 @@ export class ContactModule implements Contact {
enableVideo,
enableBinary,
created,
members: members.map(guid => ({ guid })),
members: members.map(({ member }) => ({ guid: member }))
//members: []
}
this.focus.setDetail(cardId, id, focusDetail);
}
@ -1010,6 +1011,18 @@ export class ContactModule implements Contact {
}
}
public async getBlockedChannels(): Promise<Channel[]> {
const channels = [] as Channel[];
this.channelEntries.forEach((card, cardId) => {
card.forEach((entry, channelId) => {
if (this.isChannelBlocked(cardId, channelId)) {
channels.push(entry.channel);
}
});
});
return channels;
}
public async clearBlockedChannelTopic(cardId: string, channelId: string, topicId: string) {
const { guid } = this;
const id = `${cardId}:${channelId}:${topicId}`

View File

@ -236,7 +236,8 @@ export type AccountEntity = {
seal?: string;
version: string;
node: string;
storageUsed: string;
storageUsed: number;
disabled: boolean;
};
export const defaultProfileEntity = {
@ -277,17 +278,16 @@ export type Calling = {
callerToken: string;
calleeToken: string;
keepAlive: number;
ice: { urls: string[]; username: string; credential: string }[];
ice: { urls: string; username: string; credential: string }[];
}
export type Ringing = {
cardId: string;
callId: string;
calleeToken: string;
ice: { urls: string[]; username: string; credential: string }[];
iceUrl: string;
iceUsername: string;
icePassword: string;
iceUrl?: string;
iceUsername?: string;
icePassword?: string;
};
export type Revision = {

View File

@ -259,7 +259,13 @@ export class FocusModule implements Focus {
return new Promise(function (resolve, reject) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onprogress = (ev: ProgressEvent<EventTarget>)=>{ progress((ev.loaded * 100) / ev.total) };
xhr.onprogress = (ev: ProgressEvent<EventTarget>)=>{
try {
progress((ev.loaded * 100) / ev.total)
} catch (err) {
xhr.abort();
}
};
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
@ -275,7 +281,7 @@ export class FocusModule implements Focus {
});
}
private uploadBlock(block: string, topicId: string, progress: (percent: number)=>boolean): Promise<string> {
private uploadBlock(block: string, topicId: string, progress: (percent: number)=>boolean|void): Promise<string> {
const { cardId, channelId, connection } = this;
if (!connection) {
throw new Error('disconnected from channel');
@ -307,7 +313,7 @@ export class FocusModule implements Focus {
});
}
private mirrorFile(source: File|string, topicId: string, progress: (percent: number)=>boolean): Promise<string> {
private mirrorFile(source: File|string, topicId: string, progress: (percent: number)=>boolean|void): Promise<{ assetId: string }> {
const { cardId, channelId, connection } = this;
if (!connection) {
throw new Error('disconnected from channel');
@ -317,7 +323,7 @@ export class FocusModule implements Focus {
const url = `http${secure ? 's' : ''}://${node}/content/channels/${channelId}/topics/${topicId}/blocks?${params}`
const formData = new FormData();
if (typeof source === 'string') { // file path used in mobile
formData.append("asset", {uri: source, name: 'asset', type: 'application/octent-stream'});
formData.append("asset", {uri: source, name: 'asset', type: 'application/octent-stream'} as any);
} else { // file object used in browser
formData.append('asset', source);
}
@ -344,7 +350,7 @@ export class FocusModule implements Focus {
});
}
private transformFile(source: File|string, topicId: string, transforms: string[], progress: (percent: number)=>boolean): Promise<{assetId: string, transform: string}[]> {
private transformFile(source: File|string, topicId: string, transforms: string[], progress: (percent: number)=>boolean|void): Promise<{assetId: string, transform: string}[]> {
const { cardId, channelId, connection } = this;
if (!connection) {
throw new Error('disconnected from channel');
@ -355,7 +361,7 @@ export class FocusModule implements Focus {
const formData = new FormData();
if (typeof source === 'string') { // file path used in mobile
formData.append("asset", {uri: source, name: 'asset', type: 'application/octent-stream'});
formData.append("asset", {uri: source, name: 'asset', type: 'application/octent-stream'} as any);
} else { // file object used in browser
formData.append('asset', source);
}
@ -450,7 +456,7 @@ export class FocusModule implements Focus {
const { encryptedDataB64 } = await crypto.aesEncrypt(base64Data, ivHex, channelKey);
const partId = await this.uploadBlock(encryptedDataB64, topicId, (percent: number) => {
const count = Math.ceil(stagingFile.size / ENCRYPT_BLOCK_SIZE);
assetProgress(Math.floor((i * 100 + percent) / count));
return assetProgress(Math.floor((i * 100 + percent) / count));
});
split.push({ partId, blockIv: ivHex });
}
@ -691,7 +697,7 @@ export class FocusModule implements Focus {
transforms.push('acopy;audio');
transformMap.set('acopy;audio', transform.appId);
} else if (transform.type === TransformType.Copy && asset.type === AssetType.Binary) {
const assetId = await this.mirrorFile(asset.source, topicId, progress);
const { assetId } = await this.mirrorFile(asset.source, topicId, progress);
const assetItem = {
assetId: `${assetItems.length}`,
hosting: HostingMode.Basic,
@ -798,6 +804,7 @@ export class FocusModule implements Focus {
if (!asset) {
throw new Error('asset entry not found');
}
if (asset.hosting === HostingMode.Inline && asset.inline) {
return `${asset.inline}`;
} else if (asset.hosting === HostingMode.Basic && asset.basic) {
@ -809,22 +816,22 @@ export class FocusModule implements Focus {
}
const write = await staging.write();
this.closeStaging.push(write.close);
for (let i = 0; i < asset.split.length; i++) {
let download = true;
const assetCount = asset.split.length;
for (let i = 0; i < assetCount; i++) {
if (progress) {
download = progress(Math.floor((i * 100) / asset.split.length));
}
if (download === false) {
throw new Error('aborted asset load');
const download = progress(Math.floor((i * 100) / assetCount));
if (download === false) {
throw new Error('aborted asset load');
}
}
const block = await this.downloadBlock(topicId, asset.split[i].partId, (percent: number)=>{
if (progress && download !== false) {
download = progress(Math.floor((i * 100 + percent) / asset.split.length));
if (progress) {
const download = progress(Math.floor((i * 100 + percent) / assetCount));
if (download === false) {
throw new Error('aborting asset load');
}
}
});
if (download === false) {
throw new Error('aborted asset load');
}
const { data } = await crypto.aesDecrypt(block, asset.split[i].blockIv, channelKey);
await write.setData(data);
}

View File

@ -1,5 +1,6 @@
import type { AssetItem } from './items';
import { HostingMode } from './types';
import { BasicAsset } from './entities';
export function getLegacyData(data: any): { data: any, assets: AssetItem[] } {
if (data == null) {
@ -86,7 +87,7 @@ export function getLegacyData(data: any): { data: any, assets: AssetItem[] } {
assetItems.add(fullAsset);
index += 1;
return { audio: { label, full: `${index-1}` }};
} else {
} else if (binary) {
const { label, extension, data } = binary;
const dataAsset = {
assetId: `${index}`,
@ -96,9 +97,11 @@ export function getLegacyData(data: any): { data: any, assets: AssetItem[] } {
assetItems.add(dataAsset);
index += 1;
return { binary: { label, extension, data: `${index-1}` }};
} else {
return {};
}
}
})
});
return { data: { text, textColor, textSize, assets: dataAssets }, assets: Array.from(assetItems.values()) };
}

View File

@ -1,4 +1,5 @@
import type { Link, Logging } from './api';
import type { Link } from './api';
import { Logging } from './logging';
import { addCall } from './net/addCall';
import { removeCall } from './net/removeCall';
import { removeContactCall } from './net/removeContactCall';
@ -13,19 +14,17 @@ const RING_INTERVAL = 2000;
export class LinkModule implements Link {
private log: Logging;
private status: string;
private statusListener: (status: string)=>Promise<void> | null;
private messageListener: (message: any)=>Promise<void> | null;
private statusListener: ((status: string)=>Promise<void>) | null;
private messageListener: ((message: any)=>Promise<void>) | null;
private messages: string[];
private error: boolean;
private closed: boolean;
private connected: boolean;
private notifying: boolean;
private websocket: Websocket | null;
private staleInterval: number | null;
private aliveInterval: number | null;
private ringInterval: number | null;
private node: string;
private secure: boolean;
private token: string;
private websocket: WebSocket | null;
private staleInterval: ReturnType<typeof setTimeout> | null;
private aliveInterval: ReturnType<typeof setTimeout> | null;
private ringInterval: ReturnType<typeof setTimeout> | null;
private ice: { urls: string; username: string; credential: string }[];
private cleanup: null | (()=>void);
@ -145,7 +144,7 @@ export class LinkModule implements Link {
}
public async sendMessage(message: any) {
if (this.status !== 'connected') {
if (this.status !== 'connected' || !this.websocket) {
this.log.error('dropping message while not connected')
} else {
this.websocket.send(JSON.stringify(message));
@ -157,16 +156,18 @@ export class LinkModule implements Link {
this.notifying = true;
while(this.messages.length > 0 && !this.error) {
const data = this.messages.shift();
try {
const message = JSON.parse(data);
if (message.status) {
await this.notifyStatus(message.status);
} else {
await this.notifyMessage(message);
if (data) {
try {
const message = JSON.parse(data);
if (message.status) {
await this.notifyStatus(message.status);
} else {
await this.notifyMessage(message);
}
} catch (err) {
this.log.error('failed to process signal message');
this.notifyStatus('error');
}
} catch (err) {
this.log.error('failed to process signal message');
this.notifyStatus('error');
}
}
this.notifying = false;
@ -206,15 +207,11 @@ export class LinkModule implements Link {
await this.messageListener(message);
}
} catch (err) {
this.log('message notification failed');
this.log.warn('message notification failed');
}
}
private setWebSocket(token: string, node: string, secure: boolean): WebSocket {
if (this.closed) {
return this.websocket;
}
const wsUrl = `ws${secure ? 's' : ''}://${node}/signal`;
const ws = new WebSocket(wsUrl);
ws.onmessage = (e) => {
@ -230,7 +227,9 @@ export class LinkModule implements Link {
ws.onclose = () => {};
ws.onopen = () => {};
ws.onerror = () => {};
this.websocket = this.setWebSocket(token, node);
if (!this.closed) {
this.websocket = this.setWebSocket(token, node, secure);
}
}
}, RETRY_INTERVAL);
};

View File

@ -1,7 +1,7 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
import { SetupEntity } from '../entities';
export async function getNodeConfig(server: string, secure: boolean, token: string): Promise<ConfigEntity> {
export async function getNodeConfig(server: string, secure: boolean, token: string): Promise<SetupEntity> {
const endpoint = `http${secure ? 's' : ''}://${server}/admin/config?token=${token}`;
const config = await fetchWithTimeout(endpoint, { method: 'GET' });
checkResponse(config.status);

View File

@ -1,7 +1,7 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
import { PushParams } from '../types';
export async function setAccountNotifications(node: string, secure: boolean, token: string, flag: boolean, pushParams: PushParams) {
export async function setAccountNotifications(node: string, secure: boolean, token: string, flag: boolean, pushParams?: PushParams) {
const pushEndpoint = pushParams ? encodeURIComponent(pushParams.endpoint) : '';
const publicKey = pushParams ? encodeURIComponent(pushParams.publicKey) : '';
const auth = pushParams ? encodeURIComponent(pushParams.auth) : '';

View File

@ -1,5 +1,6 @@
import type { Service } from './api';
import type { Member, Setup } from './types';
import { KeyType, ICEService } from './types';
import { type AccountEntity, avatar } from './entities';
import type { Logging } from './logging';
import { getMembers } from './net/getMembers';
@ -64,9 +65,10 @@ export class ServiceModule implements Service {
const { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService,
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit } = entity;
const service = iceService ? iceService : 'default';
const service = iceService === 'cloudflare' ? ICEService.Cloudflare : ICEService.Default;
const type = keyType === 'RSA4096' ? KeyType.RSA_4096 : KeyType.RSA_2048;
const setup = { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService: service,
keyType: type, pushSupported, allowUnsealed, transformSupported, enableIce, iceService: service,
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit };
return setup;
}
@ -76,9 +78,10 @@ export class ServiceModule implements Service {
const { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService,
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit } = setup;
const service = iceService === 'default' ? null : iceService;
const service = iceService === ICEService.Cloudflare ? 'cloudflare' : ''
const type = keyType === KeyType.RSA_4096 ? 'RSA4096' : 'RSA2048';
const entity = { domain, accountStorage, enableImage, enableAudio, enableVideo, enableBinary,
keyType, pushSupported, allowUnsealed, transformSupported, enableIce, iceService: service,
keyType: type, pushSupported, allowUnsealed, transformSupported, enableIce, iceService: service,
iceUrl, iceUsername, icePassword, enableOpenAccess, openAccessLimit };
await setNodeConfig(node, secure, token, entity);
}

View File

@ -169,7 +169,7 @@ export class StreamModule {
enableVideo,
enableBinary,
created,
members: members.map(guid => ({ guid })),
members: members.map(guid => ({ guid: guid.member })),
}
this.focus.setDetail(null, id, focusDetail);
}
@ -392,6 +392,16 @@ export class StreamModule {
await clearChannelCard(node, secure, token, channelId, cardId);
}
public async getBlockedChannels(): Promise<Channel[]> {
const channels = [] as Channel[];
this.channelEntries.forEach((entry, channelId) => {
if (this.isChannelBlocked(channelId)) {
channels.push(entry.channel);
}
});
return channels;
}
public async setBlockedChannel(channelId: string, blocked: boolean): Promise<void> {
const entry = this.channelEntries.get(channelId);
if (entry) {

View File

@ -215,8 +215,6 @@ export type Member = {
guid: string;
handle: string;
name: string;
description: string;
location: string;
imageUrl: string;
disabled: boolean;
storageUsed: number,