server: notificationService: replace callbacks with promises

This commit is contained in:
Jesse Chan
2020-12-28 21:35:54 +08:00
parent d29873b62b
commit 4239ecf80e
4 changed files with 110 additions and 86 deletions
@@ -117,10 +117,7 @@ const FloodActions = {
fetchNotifications: (options: NotificationFetchOptions) => fetchNotifications: (options: NotificationFetchOptions) =>
axios axios
.get(`${baseURI}api/notifications`, { .get(`${baseURI}api/notifications`, {
params: { params: options,
limit: options.limit,
start: options.start,
},
}) })
.then((json) => json.data) .then((json) => json.data)
.then( .then(
+33 -2
View File
@@ -70,12 +70,43 @@ router.get<unknown, unknown, unknown, {snapshot: HistorySnapshot}>('/history', (
); );
}); });
/**
* GET /api/notifications
* @summary Gets notifications
* @tags Flood
* @security User
* @param {NotificationFetchOptions} queries - options
* @return {{Notification[][], NotificationCount}} 200 - success response - application/json
* @return {Error} 500 - failure response - application/json
*/
router.get<unknown, unknown, unknown, NotificationFetchOptions>('/notifications', (req, res) => { router.get<unknown, unknown, unknown, NotificationFetchOptions>('/notifications', (req, res) => {
req.services?.notificationService.getNotifications(req.query, getResponseFn(res)); req.services?.notificationService.getNotifications(req.query).then(
(notifications) => {
res.status(200).json(notifications);
},
(err: Error) => {
res.status(500).json({message: err.message});
},
);
}); });
/**
* DELETE /api/notifications
* @summary Clears notifications
* @tags Flood
* @security User
* @return 200 - success response
* @return {Error} 500 - failure response - application/json
*/
router.delete('/notifications', (req, res) => { router.delete('/notifications', (req, res) => {
req.services?.notificationService.clearNotifications(getResponseFn(res)); req.services?.notificationService.clearNotifications().then(
() => {
res.status(200).send();
},
(err: Error) => {
res.status(500).json({message: err.message});
},
);
}); });
/** /**
+75 -79
View File
@@ -1,4 +1,4 @@
import Datastore from 'nedb'; import Datastore from 'nedb-promises';
import path from 'path'; import path from 'path';
import type {Notification, NotificationCount, NotificationFetchOptions} from '@shared/types/Notification'; import type {Notification, NotificationCount, NotificationFetchOptions} from '@shared/types/Notification';
@@ -14,50 +14,18 @@ const DEFAULT_QUERY_LIMIT = 20;
class NotificationService extends BaseService<NotificationServiceEvents> { class NotificationService extends BaseService<NotificationServiceEvents> {
count: NotificationCount = {read: 0, total: 0, unread: 0}; count: NotificationCount = {read: 0, total: 0, unread: 0};
db = this.loadDatabase(); db = Datastore.create({
autoload: true,
filename: path.join(config.dbPath, this.user._id, 'notifications.db'),
});
constructor(...args: ConstructorParameters<typeof BaseService>) { constructor(...args: ConstructorParameters<typeof BaseService>) {
super(...args); super(...args);
this.onServicesUpdated = () => { (async () => {
this.countNotifications(); const notifications = await this.db.find<Notification>({}).catch(() => undefined);
};
}
addNotification(notifications: Array<Pick<Notification, 'id' | 'data'>>) { if (notifications != null) {
this.count.total += notifications.length;
this.count.unread += notifications.length;
const timestamp = Date.now();
const notificationsToInsert = notifications.map((notification) => ({
ts: timestamp,
data: notification.data,
id: notification.id,
read: false,
})) as Notification[];
this.db.insert(notificationsToInsert, () => this.emitUpdate());
}
clearNotifications(callback: (data?: null, err?: Error) => void) {
this.db.remove({}, {multi: true}, (err) => {
if (err) {
callback(null, err);
return;
}
this.count = {read: 0, total: 0, unread: 0};
this.emitUpdate();
callback();
});
}
countNotifications() {
this.db.find({}, (err: Error, notifications: Array<Notification>) => {
if (err) {
this.count = {read: 0, total: 0, unread: 0};
} else {
notifications.forEach((notification) => { notifications.forEach((notification) => {
if (notification.read) { if (notification.read) {
this.count.read += 1; this.count.read += 1;
@@ -70,7 +38,7 @@ class NotificationService extends BaseService<NotificationServiceEvents> {
} }
this.emitUpdate(); this.emitUpdate();
}); })();
} }
emitUpdate = () => { emitUpdate = () => {
@@ -84,49 +52,77 @@ class NotificationService extends BaseService<NotificationServiceEvents> {
return this.count; return this.count;
} }
getNotifications( /**
query: NotificationFetchOptions, * Adds notifications
callback: ( *
data: { * @param {Array<Notification>} notifications - Notifications to add
notifications: Notification[][]; * @return {Promise<void>} - Rejects with error.
count: NotificationCount; */
} | null, async addNotification(notifications: Array<Pick<Notification, 'id' | 'data'>>): Promise<void> {
err?: Error, this.count.total += notifications.length;
) => void, this.count.unread += notifications.length;
) {
const sortedNotifications = this.db.find({}).sort({ts: -1});
const queryCallback = (err: Error | null, notifications: Notification[][]) => {
if (err) {
callback(null, err);
return;
}
callback({notifications, count: this.count}); await this.db
}; .insert(
notifications.map((notification) => ({
ts: Date.now(),
data: notification.data,
id: notification.id,
read: false,
})),
)
.catch(() => undefined);
if (query.allNotifications) { this.emitUpdate();
sortedNotifications.exec(queryCallback);
} else if (query.start != null) {
sortedNotifications
.skip(Number(query.start))
.limit(Number(query.limit) || DEFAULT_QUERY_LIMIT)
.exec(queryCallback);
} else {
sortedNotifications.limit(Number(query.limit) || DEFAULT_QUERY_LIMIT).exec(queryCallback);
}
} }
loadDatabase(): Datastore<Array<Notification>> { /**
if (this.db != null) { * Clears notifications
return this.db; *
* @return {Promise<void>} - Rejects with error.
*/
async clearNotifications(): Promise<void> {
await this.db.remove({}, {multi: true});
this.count = {read: 0, total: 0, unread: 0};
this.emitUpdate();
}
/**
* Gets notifications
*
* @param {NotificationFetchOptions} - options - An object of options...
* @return {Promise<{Notification[][], NotificationCount}>} - Resolves with notifications and counts or rejects with error.
*/
async getNotifications({
allNotifications,
start,
limit,
}: NotificationFetchOptions): Promise<{
notifications: Notification[][];
count: NotificationCount;
}> {
const sortedNotifications = this.db.find<Notification>({}).sort({ts: -1});
if (allNotifications) {
return {
notifications: await sortedNotifications.exec(),
count: this.count,
};
} else if (start != null) {
return {
notifications: await sortedNotifications
.skip(Number(start))
.limit(Number(limit) || DEFAULT_QUERY_LIMIT)
.exec(),
count: this.count,
};
} else {
return {
notifications: await sortedNotifications.limit(Number(limit) || DEFAULT_QUERY_LIMIT).exec(),
count: this.count,
};
} }
const db = new Datastore({
autoload: true,
filename: path.join(config.dbPath, this.user._id, 'notifications.db'),
});
return db;
} }
} }
+1 -1
View File
@@ -34,7 +34,7 @@ export interface NotificationState {
} }
export interface NotificationFetchOptions { export interface NotificationFetchOptions {
id: string; id?: string;
limit: number; limit: number;
start: number; start: number;
allNotifications?: boolean; allNotifications?: boolean;