mirror of
https://github.com/zoriya/flood.git
synced 2026-06-02 11:06:35 +00:00
server: migrate Flood settings functions to per-user service
This commit is contained in:
@@ -32,6 +32,7 @@ const TorrentActions = {
|
||||
type: 'CLIENT_ADD_TORRENT_SUCCESS',
|
||||
data: {
|
||||
count: options.urls.length,
|
||||
start: options.start,
|
||||
destination: options.destination,
|
||||
response,
|
||||
},
|
||||
@@ -57,6 +58,7 @@ const TorrentActions = {
|
||||
type: 'CLIENT_ADD_TORRENT_SUCCESS',
|
||||
data: {
|
||||
count: options.files.length,
|
||||
start: options.start,
|
||||
destination: options.destination,
|
||||
data,
|
||||
},
|
||||
@@ -79,7 +81,6 @@ const TorrentActions = {
|
||||
type: 'CLIENT_ADD_TORRENT_SUCCESS',
|
||||
data: {
|
||||
count: 1,
|
||||
destination: '',
|
||||
},
|
||||
});
|
||||
download(response.data, (options.name || `${Date.now()}`).concat('.torrent'));
|
||||
|
||||
@@ -233,7 +233,7 @@ interface ClientFetchTorrentDetailsSuccessAction {
|
||||
|
||||
interface ClientAddTorrentSuccessAction {
|
||||
type: 'CLIENT_ADD_TORRENT_SUCCESS';
|
||||
data: {count: number; destination: string};
|
||||
data: {count: number; start?: boolean; destination?: string};
|
||||
}
|
||||
|
||||
interface ClientMoveTorrentsSuccessAction {
|
||||
|
||||
@@ -99,15 +99,28 @@ class TorrentStoreClass extends BaseStore {
|
||||
this.emit('CLIENT_ADD_TORRENT_ERROR');
|
||||
}
|
||||
|
||||
handleAddTorrentSuccess(response: {count: number; destination: string}) {
|
||||
handleAddTorrentSuccess({count, start, destination}: {count: number; start?: boolean; destination?: string}) {
|
||||
this.emit('CLIENT_ADD_TORRENT_SUCCESS');
|
||||
|
||||
SettingsStore.setFloodSetting('torrentDestination', response.destination);
|
||||
// Remembers user's selections
|
||||
const changedSettings: Partial<FloodSettings> = {
|
||||
...(start != null
|
||||
? {
|
||||
startTorrentsOnLoad: start,
|
||||
}
|
||||
: {}),
|
||||
...(destination != null
|
||||
? {
|
||||
torrentDestination: destination,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
SettingsStore.saveFloodSettings(changedSettings);
|
||||
|
||||
AlertStore.add({
|
||||
accumulation: {
|
||||
id: 'alert.torrent.add',
|
||||
value: response.count || 1,
|
||||
value: count || 1,
|
||||
},
|
||||
id: 'alert.torrent.add',
|
||||
});
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import Datastore from 'nedb';
|
||||
import noop from 'lodash/noop';
|
||||
import path from 'path';
|
||||
|
||||
import type {UserInDatabase} from '@shared/schema/Auth';
|
||||
|
||||
import config from '../../config';
|
||||
|
||||
interface SettingsRecord {
|
||||
id: string;
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
const databases = new Map();
|
||||
|
||||
function getDb(user: UserInDatabase): Datastore<SettingsRecord> {
|
||||
const userId = user._id;
|
||||
|
||||
if (databases.has(userId)) {
|
||||
return databases.get(userId);
|
||||
}
|
||||
|
||||
const database = new Datastore<SettingsRecord>({
|
||||
autoload: true,
|
||||
filename: path.join(config.dbPath, userId, 'settings', 'settings.db'),
|
||||
});
|
||||
|
||||
databases.set(userId, database);
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
const settings = {
|
||||
get: (
|
||||
user: UserInDatabase,
|
||||
opts: {property?: string},
|
||||
callback: (data: Record<string, unknown> | null, error?: Error) => void,
|
||||
) => {
|
||||
const query: {id?: string} = {};
|
||||
const settingsToReturn: Record<string, unknown> = {};
|
||||
|
||||
if (opts.property) {
|
||||
query.id = opts.property;
|
||||
}
|
||||
|
||||
getDb(user)
|
||||
.find(query)
|
||||
.exec((err, docs) => {
|
||||
if (err) {
|
||||
callback(null, err);
|
||||
return;
|
||||
}
|
||||
|
||||
docs.forEach((doc) => {
|
||||
settingsToReturn[doc.id] = doc.data;
|
||||
});
|
||||
|
||||
callback(settingsToReturn);
|
||||
});
|
||||
},
|
||||
|
||||
set: (
|
||||
user: UserInDatabase,
|
||||
payloads: Array<SettingsRecord>,
|
||||
callback: (data?: Array<Array<SettingsRecord>> | null, error?: Error) => void = noop,
|
||||
) => {
|
||||
const docsResponse: Array<Array<SettingsRecord>> = [];
|
||||
|
||||
if (payloads && payloads.length) {
|
||||
let error;
|
||||
payloads.forEach((payload) => {
|
||||
getDb(user).update(
|
||||
{id: payload.id},
|
||||
{$set: {data: payload.data}},
|
||||
{upsert: true},
|
||||
(err: Error | null, _numberOfUpdated: number, docs: Array<SettingsRecord>, _upsert: boolean) => {
|
||||
docsResponse.push(docs);
|
||||
if (err) {
|
||||
error = err;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
if (error) {
|
||||
callback(null, error);
|
||||
} else {
|
||||
callback(docsResponse);
|
||||
}
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default settings;
|
||||
+60
-20
@@ -1,6 +1,7 @@
|
||||
import express from 'express';
|
||||
import passport from 'passport';
|
||||
|
||||
import type {FloodSettings} from '@shared/types/FloodSettings';
|
||||
import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes';
|
||||
import type {NotificationFetchOptions} from '@shared/types/Notification';
|
||||
import type {SetFloodSettingsOptions} from '@shared/types/api/index';
|
||||
@@ -13,7 +14,6 @@ import clientActivityStream from '../../middleware/clientActivityStream';
|
||||
import eventStream from '../../middleware/eventStream';
|
||||
import feedMonitorRoutes from './feed-monitor';
|
||||
import {getDirectoryList} from '../../util/fileUtil';
|
||||
import settings from '../../models/settings';
|
||||
import torrentsRoutes from './torrents';
|
||||
|
||||
const router = express.Router();
|
||||
@@ -54,29 +54,69 @@ router.delete('/notifications', (req, res) => {
|
||||
req.services?.notificationService.clearNotifications(ajaxUtil.getResponseFn(res));
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/settings
|
||||
* @summary Gets all Flood's settings
|
||||
* @tags Flood
|
||||
* @security User
|
||||
* @return {FloodSettings} 200 - success response - application/json
|
||||
* @return {Error} 500 - failure response - application/json
|
||||
*/
|
||||
router.get('/settings', (req, res) => {
|
||||
if (req.user == null) {
|
||||
res.status(401).json(Error('Unauthorized'));
|
||||
return;
|
||||
}
|
||||
settings.get(req.user, req.query, ajaxUtil.getResponseFn(res));
|
||||
const callback = ajaxUtil.getResponseFn(res);
|
||||
|
||||
req.services?.settingService
|
||||
.get(null)
|
||||
.then((settings) => {
|
||||
callback(settings as FloodSettings);
|
||||
})
|
||||
.catch((err) => {
|
||||
callback(null, err);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/settings/{property}
|
||||
* @summary Gets Flood's settings
|
||||
* @tags Flood
|
||||
* @security User
|
||||
* @param property.path
|
||||
* @return {Partial<FloodSettings>} 200 - success response - application/json
|
||||
* @return {Error} 500 - failure response - application/json
|
||||
*/
|
||||
router.get('/settings/:property', (req, res) => {
|
||||
const callback = ajaxUtil.getResponseFn(res);
|
||||
|
||||
req.services?.settingService
|
||||
.get(req.params.property)
|
||||
.then((settings) => {
|
||||
callback(settings);
|
||||
})
|
||||
.catch((err) => {
|
||||
callback(null, err);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* PATCH /api/settings
|
||||
* @summary Sets Flood's settings
|
||||
* @tags Flood
|
||||
* @security User
|
||||
* @param {Partial<FloodSettings>} request.body.required - options - application/json
|
||||
* @return {Partial<FloodSettings>} 200 - success response - application/json
|
||||
* @return {Error} 500 - failure response - application/json
|
||||
*/
|
||||
router.patch<unknown, unknown, SetFloodSettingsOptions>('/settings', (req, res) => {
|
||||
Promise.all(
|
||||
Object.keys(req.body).map(async (property) => {
|
||||
return {
|
||||
id: property,
|
||||
data: req.body[property as keyof typeof req.body],
|
||||
};
|
||||
}),
|
||||
).then((settingRecords) => {
|
||||
if (req.user == null) {
|
||||
res.status(401).json(Error('Unauthorized'));
|
||||
return;
|
||||
}
|
||||
settings.set(req.user, settingRecords, ajaxUtil.getResponseFn(res));
|
||||
});
|
||||
const callback = ajaxUtil.getResponseFn(res);
|
||||
|
||||
req.services?.settingService
|
||||
.set(req.body)
|
||||
.then((savedSettings) => {
|
||||
callback(savedSettings);
|
||||
})
|
||||
.catch((err) => {
|
||||
callback(null, err);
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -23,7 +23,6 @@ import ajaxUtil from '../../util/ajaxUtil';
|
||||
import client from '../../models/client';
|
||||
import {getTempPath} from '../../models/TemporaryStorage';
|
||||
import mediainfo from '../../util/mediainfo';
|
||||
import settings from '../../models/settings';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -42,9 +41,6 @@ router.post<unknown, unknown, AddTorrentByURLOptions>('/add-urls', (req, res) =>
|
||||
req.services?.clientGatewayService
|
||||
.addTorrentsByURL(req.body)
|
||||
.then((response) => {
|
||||
if (req.user != null) {
|
||||
settings.set(req.user, [{id: 'startTorrentsOnLoad', data: req.body.start === true}]);
|
||||
}
|
||||
req.services?.torrentService.fetchTorrentList();
|
||||
return response;
|
||||
})
|
||||
@@ -69,9 +65,6 @@ router.post<unknown, unknown, AddTorrentByFileOptions>('/add-files', (req, res)
|
||||
req.services?.clientGatewayService
|
||||
.addTorrentsByFile(req.body)
|
||||
.then((response) => {
|
||||
if (req.user != null) {
|
||||
settings.set(req.user, [{id: 'startTorrentsOnLoad', data: req.body.start === true}]);
|
||||
}
|
||||
req.services?.torrentService.fetchTorrentList();
|
||||
return response;
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import ClientRequestManager from './rTorrent/clientRequestManager';
|
||||
import FeedService from './feedService';
|
||||
import HistoryService from './historyService';
|
||||
import NotificationService from './notificationService';
|
||||
import SettingService from './settingService';
|
||||
import TaxonomyService from './taxonomyService';
|
||||
import TorrentService from './torrentService';
|
||||
|
||||
@@ -14,6 +15,7 @@ type Service =
|
||||
| typeof FeedService
|
||||
| typeof HistoryService
|
||||
| typeof NotificationService
|
||||
| typeof SettingService
|
||||
| typeof TaxonomyService
|
||||
| typeof TorrentService;
|
||||
|
||||
@@ -23,6 +25,7 @@ const serviceInstances: {
|
||||
feedServices: Record<string, FeedService>;
|
||||
historyServices: Record<string, HistoryService>;
|
||||
notificationServices: Record<string, NotificationService>;
|
||||
settingServices: Record<string, SettingService>;
|
||||
taxonomyServices: Record<string, TaxonomyService>;
|
||||
torrentServices: Record<string, TorrentService>;
|
||||
} = {
|
||||
@@ -31,6 +34,7 @@ const serviceInstances: {
|
||||
feedServices: {},
|
||||
historyServices: {},
|
||||
notificationServices: {},
|
||||
settingServices: {},
|
||||
taxonomyServices: {},
|
||||
torrentServices: {},
|
||||
};
|
||||
@@ -70,6 +74,10 @@ const getNotificationService = (user: UserInDatabase): NotificationService => {
|
||||
return getService('notificationServices', NotificationService, user);
|
||||
};
|
||||
|
||||
const getSettingService = (user: UserInDatabase): SettingService => {
|
||||
return getService('settingServices', SettingService, user);
|
||||
};
|
||||
|
||||
const getTaxonomyService = (user: UserInDatabase): TaxonomyService => {
|
||||
return getService('taxonomyServices', TaxonomyService, user);
|
||||
};
|
||||
@@ -100,6 +108,10 @@ const getAllServices = (user: UserInDatabase) =>
|
||||
return getNotificationService(user);
|
||||
},
|
||||
|
||||
get settingService() {
|
||||
return getSettingService(user);
|
||||
},
|
||||
|
||||
get taxonomyService() {
|
||||
return getTaxonomyService(user);
|
||||
},
|
||||
@@ -157,6 +169,7 @@ export default {
|
||||
getClientGatewayService,
|
||||
getHistoryService,
|
||||
getNotificationService,
|
||||
getSettingService,
|
||||
getTaxonomyService,
|
||||
getTorrentService,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import Datastore from 'nedb';
|
||||
import path from 'path';
|
||||
|
||||
import type {FloodSettings} from '@shared/types/FloodSettings';
|
||||
|
||||
import config from '../../config';
|
||||
import BaseService from './BaseService';
|
||||
|
||||
interface SettingRecord {
|
||||
id: string;
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
interface SettingServiceEvents {
|
||||
SETTINGS_CHANGE: (changeSettings: Partial<FloodSettings>) => void;
|
||||
}
|
||||
|
||||
class SettingService extends BaseService<SettingServiceEvents> {
|
||||
db = this.loadDatabase();
|
||||
|
||||
loadDatabase(): Datastore<SettingRecord> {
|
||||
const userId = this.user._id;
|
||||
|
||||
const database = new Datastore<SettingRecord>({
|
||||
autoload: true,
|
||||
filename: path.join(config.dbPath, userId, 'settings', 'settings.db'),
|
||||
});
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
async get(property: string | null): Promise<Partial<FloodSettings>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db
|
||||
.find(
|
||||
property
|
||||
? {
|
||||
id: property,
|
||||
}
|
||||
: {},
|
||||
)
|
||||
.exec(async (err, docs) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(
|
||||
Object.assign(
|
||||
{},
|
||||
...(await Promise.all(
|
||||
docs.map(async (doc) => {
|
||||
return {
|
||||
[doc.id]: doc.data,
|
||||
};
|
||||
}),
|
||||
)),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async set(changedSettings: Partial<FloodSettings>): Promise<Partial<FloodSettings>> {
|
||||
const savedSettings: typeof changedSettings = {};
|
||||
|
||||
if (changedSettings) {
|
||||
let error: Error | null = null;
|
||||
|
||||
await Promise.all(
|
||||
Object.keys(changedSettings).map((key) => {
|
||||
return new Promise<keyof FloodSettings>((resolve, reject) => {
|
||||
const property = key as keyof FloodSettings;
|
||||
return this.db.update(
|
||||
{id: property},
|
||||
{$set: {data: changedSettings[property]}},
|
||||
{upsert: true},
|
||||
(err: Error | null) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(property);
|
||||
},
|
||||
);
|
||||
})
|
||||
.then((property) => {
|
||||
Object.assign(savedSettings, {
|
||||
[property]: changedSettings[property],
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
error = e;
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('SETTINGS_CHANGE', savedSettings);
|
||||
return savedSettings;
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingService;
|
||||
Reference in New Issue
Block a user