mirror of
https://github.com/zoriya/flood.git
synced 2026-06-05 19:54:18 +00:00
server: services: migrate to TypeScript
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
const objectUtil = require('../util/objectUtil');
|
||||
import objectUtil from '../util/objectUtil';
|
||||
|
||||
const clientSettings = {
|
||||
export const clientSettingsMap = {
|
||||
dht: 'dht.mode',
|
||||
dhtPort: 'dht.port',
|
||||
dhtStats: 'dht.statistics',
|
||||
@@ -48,8 +48,13 @@ const clientSettings = {
|
||||
throttleMinPeersSeed: 'throttle.min_peers.seed',
|
||||
trackersNumWant: 'trackers.numwant',
|
||||
trackersUseUdp: 'trackers.use_udp',
|
||||
} as const;
|
||||
|
||||
// TODO: Is this bidirectional map really necessary?
|
||||
export const clientSettingsBiMap = objectUtil.reflect(clientSettingsMap);
|
||||
|
||||
export type ClientSetting = keyof typeof clientSettingsMap;
|
||||
export type ClientSettings = {
|
||||
// TODO: Need proper types for each property
|
||||
[property in ClientSetting]?: string | Record<string, unknown> | null;
|
||||
};
|
||||
|
||||
const clientSettingsMap = objectUtil.reflect(clientSettings);
|
||||
|
||||
module.exports = {clientSettings, clientSettingsMap};
|
||||
@@ -1,7 +0,0 @@
|
||||
const diffActionTypes = ['ITEM_ADDED', 'ITEM_CHANGED', 'ITEM_REMOVED'];
|
||||
|
||||
module.exports = diffActionTypes.reduce((memo, key) => {
|
||||
memo[key] = key;
|
||||
|
||||
return memo;
|
||||
}, {});
|
||||
@@ -0,0 +1,18 @@
|
||||
import objectUtil from '../util/objectUtil';
|
||||
|
||||
const diffActionTypes = ['ITEM_ADDED', 'ITEM_CHANGED', 'ITEM_REMOVED'] as const;
|
||||
|
||||
export default objectUtil.createStringMapFromArray(diffActionTypes);
|
||||
|
||||
export type DiffActionType = typeof diffActionTypes[number];
|
||||
|
||||
export type DiffAction<T = unknown> = Array<
|
||||
| {
|
||||
action: Exclude<DiffActionType, 'ITEM_REMOVED'>;
|
||||
data: T;
|
||||
}
|
||||
| {
|
||||
action: 'ITEM_REMOVED';
|
||||
data: keyof T;
|
||||
}
|
||||
>;
|
||||
@@ -1,12 +0,0 @@
|
||||
const objectUtil = require('../util/objectUtil');
|
||||
|
||||
const historySnapshotTypes = {
|
||||
FIVE_MINUTE: 'fiveMin',
|
||||
THIRTY_MINUTE: 'thirtyMin',
|
||||
HOUR: 'hour',
|
||||
WEEK: 'week',
|
||||
MONTH: 'month',
|
||||
YEAR: 'year',
|
||||
};
|
||||
|
||||
module.exports = objectUtil.reflect(historySnapshotTypes);
|
||||
@@ -0,0 +1,4 @@
|
||||
const historySnapshotTypes = ['FIVE_MINUTE', 'THIRTY_MINUTE', 'HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR'] as const;
|
||||
|
||||
export default historySnapshotTypes;
|
||||
export type HistorySnapshot = typeof historySnapshotTypes[number];
|
||||
@@ -1,19 +0,0 @@
|
||||
const objectUtil = require('../util/objectUtil');
|
||||
|
||||
const serverEventTypes = [
|
||||
'CLIENT_CONNECTIVITY_STATUS_CHANGE',
|
||||
'DISK_USAGE_CHANGE',
|
||||
'NOTIFICATION_COUNT_CHANGE',
|
||||
'TAXONOMY_FULL_UPDATE',
|
||||
'TAXONOMY_DIFF_CHANGE',
|
||||
'TORRENT_LIST_ACTION_TORRENT_ADDED',
|
||||
'TORRENT_LIST_ACTION_TORRENT_DELETED',
|
||||
'TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED',
|
||||
'TORRENT_LIST_DIFF_CHANGE',
|
||||
'TORRENT_LIST_FULL_UPDATE',
|
||||
'TRANSFER_HISTORY_FULL_UPDATE',
|
||||
'TRANSFER_SUMMARY_DIFF_CHANGE',
|
||||
'TRANSFER_SUMMARY_FULL_UPDATE',
|
||||
];
|
||||
|
||||
module.exports = objectUtil.createStringMapFromArray(serverEventTypes);
|
||||
@@ -1,6 +1,6 @@
|
||||
const torrentFilePropsMap = {
|
||||
props: ['path', 'pathComponents', 'priority', 'sizeBytes', 'sizeChunks', 'completedChunks'],
|
||||
methods: ['f.path=', 'f.path_components=', 'f.priority=', 'f.size_bytes=', 'f.size_chunks=', 'f.completed_chunks='],
|
||||
};
|
||||
} as const;
|
||||
|
||||
module.exports = torrentFilePropsMap;
|
||||
export default torrentFilePropsMap;
|
||||
@@ -27,6 +27,6 @@ const torrentPeerPropsMap = {
|
||||
'p.is_encrypted=',
|
||||
'p.is_incoming=',
|
||||
],
|
||||
};
|
||||
} as const;
|
||||
|
||||
module.exports = torrentPeerPropsMap;
|
||||
export default torrentPeerPropsMap;
|
||||
@@ -1,19 +0,0 @@
|
||||
const objectUtil = require('../util/objectUtil');
|
||||
|
||||
const torrentStatusMap = objectUtil.reflect({
|
||||
ch: 'checking',
|
||||
sd: 'seeding',
|
||||
p: 'paused',
|
||||
c: 'complete',
|
||||
d: 'downloading',
|
||||
ad: 'activelyDownloading',
|
||||
au: 'activelyUploading',
|
||||
s: 'stopped',
|
||||
e: 'error',
|
||||
i: 'inactive',
|
||||
a: 'active',
|
||||
});
|
||||
|
||||
torrentStatusMap.statusShorthand = ['ch', 'sd', 'p', 'c', 'd', 'ad', 'au', 's', 'e', 'i', 'a'];
|
||||
|
||||
module.exports = torrentStatusMap;
|
||||
@@ -0,0 +1,16 @@
|
||||
const torrentStatusMap = [
|
||||
'checking',
|
||||
'seeding',
|
||||
'paused',
|
||||
'complete',
|
||||
'downloading',
|
||||
'activelyDownloading',
|
||||
'activelyUploading',
|
||||
'stopped',
|
||||
'error',
|
||||
'inactive',
|
||||
'active',
|
||||
] as const;
|
||||
|
||||
export type TorrentStatus = typeof torrentStatusMap[number] | 'all';
|
||||
export default torrentStatusMap;
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
const torrentTrackerPropsMap = {
|
||||
props: ['group', 'url', 'id', 'minInterval', 'normalInterval', 'type'],
|
||||
methods: ['t.group=', 't.url=', 't.id=', 't.min_interval=', 't.normal_interval=', 't.type='],
|
||||
};
|
||||
} as const;
|
||||
|
||||
module.exports = torrentTrackerPropsMap;
|
||||
export default torrentTrackerPropsMap;
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface AddTorrentByURLOptions {
|
||||
urls: Array<string>;
|
||||
destination: string;
|
||||
isBasePath: boolean;
|
||||
start: boolean;
|
||||
tags?: Array<string>;
|
||||
}
|
||||
|
||||
export interface MoveTorrentsOptions {
|
||||
destination: string;
|
||||
isBasePath: boolean;
|
||||
filenames: Array<string>;
|
||||
sourcePaths: Array<string>;
|
||||
moveFiles: boolean;
|
||||
isCheckHash: boolean;
|
||||
}
|
||||
@@ -14,6 +14,8 @@ export interface Credentials {
|
||||
isAdmin?: boolean;
|
||||
}
|
||||
|
||||
export type UserInDatabase = Required<Credentials> & {_id: string};
|
||||
|
||||
// auth/authenticate
|
||||
export interface AuthAuthenticationResponse {
|
||||
success: boolean;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// TODO: Unite with clientSettingsMap when server is TS.
|
||||
|
||||
import {clientSettings} from '../constants/clientSettingsMap';
|
||||
|
||||
export type ClientSetting = keyof typeof clientSettings;
|
||||
export type ClientSettings = {
|
||||
// TODO: Need proper types for each property
|
||||
[property in ClientSetting]?: string | Record<string, unknown> | null;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface Disk {
|
||||
target: string;
|
||||
size: number;
|
||||
avail: number;
|
||||
used: number;
|
||||
}
|
||||
|
||||
export type Disks = Array<Disk>;
|
||||
@@ -0,0 +1,28 @@
|
||||
export interface Notification {
|
||||
_id?: string;
|
||||
id: 'notification.torrent.finished' | 'notification.torrent.errored' | 'notification.feed.downloaded.torrent';
|
||||
read: boolean;
|
||||
ts: number; // timestamp
|
||||
data: {
|
||||
name: string;
|
||||
ruleLabel?: string;
|
||||
feedLabel?: string;
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface NotificationCount {
|
||||
total: number;
|
||||
unread: number;
|
||||
read: number;
|
||||
}
|
||||
|
||||
export interface NotificationState {
|
||||
id: string;
|
||||
count: NotificationCount;
|
||||
limit: number;
|
||||
start: number;
|
||||
notifications: Array<Notification>;
|
||||
}
|
||||
|
||||
export type NotificationFetchOptions = Pick<NotificationState, 'id' | 'limit' | 'start'> & {allNotifications?: boolean};
|
||||
@@ -0,0 +1,21 @@
|
||||
import type {Disks} from './DiskUsage';
|
||||
import type {NotificationCount} from './Notification';
|
||||
import type {Taxonomy, TaxonomyDiffs} from './Taxonomy';
|
||||
import type {Torrents, TorrentListDiff} from './Torrent';
|
||||
import type {TransferHistory, TransferSummary, TransferSummaryDiff} from './TransferData';
|
||||
|
||||
// type: data
|
||||
export interface ServerEvents {
|
||||
CLIENT_CONNECTIVITY_STATUS_CHANGE: {
|
||||
isConnected: boolean;
|
||||
};
|
||||
DISK_USAGE_CHANGE: Disks;
|
||||
NOTIFICATION_COUNT_CHANGE: NotificationCount;
|
||||
TAXONOMY_FULL_UPDATE: Taxonomy;
|
||||
TAXONOMY_DIFF_CHANGE: TaxonomyDiffs;
|
||||
TORRENT_LIST_FULL_UPDATE: Torrents;
|
||||
TORRENT_LIST_DIFF_CHANGE: TorrentListDiff;
|
||||
TRANSFER_HISTORY_FULL_UPDATE: TransferHistory;
|
||||
TRANSFER_SUMMARY_FULL_UPDATE: TransferSummary;
|
||||
TRANSFER_SUMMARY_DIFF_CHANGE: TransferSummaryDiff;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type {DiffAction} from '../constants/diffActionTypes';
|
||||
|
||||
export interface Taxonomy {
|
||||
statusCounts: Record<string, number>;
|
||||
tagCounts: Record<string, number>;
|
||||
trackerCounts: Record<string, number>;
|
||||
}
|
||||
|
||||
type TaxonomyDiff<T extends keyof Taxonomy> = DiffAction<Taxonomy[T]> | null;
|
||||
|
||||
export interface TaxonomyDiffs {
|
||||
statusCounts: TaxonomyDiff<'statusCounts'>;
|
||||
tagCounts: TaxonomyDiff<'tagCounts'>;
|
||||
trackerCounts: TaxonomyDiff<'trackerCounts'>;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import {TorrentStatus} from '../constants/torrentStatusMap';
|
||||
|
||||
export interface Duration {
|
||||
weeks?: number;
|
||||
days?: number;
|
||||
hours?: number;
|
||||
minutes?: number;
|
||||
seconds?: number;
|
||||
cumSeconds: number;
|
||||
}
|
||||
|
||||
export interface TorrentDetails {
|
||||
fileTree: {
|
||||
files: Array<{
|
||||
index: number;
|
||||
filename: string;
|
||||
path: string;
|
||||
percentComplete: number;
|
||||
priority: number;
|
||||
sizeBytes: number;
|
||||
}>;
|
||||
peers: Array<TorrentPeer>;
|
||||
trackers: Array<TorrentTracker>;
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Unite with torrentPeerPropsMap when it is TS.
|
||||
export interface TorrentPeer {
|
||||
index: number;
|
||||
country: string;
|
||||
address: string;
|
||||
completedPercent: number;
|
||||
clientVersion: string;
|
||||
downloadRate: number;
|
||||
downloadTotal: number;
|
||||
uploadRate: number;
|
||||
uploadTotal: number;
|
||||
id: string;
|
||||
peerRate: number;
|
||||
peerTotal: number;
|
||||
isEncrypted: boolean;
|
||||
isIncoming: boolean;
|
||||
}
|
||||
|
||||
// TODO: Unite with torrentTrackerPropsMap when it is TS.
|
||||
export interface TorrentTracker {
|
||||
index: number;
|
||||
id: string;
|
||||
url: string;
|
||||
type: number;
|
||||
group: number;
|
||||
minInterval: number;
|
||||
normalInterval: number;
|
||||
}
|
||||
|
||||
// TODO: Rampant over-fetching of torrent properties. Need to remove unused items.
|
||||
// TODO: Unite with torrentListPropMap when it is TS.
|
||||
export interface TorrentProperties {
|
||||
baseDirectory: string;
|
||||
baseFilename: string;
|
||||
basePath: string;
|
||||
bytesDone: number;
|
||||
comment: string;
|
||||
dateAdded: string;
|
||||
dateCreated: string;
|
||||
details: TorrentDetails;
|
||||
directory: string;
|
||||
downRate: number;
|
||||
downTotal: number;
|
||||
eta: 'Infinity' | Duration;
|
||||
hash: string;
|
||||
ignoreScheduler: boolean;
|
||||
isActive: boolean;
|
||||
isComplete: boolean;
|
||||
isHashing: string;
|
||||
isMultiFile: boolean;
|
||||
isOpen: boolean;
|
||||
isPrivate: boolean;
|
||||
isStateChanged: boolean;
|
||||
message: string;
|
||||
name: string;
|
||||
peersConnected: number;
|
||||
peersTotal: number;
|
||||
percentComplete: number;
|
||||
priority: string;
|
||||
ratio: number;
|
||||
seedingTime: string;
|
||||
seedsConnected: number;
|
||||
seedsTotal: number;
|
||||
sizeBytes: number;
|
||||
state: string;
|
||||
status: Array<TorrentStatus>;
|
||||
tags: Array<string>;
|
||||
throttleName: string;
|
||||
trackerURIs: Array<string>;
|
||||
upRate: number;
|
||||
upTotal: number;
|
||||
}
|
||||
|
||||
export interface TorrentListDiff {
|
||||
[hash: string]:
|
||||
| {
|
||||
action: 'TORRENT_LIST_ACTION_TORRENT_ADDED';
|
||||
data: TorrentProperties;
|
||||
}
|
||||
| {
|
||||
action: 'TORRENT_LIST_ACTION_TORRENT_DELETED';
|
||||
}
|
||||
| {
|
||||
action: 'TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED';
|
||||
data: Partial<TorrentProperties>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Torrents {
|
||||
[hash: string]: TorrentProperties;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import {DiffAction} from '@shared/constants/diffActionTypes';
|
||||
|
||||
export interface TransferSummary {
|
||||
downRate: number;
|
||||
downThrottle: number;
|
||||
downTotal: number;
|
||||
upRate: number;
|
||||
upThrottle: number;
|
||||
upTotal: number;
|
||||
}
|
||||
|
||||
export type TransferSummaryDiff = DiffAction<Partial<TransferSummary>>;
|
||||
|
||||
export type TransferDirection = 'upload' | 'download';
|
||||
|
||||
export type TransferData = Record<TransferDirection, number>;
|
||||
|
||||
export interface TransferSnapshot extends TransferData {
|
||||
numUpdates?: number;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export type TransferHistory = Record<TransferDirection | 'timestamps', Array<number>>;
|
||||
@@ -1,72 +0,0 @@
|
||||
const diffActionTypes = require('../constants/diffActionTypes');
|
||||
|
||||
const objectUtil = {
|
||||
createStringMapFromArray: (array) =>
|
||||
array.reduce((memo, key) => {
|
||||
memo[key] = key;
|
||||
|
||||
return memo;
|
||||
}, {}),
|
||||
|
||||
createSymbolMapFromArray: (array = []) =>
|
||||
array.reduce((memo, key) => {
|
||||
memo[key] = Symbol(key);
|
||||
|
||||
return memo;
|
||||
}, {}),
|
||||
|
||||
getDiff: (prevObject = {}, nextObject = {}) => {
|
||||
const prevObjectKeys = Object.keys(prevObject);
|
||||
const nextObjectKeys = Object.keys(nextObject);
|
||||
|
||||
let shouldCheckForRemovals = nextObjectKeys.length < prevObjectKeys.length;
|
||||
|
||||
const diff = nextObjectKeys.reduce((accumulator, key) => {
|
||||
const prevValue = prevObject[key];
|
||||
const nextValue = nextObject[key];
|
||||
|
||||
if (prevValue == null) {
|
||||
shouldCheckForRemovals = true;
|
||||
|
||||
accumulator.push({
|
||||
action: diffActionTypes.ITEM_ADDED,
|
||||
data: {
|
||||
[key]: nextValue,
|
||||
},
|
||||
});
|
||||
} else if (prevValue !== nextValue) {
|
||||
accumulator.push({
|
||||
action: diffActionTypes.ITEM_CHANGED,
|
||||
data: {
|
||||
[key]: nextValue,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}, []);
|
||||
|
||||
if (shouldCheckForRemovals) {
|
||||
prevObjectKeys.forEach((key) => {
|
||||
if (nextObject[key] == null) {
|
||||
diff.push({
|
||||
action: diffActionTypes.ITEM_REMOVED,
|
||||
data: key,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return diff;
|
||||
},
|
||||
|
||||
reflect: (object) =>
|
||||
Object.keys(object).reduce((memo, key) => {
|
||||
memo[key] = object[key];
|
||||
memo[object[key]] = key;
|
||||
|
||||
return memo;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
module.exports = objectUtil;
|
||||
@@ -0,0 +1,88 @@
|
||||
import type {DiffAction} from '../constants/diffActionTypes';
|
||||
|
||||
type KeyFromValue<V, T extends Record<string, string>> = {
|
||||
[K in keyof T]: V extends T[K] ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
const objectUtil = {
|
||||
createStringMapFromArray: <T extends string>(array: Readonly<Array<T>>): Readonly<{[key in T]: key}> => {
|
||||
return array.reduce((memo, key) => {
|
||||
return Object.assign(memo, {
|
||||
[key]: key,
|
||||
});
|
||||
}, {} as Partial<{[key in T]: key}>) as Readonly<{[key in T]: key}>;
|
||||
},
|
||||
|
||||
getDiff: <P extends string, N extends string>(
|
||||
prevObject: Partial<Record<P, unknown>>,
|
||||
nextObject: Partial<Record<N, unknown>>,
|
||||
) => {
|
||||
const prevObjectKeys = Object.keys(prevObject);
|
||||
const nextObjectKeys = Object.keys(nextObject);
|
||||
|
||||
let shouldCheckForRemovals = nextObjectKeys.length < prevObjectKeys.length;
|
||||
|
||||
const diff = nextObjectKeys.reduce((accumulator, key) => {
|
||||
const prevValue = prevObject[key as P];
|
||||
const nextValue = nextObject[key as N];
|
||||
|
||||
if (prevValue == null) {
|
||||
shouldCheckForRemovals = true;
|
||||
|
||||
accumulator.push({
|
||||
action: 'ITEM_ADDED',
|
||||
data: {
|
||||
[key]: nextValue,
|
||||
},
|
||||
} as {
|
||||
action: 'ITEM_ADDED';
|
||||
data: {
|
||||
[key in N]: typeof nextObject[key];
|
||||
};
|
||||
});
|
||||
} else if (prevValue !== nextValue) {
|
||||
accumulator.push({
|
||||
action: 'ITEM_CHANGED',
|
||||
data: {
|
||||
[key]: nextValue,
|
||||
},
|
||||
} as {
|
||||
action: 'ITEM_CHANGED';
|
||||
data: {
|
||||
[key in N]: typeof nextObject[key];
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}, [] as DiffAction<Record<N, unknown>>);
|
||||
|
||||
if (shouldCheckForRemovals) {
|
||||
prevObjectKeys.forEach((key) => {
|
||||
if (nextObject[key as N] == null) {
|
||||
diff.push({
|
||||
action: 'ITEM_REMOVED',
|
||||
data: key as N,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return diff;
|
||||
},
|
||||
|
||||
reflect: <T extends Record<K, string>, K extends keyof T>(
|
||||
object: T,
|
||||
): {[value in T[K]]: KeyFromValue<value, T>} & {[key in K]: T[key]} => {
|
||||
return Object.assign(
|
||||
object,
|
||||
Object.keys(object).reduce((memo, key) => {
|
||||
return Object.assign(memo, {
|
||||
[object[key as K]]: key,
|
||||
});
|
||||
}, {} as {[value in T[K]]: KeyFromValue<value, T>}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default objectUtil;
|
||||
@@ -2,6 +2,6 @@ const regEx = {
|
||||
url: /^(?:https?|ftp):\/\/.{1,}\.{1}.{1,}/,
|
||||
domainName: /(?:https?|udp):\/\/(?:www\.)?([-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,18}\b)*(\/[/\d\w.-]*)*(?:[?])*(.+)*/i,
|
||||
cdata: /<!\[CDATA\[(.*?)\]\]>/,
|
||||
};
|
||||
} as const;
|
||||
|
||||
module.exports = regEx;
|
||||
export default regEx;
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
capitalize: (string) => string.charAt(0).toUpperCase() + string.slice(1),
|
||||
|
||||
pluralize: (string, count) => {
|
||||
if (count !== 1) {
|
||||
if (string.charAt(string.length - 1) === 'y') {
|
||||
return `${string.substring(0, string.length - 1)}ies`;
|
||||
}
|
||||
return `${string}s`;
|
||||
}
|
||||
|
||||
return string;
|
||||
},
|
||||
|
||||
withoutTrailingSlash: (input) => input.replace(/\/{1,}$/, ''),
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export default {
|
||||
capitalize: (string: string): string => string.charAt(0).toUpperCase() + string.slice(1),
|
||||
withoutTrailingSlash: (input: string): string => input.replace(/\/{1,}$/, ''),
|
||||
};
|
||||
Reference in New Issue
Block a user