mirror of
https://github.com/plankanban/planka.git
synced 2025-07-24 07:39:44 +02:00
parent
ad7fb51cfa
commit
2ee1166747
1557 changed files with 76832 additions and 47042 deletions
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* current-user hook
|
||||
*
|
||||
|
@ -17,10 +22,7 @@ module.exports = function defineCurrentUserHook(sails) {
|
|||
return null;
|
||||
}
|
||||
|
||||
const session = await Session.findOne({
|
||||
accessToken,
|
||||
deletedAt: null,
|
||||
});
|
||||
const session = await Session.qm.getOneUndeletedByAccessToken(accessToken);
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
|
@ -30,9 +32,15 @@ module.exports = function defineCurrentUserHook(sails) {
|
|||
return null;
|
||||
}
|
||||
|
||||
const user = await sails.helpers.users.getOne(payload.subject);
|
||||
const user = await User.qm.getOneById(payload.subject, {
|
||||
withDeactivated: false,
|
||||
});
|
||||
|
||||
if (user && user.passwordChangedAt > payload.issuedAt) {
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user.passwordChangedAt > payload.issuedAt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -66,6 +74,10 @@ module.exports = function defineCurrentUserHook(sails) {
|
|||
if (sessionAndUser) {
|
||||
const { session, user } = sessionAndUser;
|
||||
|
||||
if (user.language) {
|
||||
req.setLocale(user.language);
|
||||
}
|
||||
|
||||
Object.assign(req, {
|
||||
currentSession: session,
|
||||
currentUser: user,
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const fse = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
@ -27,21 +32,80 @@ class LocalFileManager {
|
|||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
read(filePathSegment) {
|
||||
async read(filePathSegment) {
|
||||
const filePath = buildPath(filePathSegment);
|
||||
const isFileExists = await fse.pathExists(filePath);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
if (!isFileExists) {
|
||||
throw new Error('File does not exist');
|
||||
}
|
||||
|
||||
return fs.createReadStream(filePath);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async getSizeInBytes(filePathSegment) {
|
||||
let result;
|
||||
try {
|
||||
result = await fs.promises.stat(buildPath(filePathSegment));
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result.size;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async rename(filePathSegment, nextFilePathSegment) {
|
||||
try {
|
||||
await fs.promises.rename(buildPath(filePathSegment), buildPath(nextFilePathSegment));
|
||||
} catch (error) {
|
||||
/* empty */
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async delete(filePathSegment) {
|
||||
try {
|
||||
await fs.promises.unlink(buildPath(filePathSegment));
|
||||
} catch (error) {
|
||||
/* empty */
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async listDir(dirPathSegment) {
|
||||
let dirents;
|
||||
try {
|
||||
dirents = await fs.promises.readdir(buildPath(dirPathSegment), {
|
||||
withFileTypes: true,
|
||||
});
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dirents.flatMap((dirent) => (dirent.isDirectory() ? dirent.name : []));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async renameDir(dirPathSegment, nextDirPathSegment) {
|
||||
try {
|
||||
await fs.promises.rename(buildPath(dirPathSegment), buildPath(nextDirPathSegment));
|
||||
} catch (error) {
|
||||
/* empty */
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async deleteDir(dirPathSegment) {
|
||||
await rimraf(buildPath(dirPathSegment));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async isExists(pathSegment) {
|
||||
return fse.pathExists(buildPath(pathSegment));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
buildUrl(filePathSegment) {
|
||||
return `${sails.config.custom.baseUrl}/${filePathSegment.replace(PATH_SEGMENT_TO_URL_REPLACE_REGEX, '')}`;
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const {
|
||||
CopyObjectCommand,
|
||||
DeleteObjectsCommand,
|
||||
DeleteObjectCommand,
|
||||
GetObjectCommand,
|
||||
HeadObjectCommand,
|
||||
ListObjectsV2Command,
|
||||
PutObjectCommand,
|
||||
} = require('@aws-sdk/client-s3');
|
||||
|
@ -20,7 +28,6 @@ class S3FileManager {
|
|||
});
|
||||
|
||||
await this.client.send(command);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -45,26 +52,144 @@ class S3FileManager {
|
|||
return result.Body;
|
||||
}
|
||||
|
||||
async deleteDir(dirPathSegment) {
|
||||
async getSizeInBytes(filePathSegment) {
|
||||
const headObjectCommand = new HeadObjectCommand({
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Key: filePathSegment,
|
||||
});
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await this.client.send(headObjectCommand);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result.ContentLength;
|
||||
}
|
||||
|
||||
async rename(filePathSegment, nextFilePathSegment) {
|
||||
const copyObjectCommand = new CopyObjectCommand({
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Key: nextFilePathSegment,
|
||||
CopySource: `${sails.config.custom.s3Bucket}/${filePathSegment}`,
|
||||
});
|
||||
|
||||
try {
|
||||
await this.client.send(copyObjectCommand);
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.delete(filePathSegment);
|
||||
}
|
||||
|
||||
async delete(filePathSegment) {
|
||||
const deleteObjectCommand = new DeleteObjectCommand({
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Key: filePathSegment,
|
||||
});
|
||||
|
||||
await this.client.send(deleteObjectCommand);
|
||||
}
|
||||
|
||||
async listDir(dirPathSegment, { ContinuationToken } = {}) {
|
||||
const listObjectsCommand = new ListObjectsV2Command({
|
||||
ContinuationToken,
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Prefix: `${dirPathSegment}/`,
|
||||
Delimiter: '/',
|
||||
});
|
||||
|
||||
const result = await this.client.send(listObjectsCommand);
|
||||
|
||||
if (!result.CommonPrefixes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dirnames = result.CommonPrefixes.map(({ Prefix }) =>
|
||||
Prefix.slice(dirPathSegment.length + 1, -1),
|
||||
);
|
||||
|
||||
if (result.IsTruncated) {
|
||||
const otherDirnames = await this.listDir(dirPathSegment, {
|
||||
ContinuationToken: result.NextContinuationToken,
|
||||
});
|
||||
|
||||
if (otherDirnames) {
|
||||
dirnames.push(...otherDirnames);
|
||||
}
|
||||
}
|
||||
|
||||
return dirnames;
|
||||
}
|
||||
|
||||
async renameDir(dirPathSegment, nextDirPathSegment, { ContinuationToken } = {}) {
|
||||
const listObjectsCommand = new ListObjectsV2Command({
|
||||
ContinuationToken,
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Prefix: dirPathSegment,
|
||||
});
|
||||
|
||||
const result = await this.client.send(listObjectsCommand);
|
||||
|
||||
if (!result.Contents || result.Contents.length === 0) {
|
||||
if (!result.Contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteObjectsCommand = new DeleteObjectsCommand({
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const { Key } of result.Contents) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.rename(Key, `${nextDirPathSegment}/${Key.substring(dirPathSegment.length + 1)}`);
|
||||
}
|
||||
|
||||
if (result.IsTruncated) {
|
||||
await this.renameDir(dirPathSegment, nextDirPathSegment, {
|
||||
ContinuationToken: result.NextContinuationToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async deleteDir(dirPathSegment, { ContinuationToken } = {}) {
|
||||
const listObjectsCommand = new ListObjectsV2Command({
|
||||
ContinuationToken,
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Delete: {
|
||||
Objects: result.Contents.map(({ Key }) => ({ Key })),
|
||||
},
|
||||
Prefix: dirPathSegment,
|
||||
});
|
||||
|
||||
await this.client.send(deleteObjectsCommand);
|
||||
const result = await this.client.send(listObjectsCommand);
|
||||
|
||||
if (!result.Contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.Contents.length > 0) {
|
||||
const deleteObjectsCommand = new DeleteObjectsCommand({
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Delete: {
|
||||
Objects: result.Contents.map(({ Key }) => ({ Key })),
|
||||
},
|
||||
});
|
||||
|
||||
await this.client.send(deleteObjectsCommand);
|
||||
}
|
||||
|
||||
if (result.IsTruncated) {
|
||||
await this.deleteDir(dirPathSegment, {
|
||||
ContinuationToken: result.NextContinuationToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async isExists(pathSegment) {
|
||||
const listObjectsCommand = new ListObjectsV2Command({
|
||||
Bucket: sails.config.custom.s3Bucket,
|
||||
Prefix: pathSegment,
|
||||
MaxKeys: 1,
|
||||
});
|
||||
|
||||
const result = await this.client.send(listObjectsCommand);
|
||||
return !!result.Contents && result.Contents.length === 1;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
const LocalFileManager = require('./LocalFileManager');
|
||||
const S3FileManager = require('./S3FileManager');
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* file-manager hook
|
||||
|
@ -9,11 +11,14 @@ const S3FileManager = require('./S3FileManager');
|
|||
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||
*/
|
||||
|
||||
const LocalFileManager = require('./LocalFileManager');
|
||||
const S3FileManager = require('./S3FileManager');
|
||||
|
||||
module.exports = function defineFileManagerHook(sails) {
|
||||
let instance = null;
|
||||
|
||||
const createInstance = () => {
|
||||
instance = sails.hooks.s3.isActive()
|
||||
instance = sails.hooks.s3.isEnabled()
|
||||
? new S3FileManager(sails.hooks.s3.getClient())
|
||||
: new LocalFileManager();
|
||||
};
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
const openidClient = require('openid-client');
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* oidc hook
|
||||
|
@ -8,6 +11,8 @@ const openidClient = require('openid-client');
|
|||
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||
*/
|
||||
|
||||
const openidClient = require('openid-client');
|
||||
|
||||
module.exports = function defineOidcHook(sails) {
|
||||
let client = null;
|
||||
|
||||
|
@ -16,18 +21,25 @@ module.exports = function defineOidcHook(sails) {
|
|||
* Runs when this Sails app loads/lifts.
|
||||
*/
|
||||
async initialize() {
|
||||
if (!this.isActive()) {
|
||||
if (!this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sails.log.info('Initializing custom hook (`oidc`)');
|
||||
},
|
||||
|
||||
// TODO: wait for initialization if called more than once
|
||||
async getClient() {
|
||||
if (client === null && this.isActive()) {
|
||||
if (this.isEnabled() && !this.isActive()) {
|
||||
sails.log.info('Initializing OIDC client');
|
||||
|
||||
const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
|
||||
let issuer;
|
||||
try {
|
||||
issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
|
||||
} catch (error) {
|
||||
sails.log.warn(`Error while initializing OIDC client: ${error}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadata = {
|
||||
client_id: sails.config.custom.oidcClientId,
|
||||
|
@ -47,8 +59,12 @@ module.exports = function defineOidcHook(sails) {
|
|||
return client;
|
||||
},
|
||||
|
||||
isEnabled() {
|
||||
return !!sails.config.custom.oidcIssuer;
|
||||
},
|
||||
|
||||
isActive() {
|
||||
return sails.config.custom.oidcIssuer !== undefined;
|
||||
return client !== null;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
55
server/api/hooks/query-methods/index.js
Normal file
55
server/api/hooks/query-methods/index.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* query-methods hook
|
||||
*
|
||||
* @description :: A hook definition. Extends Sails by adding shadow routes, implicit actions,
|
||||
* and/or initialization logic.
|
||||
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function defineQueryMethodsHook(sails) {
|
||||
const addQueryMethods = () => {
|
||||
const queryMethodsByModelName = fs.readdirSync(path.join(__dirname, 'models')).reduce(
|
||||
(result, filename) => ({
|
||||
...result,
|
||||
// eslint-disable-next-line global-require, import/no-dynamic-require
|
||||
[path.parse(filename).name]: require(path.join(__dirname, 'models', filename)),
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
_(sails.models).forEach((Model) => {
|
||||
const queryMethods = queryMethodsByModelName[Model.globalId];
|
||||
|
||||
if (queryMethods) {
|
||||
Object.assign(Model, {
|
||||
qm: queryMethods,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Runs when this Sails app loads/lifts.
|
||||
*/
|
||||
|
||||
async initialize() {
|
||||
sails.log.info('Initializing custom hook (`query-methods`)');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
sails.after('hook:orm:loaded', () => {
|
||||
addQueryMethods();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
39
server/api/hooks/query-methods/models/Action.js
Normal file
39
server/api/hooks/query-methods/models/Action.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const LIMIT = 50;
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => Action.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOne = (values) => Action.create({ ...values }).fetch();
|
||||
|
||||
const getByCardId = (cardId, { beforeId } = {}) => {
|
||||
const criteria = {
|
||||
cardId,
|
||||
};
|
||||
|
||||
if (beforeId) {
|
||||
criteria.id = {
|
||||
'<': beforeId,
|
||||
};
|
||||
}
|
||||
|
||||
return Action.find(criteria).sort('id DESC').limit(LIMIT);
|
||||
};
|
||||
|
||||
const update = (criteria, values) => Action.update(criteria).set(values).fetch();
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Action.destroy(criteria).fetch();
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByCardId,
|
||||
update,
|
||||
delete: delete_,
|
||||
};
|
231
server/api/hooks/query-methods/models/Attachment.js
Normal file
231
server/api/hooks/query-methods/models/Attachment.js
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
// TODO: refactor?
|
||||
|
||||
const defaultFind = (criteria) => Attachment.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => {
|
||||
const arrayOfFileValues = arrayOfValues.filter(({ type }) => type === Attachment.Types.FILE);
|
||||
|
||||
if (arrayOfFileValues.length > 0) {
|
||||
const arrayOfValuesByFileReferenceId = _.groupBy(arrayOfFileValues, 'data.fileReferenceId');
|
||||
|
||||
const fileReferenceIds = Object.keys(arrayOfValuesByFileReferenceId);
|
||||
|
||||
const fileReferenceIdsByTotal = Object.entries(arrayOfValuesByFileReferenceId).reduce(
|
||||
(result, [fileReferenceId, arrayOfValuesItem]) => ({
|
||||
...result,
|
||||
[arrayOfValuesItem.length]: [...(result[arrayOfValuesItem.length] || []), fileReferenceId],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
return sails.getDatastore().transaction(async (db) => {
|
||||
const queryValues = [];
|
||||
let query = `UPDATE file_reference SET total = total + CASE `;
|
||||
|
||||
Object.entries(fileReferenceIdsByTotal).forEach(([total, fileReferenceIdsItem]) => {
|
||||
const inValues = fileReferenceIdsItem.map((fileReferenceId) => {
|
||||
queryValues.push(fileReferenceId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
queryValues.push(total);
|
||||
query += `WHEN id IN (${inValues.join(', ')}) THEN $${queryValues.length}::int `;
|
||||
});
|
||||
|
||||
const inValues = fileReferenceIds.map((fileReferenceId) => {
|
||||
queryValues.push(fileReferenceId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
queryValues.push(new Date().toISOString());
|
||||
query += `END, updated_at = $${queryValues.length} WHERE id IN (${inValues.join(', ')}) AND total IS NOT NULL RETURNING id`;
|
||||
|
||||
const queryResult = await sails.sendNativeQuery(query, queryValues).usingConnection(db);
|
||||
const nextFileReferenceIds = sails.helpers.utils.mapRecords(queryResult.rows);
|
||||
|
||||
if (nextFileReferenceIds.length < fileReferenceIds.length) {
|
||||
const nextFileReferenceIdsSet = new Set(nextFileReferenceIds);
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
arrayOfValues = arrayOfValues.filter(
|
||||
(values) =>
|
||||
values.type !== Attachment.Types.FILE ||
|
||||
nextFileReferenceIdsSet.has(values.data.fileReferenceId),
|
||||
);
|
||||
}
|
||||
|
||||
return Attachment.createEach(arrayOfValues).fetch().usingConnection(db);
|
||||
});
|
||||
}
|
||||
|
||||
return Attachment.createEach(arrayOfValues).fetch();
|
||||
};
|
||||
|
||||
const createOne = (values) => {
|
||||
if (values.type === Attachment.Types.FILE) {
|
||||
const { fileReferenceId } = values.data;
|
||||
|
||||
return sails.getDatastore().transaction(async (db) => {
|
||||
const attachment = await Attachment.create({ ...values })
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
|
||||
const queryResult = await sails
|
||||
.sendNativeQuery(
|
||||
'UPDATE file_reference SET total = total + 1, updated_at = $1 WHERE id = $2 AND total IS NOT NULL',
|
||||
[new Date().toISOString(), fileReferenceId],
|
||||
)
|
||||
.usingConnection(db);
|
||||
|
||||
if (queryResult.rowCount === 0) {
|
||||
throw 'fileReferenceNotFound';
|
||||
}
|
||||
|
||||
return attachment;
|
||||
});
|
||||
}
|
||||
|
||||
return Attachment.create({ ...values }).fetch();
|
||||
};
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByCardId = (cardId) =>
|
||||
defaultFind({
|
||||
cardId,
|
||||
});
|
||||
|
||||
const getByCardIds = (cardIds) =>
|
||||
defaultFind({
|
||||
cardId: cardIds,
|
||||
});
|
||||
|
||||
const getOneById = (id, { cardId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (cardId) {
|
||||
criteria.cardId = cardId;
|
||||
}
|
||||
|
||||
return Attachment.findOne(criteria);
|
||||
};
|
||||
|
||||
const update = (criteria, values) => Attachment.update(criteria).set(values).fetch();
|
||||
|
||||
const updateOne = (criteria, values) => Attachment.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) =>
|
||||
sails.getDatastore().transaction(async (db) => {
|
||||
const attachments = await Attachment.destroy(criteria).fetch().usingConnection(db);
|
||||
const fileAttachments = attachments.filter(({ type }) => type === Attachment.Types.FILE);
|
||||
|
||||
let fileReferences = [];
|
||||
if (fileAttachments.length > 0) {
|
||||
const attachmentsByFileReferenceId = _.groupBy(fileAttachments, 'data.fileReferenceId');
|
||||
|
||||
const fileReferenceIdsByTotal = Object.entries(attachmentsByFileReferenceId).reduce(
|
||||
(result, [fileReferenceId, attachmentsItem]) => ({
|
||||
...result,
|
||||
[attachmentsItem.length]: [...(result[attachmentsItem.length] || []), fileReferenceId],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
const queryValues = [];
|
||||
let query = 'UPDATE file_reference SET total = CASE WHEN total = CASE ';
|
||||
|
||||
Object.entries(fileReferenceIdsByTotal).forEach(([total, fileReferenceIds]) => {
|
||||
const inValues = fileReferenceIds.map((fileReferenceId) => {
|
||||
queryValues.push(fileReferenceId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
queryValues.push(total);
|
||||
query += `WHEN id IN (${inValues.join(', ')}) THEN $${queryValues.length}::int `;
|
||||
});
|
||||
|
||||
query += 'END THEN NULL ELSE total - CASE ';
|
||||
|
||||
Object.entries(fileReferenceIdsByTotal).forEach(([total, fileReferenceIds]) => {
|
||||
const inValues = fileReferenceIds.map((fileReferenceId) => {
|
||||
queryValues.push(fileReferenceId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
queryValues.push(total);
|
||||
query += `WHEN id IN (${inValues.join(', ')}) THEN $${queryValues.length}::int `;
|
||||
});
|
||||
|
||||
const inValues = Object.keys(attachmentsByFileReferenceId).map((fileReferenceId) => {
|
||||
queryValues.push(fileReferenceId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
queryValues.push(new Date().toISOString());
|
||||
query += `END END, updated_at = $${queryValues.length} WHERE id IN (${inValues.join(', ')}) AND total IS NOT NULL RETURNING id, total`;
|
||||
|
||||
const queryResult = await sails.sendNativeQuery(query, queryValues).usingConnection(db);
|
||||
fileReferences = queryResult.rows;
|
||||
}
|
||||
|
||||
return {
|
||||
attachments,
|
||||
fileReferences,
|
||||
};
|
||||
});
|
||||
|
||||
const deleteOne = async (criteria, { isFile } = {}) => {
|
||||
let fileReference = null;
|
||||
|
||||
if (isFile) {
|
||||
return sails.getDatastore().transaction(async (db) => {
|
||||
const attachment = await Attachment.destroyOne(criteria).usingConnection(db);
|
||||
|
||||
if (attachment.type === Attachment.Types.FILE) {
|
||||
const queryResult = await sails
|
||||
.sendNativeQuery(
|
||||
'UPDATE file_reference SET total = CASE WHEN total > 1 THEN total - 1 END, updated_at = $1 WHERE id = $2 RETURNING id, total',
|
||||
[new Date().toISOString(), attachment.data.fileReferenceId],
|
||||
)
|
||||
.usingConnection(db);
|
||||
|
||||
[fileReference] = queryResult.rows;
|
||||
}
|
||||
|
||||
return {
|
||||
attachment,
|
||||
fileReference,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const attachment = await Attachment.destroyOne(criteria);
|
||||
|
||||
return {
|
||||
attachment,
|
||||
fileReference,
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getByCardIds,
|
||||
getOneById,
|
||||
update,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
49
server/api/hooks/query-methods/models/BackgroundImage.js
Normal file
49
server/api/hooks/query-methods/models/BackgroundImage.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => BackgroundImage.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => BackgroundImage.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByProjectId = (projectId) =>
|
||||
defaultFind({
|
||||
projectId,
|
||||
});
|
||||
|
||||
const getByProjectIds = (projectIds) =>
|
||||
defaultFind({
|
||||
projectId: projectIds,
|
||||
});
|
||||
|
||||
const getOneById = (id, { projectId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (projectId) {
|
||||
criteria.projectId = projectId;
|
||||
}
|
||||
|
||||
return BackgroundImage.findOne(criteria);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => BackgroundImage.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => BackgroundImage.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByProjectId,
|
||||
getByProjectIds,
|
||||
getOneById,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => BaseCustomFieldGroup.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => BaseCustomFieldGroup.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByProjectId = (projectId) =>
|
||||
defaultFind({
|
||||
projectId,
|
||||
});
|
||||
|
||||
const getByProjectIds = (projectIds) =>
|
||||
defaultFind({
|
||||
projectId: projectIds,
|
||||
});
|
||||
|
||||
const getOneById = (id, { projectId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (projectId) {
|
||||
criteria.projectId = projectId;
|
||||
}
|
||||
|
||||
return BaseCustomFieldGroup.findOne(criteria);
|
||||
};
|
||||
|
||||
const updateOne = (criteria, values) => BaseCustomFieldGroup.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => BaseCustomFieldGroup.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => BaseCustomFieldGroup.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByProjectId,
|
||||
getByProjectIds,
|
||||
getOneById,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
91
server/api/hooks/query-methods/models/Board.js
Normal file
91
server/api/hooks/query-methods/models/Board.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id' } = {}) => Board.find(criteria).sort(sort);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values, { user } = {}) =>
|
||||
sails.getDatastore().transaction(async (db) => {
|
||||
const board = await Board.create({ ...values })
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
|
||||
const boardMembership = await BoardMembership.create({
|
||||
projectId: board.projectId,
|
||||
boardId: board.id,
|
||||
userId: user.id,
|
||||
role: BoardMembership.Roles.EDITOR,
|
||||
})
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
|
||||
const lists = await List.createEach(
|
||||
[List.Types.ARCHIVE, List.Types.TRASH].map((type) => ({
|
||||
type,
|
||||
boardId: board.id,
|
||||
})),
|
||||
)
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
|
||||
return { board, boardMembership, lists };
|
||||
});
|
||||
|
||||
const getByIds = (ids, { exceptProjectIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
id: ids,
|
||||
};
|
||||
|
||||
if (exceptProjectIdOrIds) {
|
||||
criteria.projectId = {
|
||||
'!=': exceptProjectIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getByProjectId = (projectId, { exceptIdOrIds, sort = ['position', 'id'] } = {}) => {
|
||||
const criteria = {
|
||||
projectId,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria, { sort });
|
||||
};
|
||||
|
||||
const getByProjectIds = (projectIds, { sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
projectId: projectIds,
|
||||
},
|
||||
{ sort },
|
||||
);
|
||||
|
||||
const getOneById = (id) => Board.findOne(id);
|
||||
|
||||
const updateOne = (criteria, values) => Board.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Board.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => Board.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByProjectId,
|
||||
getByProjectIds,
|
||||
getOneById,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
100
server/api/hooks/query-methods/models/BoardMembership.js
Normal file
100
server/api/hooks/query-methods/models/BoardMembership.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => BoardMembership.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => BoardMembership.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByProjectId = (projectId) =>
|
||||
defaultFind({
|
||||
projectId,
|
||||
});
|
||||
|
||||
const getByProjectIdAndUserId = (projectId, userId) =>
|
||||
defaultFind({
|
||||
projectId,
|
||||
userId,
|
||||
});
|
||||
|
||||
const getByProjectIds = (projectIds) =>
|
||||
defaultFind({
|
||||
projectId: projectIds,
|
||||
});
|
||||
|
||||
const getByBoardId = (boardId) =>
|
||||
defaultFind({
|
||||
boardId,
|
||||
});
|
||||
|
||||
const getByBoardIds = (boardIds, { exceptUserIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
boardId: boardIds,
|
||||
};
|
||||
|
||||
if (exceptUserIdOrIds) {
|
||||
criteria.userId = {
|
||||
'!=': exceptUserIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getByBoardIdsAndUserId = (boardIds, userId) =>
|
||||
defaultFind({
|
||||
userId,
|
||||
boardId: boardIds,
|
||||
});
|
||||
|
||||
const getByUserId = (userId, { exceptProjectIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
userId,
|
||||
};
|
||||
|
||||
if (exceptProjectIdOrIds) {
|
||||
criteria.projectId = {
|
||||
'!=': exceptProjectIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getOneById = (id) => BoardMembership.findOne(id);
|
||||
|
||||
const getOneByBoardIdAndUserId = (boardId, userId) =>
|
||||
BoardMembership.findOne({
|
||||
boardId,
|
||||
userId,
|
||||
});
|
||||
|
||||
const updateOne = async (criteria, values) =>
|
||||
BoardMembership.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => BoardMembership.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => BoardMembership.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByProjectId,
|
||||
getByProjectIdAndUserId,
|
||||
getByProjectIds,
|
||||
getByBoardId,
|
||||
getByBoardIds,
|
||||
getByBoardIdsAndUserId,
|
||||
getByUserId,
|
||||
getOneById,
|
||||
getOneByBoardIdAndUserId,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
46
server/api/hooks/query-methods/models/BoardSubscription.js
Normal file
46
server/api/hooks/query-methods/models/BoardSubscription.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => BoardSubscription.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => BoardSubscription.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByBoardId = (boardId, { exceptUserIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
boardId,
|
||||
};
|
||||
|
||||
if (exceptUserIdOrIds) {
|
||||
criteria.userId = {
|
||||
'!=': exceptUserIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getOneByBoardIdAndUserId = (boardId, userId) =>
|
||||
BoardSubscription.findOne({
|
||||
boardId,
|
||||
userId,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => BoardSubscription.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => BoardSubscription.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByBoardId,
|
||||
getOneByBoardIdAndUserId,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
224
server/api/hooks/query-methods/models/Card.js
Normal file
224
server/api/hooks/query-methods/models/Card.js
Normal file
|
@ -0,0 +1,224 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const LIMIT = 50;
|
||||
|
||||
const SEARCH_PARTS_REGEX = /[ ,;]+/;
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id', limit } = {}) =>
|
||||
Card.find(criteria).sort(sort).limit(limit);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const getIdsByEndlessListId = async (
|
||||
listId,
|
||||
{ before, search, filterUserIds, filterLabelIds } = {},
|
||||
) => {
|
||||
if (filterUserIds && filterUserIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (filterLabelIds && filterLabelIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const queryValues = [];
|
||||
let query = 'SELECT DISTINCT card.id FROM card';
|
||||
|
||||
if (filterUserIds) {
|
||||
query += ' JOIN card_membership ON card.id = card_membership.card_id';
|
||||
}
|
||||
|
||||
if (filterLabelIds) {
|
||||
query += ' JOIN card_label ON card.id = card_label.card_id';
|
||||
}
|
||||
|
||||
queryValues.push(listId);
|
||||
query += ` WHERE card.list_id = $${queryValues.length}`;
|
||||
|
||||
if (before) {
|
||||
queryValues.push(before.listChangedAt);
|
||||
query += ` AND (card.list_changed_at < $${queryValues.length} OR (card.list_changed_at = $${queryValues.length}`;
|
||||
|
||||
queryValues.push(before.id);
|
||||
query += ` AND card.id < $${queryValues.length}))`;
|
||||
}
|
||||
|
||||
if (search) {
|
||||
if (search.startsWith('/')) {
|
||||
queryValues.push(search.substring(1));
|
||||
query += ` AND (card.name ~* $${queryValues.length} OR card.description ~* $${queryValues.length})`;
|
||||
} else {
|
||||
const searchParts = search.split(SEARCH_PARTS_REGEX).flatMap((searchPart) => {
|
||||
if (!searchPart) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return searchPart.toLowerCase();
|
||||
});
|
||||
|
||||
if (searchParts.length > 0) {
|
||||
let ilikeValues = searchParts.map((searchPart) => {
|
||||
queryValues.push(searchPart);
|
||||
return `'%' || $${queryValues.length} || '%'`;
|
||||
});
|
||||
|
||||
query += ` AND ((card.name ILIKE ALL(ARRAY[${ilikeValues.join(', ')}]))`;
|
||||
|
||||
ilikeValues = searchParts.map((searchPart) => {
|
||||
queryValues.push(searchPart);
|
||||
return `'%' || $${queryValues.length} || '%'`;
|
||||
});
|
||||
|
||||
query += ` OR (card.description ILIKE ALL(ARRAY[${ilikeValues.join(', ')}])))`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filterUserIds) {
|
||||
const inValues = filterUserIds.map((filterUserId) => {
|
||||
queryValues.push(filterUserId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
query += ` AND card_membership.user_id IN (${inValues.join(', ')})`;
|
||||
}
|
||||
|
||||
if (filterLabelIds) {
|
||||
const inValues = filterLabelIds.map((filterLabelId) => {
|
||||
queryValues.push(filterLabelId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
query += ` AND card_label.label_id IN (${inValues.join(', ')})`;
|
||||
}
|
||||
|
||||
query += ` LIMIT ${LIMIT}`;
|
||||
|
||||
let queryResult;
|
||||
try {
|
||||
queryResult = await sails.sendNativeQuery(query, queryValues);
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === 'E_QUERY_FAILED' &&
|
||||
error.message.includes('Query failed: invalid regular expression')
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return sails.helpers.utils.mapRecords(queryResult.rows);
|
||||
};
|
||||
|
||||
const createOne = (values) => Card.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByBoardId = (boardId) =>
|
||||
defaultFind({
|
||||
boardId,
|
||||
});
|
||||
|
||||
const getByListId = async (listId, { exceptIdOrIds, sort = ['position', 'id'] } = {}) => {
|
||||
const criteria = {
|
||||
listId,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria, { sort });
|
||||
};
|
||||
|
||||
const getByEndlessListId = async (listId, { before, search, filterUserIds, filterLabelIds }) => {
|
||||
const criteria = {};
|
||||
|
||||
const options = {
|
||||
sort: ['listChangedAt DESC', 'id DESC'],
|
||||
};
|
||||
|
||||
if (search || filterUserIds || filterLabelIds) {
|
||||
criteria.id = await getIdsByEndlessListId(listId, {
|
||||
before,
|
||||
search,
|
||||
filterUserIds,
|
||||
filterLabelIds,
|
||||
});
|
||||
} else {
|
||||
criteria.and = [{ listId }];
|
||||
|
||||
if (before) {
|
||||
criteria.and.push({
|
||||
or: [
|
||||
{
|
||||
listChangedAt: {
|
||||
'<': before.listChangedAt,
|
||||
},
|
||||
},
|
||||
{
|
||||
listChangedAt: before.listChangedAt,
|
||||
id: {
|
||||
'<': before.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
options.limit = LIMIT;
|
||||
}
|
||||
|
||||
return defaultFind(criteria, options);
|
||||
};
|
||||
|
||||
const getByListIds = async (listIds, { sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
listId: listIds,
|
||||
},
|
||||
{ sort },
|
||||
);
|
||||
|
||||
const getOneById = (id, { listId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (listId) {
|
||||
criteria.listId = listId;
|
||||
}
|
||||
|
||||
return Card.findOne(criteria);
|
||||
};
|
||||
|
||||
const update = (criteria, values) => Card.update(criteria).set(values).fetch();
|
||||
|
||||
const updateOne = (criteria, values) => Card.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Card.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => Card.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
getIdsByEndlessListId,
|
||||
|
||||
createOne,
|
||||
getByIds,
|
||||
getByBoardId,
|
||||
getByListId,
|
||||
getByEndlessListId,
|
||||
getByListIds,
|
||||
getOneById,
|
||||
update,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
46
server/api/hooks/query-methods/models/CardLabel.js
Normal file
46
server/api/hooks/query-methods/models/CardLabel.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => CardLabel.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => CardLabel.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOne = (values) => CardLabel.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByCardId = (cardId) =>
|
||||
defaultFind({
|
||||
cardId,
|
||||
});
|
||||
|
||||
const getByCardIds = (cardIds) =>
|
||||
defaultFind({
|
||||
cardId: cardIds,
|
||||
});
|
||||
|
||||
const getOneByCardIdAndLabelId = (cardId, labelId) =>
|
||||
CardLabel.findOne({
|
||||
cardId,
|
||||
labelId,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => CardLabel.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => CardLabel.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getByCardIds,
|
||||
getOneByCardIdAndLabelId,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
46
server/api/hooks/query-methods/models/CardMembership.js
Normal file
46
server/api/hooks/query-methods/models/CardMembership.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => CardMembership.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => CardMembership.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOne = (values) => CardMembership.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByCardId = (cardId) =>
|
||||
defaultFind({
|
||||
cardId,
|
||||
});
|
||||
|
||||
const getByCardIds = (cardIds) =>
|
||||
defaultFind({
|
||||
cardId: cardIds,
|
||||
});
|
||||
|
||||
const getOneByCardIdAndUserId = (cardId, userId) =>
|
||||
CardMembership.findOne({
|
||||
cardId,
|
||||
userId,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => CardMembership.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => CardMembership.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getByCardIds,
|
||||
getOneByCardIdAndUserId,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
53
server/api/hooks/query-methods/models/CardSubscription.js
Normal file
53
server/api/hooks/query-methods/models/CardSubscription.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => CardSubscription.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => CardSubscription.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByCardId = (cardId, { exceptUserIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
cardId,
|
||||
};
|
||||
|
||||
if (exceptUserIdOrIds) {
|
||||
criteria.userId = {
|
||||
'!=': exceptUserIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getByCardIdsAndUserId = (cardIds, userId) =>
|
||||
defaultFind({
|
||||
userId,
|
||||
cardId: cardIds,
|
||||
});
|
||||
|
||||
const getOneByCardIdAndUserId = (cardId, userId) =>
|
||||
CardSubscription.findOne({
|
||||
cardId,
|
||||
userId,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => CardSubscription.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => CardSubscription.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getByCardIdsAndUserId,
|
||||
getOneByCardIdAndUserId,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
51
server/api/hooks/query-methods/models/Comment.js
Normal file
51
server/api/hooks/query-methods/models/Comment.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const LIMIT = 50;
|
||||
|
||||
const defaultFind = (criteria, { limit } = {}) =>
|
||||
Comment.find(criteria).sort('id DESC').limit(limit);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => Comment.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByCardId = (cardId, { beforeId } = {}) => {
|
||||
const criteria = {
|
||||
cardId,
|
||||
};
|
||||
|
||||
if (beforeId) {
|
||||
criteria.id = {
|
||||
'<': beforeId,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria, { limit: LIMIT });
|
||||
};
|
||||
|
||||
const getOneById = (id) => Comment.findOne(id);
|
||||
|
||||
const update = (criteria, values) => Comment.update(criteria).set(values).fetch();
|
||||
|
||||
const updateOne = (criteria, values) => Comment.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Comment.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => Comment.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getOneById,
|
||||
update,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
87
server/api/hooks/query-methods/models/CustomField.js
Normal file
87
server/api/hooks/query-methods/models/CustomField.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria, { exceptIdOrIds, sort = 'id' } = {}) => {
|
||||
if (exceptIdOrIds) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return CustomField.find(criteria).sort(sort);
|
||||
};
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => CustomField.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOne = (values) => CustomField.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByBaseCustomFieldGroupId = (
|
||||
baseCustomFieldGroupId,
|
||||
{ exceptIdOrIds, sort = ['position', 'id'] } = {},
|
||||
) =>
|
||||
defaultFind(
|
||||
{
|
||||
baseCustomFieldGroupId,
|
||||
},
|
||||
{ exceptIdOrIds, sort },
|
||||
);
|
||||
|
||||
const getByBaseCustomFieldGroupIds = (
|
||||
baseCustomFieldGroupIds,
|
||||
{ sort = ['position', 'id'] } = {},
|
||||
) =>
|
||||
defaultFind(
|
||||
{
|
||||
baseCustomFieldGroupId: baseCustomFieldGroupIds,
|
||||
},
|
||||
{ sort },
|
||||
);
|
||||
|
||||
const getByCustomFieldGroupId = async (
|
||||
customFieldGroupId,
|
||||
{ exceptIdOrIds, sort = ['position', 'id'] } = {},
|
||||
) =>
|
||||
defaultFind(
|
||||
{
|
||||
customFieldGroupId,
|
||||
},
|
||||
{ exceptIdOrIds, sort },
|
||||
);
|
||||
|
||||
const getByCustomFieldGroupIds = async (customFieldGroupIds, { sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
customFieldGroupId: customFieldGroupIds,
|
||||
},
|
||||
{ sort },
|
||||
);
|
||||
|
||||
const getOneById = (id) => CustomField.findOne(id);
|
||||
|
||||
const updateOne = (criteria, values) => CustomField.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => CustomField.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => CustomField.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByIds,
|
||||
getByBaseCustomFieldGroupId,
|
||||
getByBaseCustomFieldGroupIds,
|
||||
getByCustomFieldGroupId,
|
||||
getByCustomFieldGroupIds,
|
||||
getOneById,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
72
server/api/hooks/query-methods/models/CustomFieldGroup.js
Normal file
72
server/api/hooks/query-methods/models/CustomFieldGroup.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria, { exceptIdOrIds, sort = 'id' } = {}) => {
|
||||
if (exceptIdOrIds) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return CustomFieldGroup.find(criteria).sort(sort);
|
||||
};
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => CustomFieldGroup.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOne = (values) => CustomFieldGroup.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByBoardId = (boardId, { exceptIdOrIds, sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
boardId,
|
||||
},
|
||||
{ exceptIdOrIds, sort },
|
||||
);
|
||||
|
||||
const getByCardId = (cardId, { exceptIdOrIds, sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
cardId,
|
||||
},
|
||||
{ exceptIdOrIds, sort },
|
||||
);
|
||||
|
||||
const getByCardIds = (cardIds, { sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
cardId: cardIds,
|
||||
},
|
||||
{ sort },
|
||||
);
|
||||
|
||||
const getOneById = (id) => CustomFieldGroup.findOne(id);
|
||||
|
||||
const update = (criteria, values) => CustomFieldGroup.update(criteria).set(values).fetch();
|
||||
|
||||
const updateOne = (criteria, values) => CustomFieldGroup.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => CustomFieldGroup.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => CustomFieldGroup.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByBoardId,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getByCardIds,
|
||||
getOneById,
|
||||
update,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
95
server/api/hooks/query-methods/models/CustomFieldValue.js
Normal file
95
server/api/hooks/query-methods/models/CustomFieldValue.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => CustomFieldValue.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => CustomFieldValue.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOrUpdateOne = async (values) => {
|
||||
const query = `
|
||||
INSERT INTO custom_field_value (card_id, custom_field_group_id, custom_field_id, content, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (card_id, custom_field_group_id, custom_field_id)
|
||||
DO UPDATE SET content = EXCLUDED.content, updated_at = EXCLUDED.created_at
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
const queryResult = await sails.sendNativeQuery(query, [
|
||||
values.cardId,
|
||||
values.customFieldGroupId,
|
||||
values.customFieldId,
|
||||
values.content,
|
||||
new Date().toISOString(),
|
||||
]);
|
||||
|
||||
const [customFieldValue] = queryResult.rows;
|
||||
|
||||
return {
|
||||
id: customFieldValue.id,
|
||||
cardId: customFieldValue.card_id,
|
||||
customFieldGroupId: customFieldValue.custom_field_group_id,
|
||||
customFieldId: customFieldValue.custom_field_id,
|
||||
content: customFieldValue.content,
|
||||
createdAt: customFieldValue.created_at,
|
||||
updatedAt: customFieldValue.updated_at,
|
||||
};
|
||||
};
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByCardId = (cardId, { customFieldGroupIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
cardId,
|
||||
};
|
||||
|
||||
if (customFieldGroupIdOrIds) {
|
||||
criteria.customFieldGroupId = customFieldGroupIdOrIds;
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getByCardIds = (cardIds) =>
|
||||
defaultFind({
|
||||
cardId: cardIds,
|
||||
});
|
||||
|
||||
const getByCustomFieldGroupId = (customFieldGroupId) =>
|
||||
defaultFind({
|
||||
customFieldGroupId,
|
||||
});
|
||||
|
||||
const getOneByCardIdAndCustomFieldGroupIdAndCustomFieldId = (
|
||||
cardId,
|
||||
customFieldGroupId,
|
||||
customFieldId,
|
||||
) =>
|
||||
CustomFieldValue.findOne({
|
||||
cardId,
|
||||
customFieldGroupId,
|
||||
customFieldId,
|
||||
});
|
||||
|
||||
const updateOne = (criteria, values) => CustomFieldValue.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => CustomFieldValue.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => CustomFieldValue.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOrUpdateOne,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getByCardIds,
|
||||
getByCustomFieldGroupId,
|
||||
getOneByCardIdAndCustomFieldGroupIdAndCustomFieldId,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const createOne = (values) => IdentityProviderUser.create({ ...values }).fetch();
|
||||
|
||||
const getOneByIssuerAndSub = (issuer, sub) =>
|
||||
IdentityProviderUser.findOne({
|
||||
issuer,
|
||||
sub,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => IdentityProviderUser.destroy(criteria).fetch();
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getOneByIssuerAndSub,
|
||||
delete: delete_,
|
||||
};
|
55
server/api/hooks/query-methods/models/Label.js
Normal file
55
server/api/hooks/query-methods/models/Label.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id' } = {}) => Label.find(criteria).sort(sort);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => Label.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByBoardId = (boardId, { exceptIdOrIds, sort = ['position', 'id'] } = {}) => {
|
||||
const criteria = {
|
||||
boardId,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria, { sort });
|
||||
};
|
||||
|
||||
const getOneById = (id, { boardId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (boardId) {
|
||||
criteria.boardId = boardId;
|
||||
}
|
||||
|
||||
return Label.findOne(criteria);
|
||||
};
|
||||
|
||||
const updateOne = (criteria, values) => Label.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Label.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => Label.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByBoardId,
|
||||
getOneById,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
66
server/api/hooks/query-methods/models/List.js
Normal file
66
server/api/hooks/query-methods/models/List.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id' } = {}) => List.find(criteria).sort(sort);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => List.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByBoardId = (boardId, { exceptIdOrIds, typeOrTypes, sort = ['position', 'id'] } = {}) => {
|
||||
const criteria = {
|
||||
boardId,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeOrTypes) {
|
||||
criteria.type = typeOrTypes;
|
||||
}
|
||||
|
||||
return defaultFind(criteria, { sort });
|
||||
};
|
||||
|
||||
const getOneById = (id, { boardId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (boardId) {
|
||||
criteria.boardId = boardId;
|
||||
}
|
||||
|
||||
return List.findOne(criteria);
|
||||
};
|
||||
|
||||
const getOneTrashByBoardId = (boardId) =>
|
||||
List.findOne({
|
||||
boardId,
|
||||
type: List.Types.TRASH,
|
||||
});
|
||||
|
||||
const updateOne = (criteria, values) => List.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => List.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => List.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByBoardId,
|
||||
getOneById,
|
||||
getOneTrashByBoardId,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
76
server/api/hooks/query-methods/models/Notification.js
Normal file
76
server/api/hooks/query-methods/models/Notification.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const LIMIT = 100;
|
||||
|
||||
const defaultFind = (criteria) => Notification.find(criteria).sort('id DESC');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => {
|
||||
if (values.userId) {
|
||||
return sails.getDatastore().transaction(async (db) => {
|
||||
const notification = await Notification.create({ ...values })
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
|
||||
const query = `
|
||||
WITH exceeded_notification AS (
|
||||
SELECT id
|
||||
FROM notification
|
||||
WHERE user_id = $1 AND is_read = FALSE
|
||||
ORDER BY id DESC
|
||||
OFFSET $2
|
||||
)
|
||||
UPDATE notification
|
||||
SET is_read = TRUE
|
||||
WHERE id in (SELECT id FROM exceeded_notification)
|
||||
`;
|
||||
|
||||
await sails.sendNativeQuery(query, [values.userId, LIMIT]).usingConnection(db);
|
||||
|
||||
return notification;
|
||||
});
|
||||
}
|
||||
|
||||
return Notification.create({ ...values }).fetch();
|
||||
};
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getUnreadByUserId = (userId) =>
|
||||
defaultFind({
|
||||
userId,
|
||||
isRead: false,
|
||||
});
|
||||
|
||||
const getOneById = (id, { userId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (userId) {
|
||||
criteria.userId = userId;
|
||||
}
|
||||
|
||||
return Notification.findOne(criteria);
|
||||
};
|
||||
|
||||
const update = (criteria, values) => Notification.update(criteria).set(values).fetch();
|
||||
|
||||
const updateOne = (criteria, values) => Notification.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Notification.destroy(criteria).fetch();
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getUnreadByUserId,
|
||||
getOneById,
|
||||
update,
|
||||
updateOne,
|
||||
delete: delete_,
|
||||
};
|
45
server/api/hooks/query-methods/models/NotificationService.js
Normal file
45
server/api/hooks/query-methods/models/NotificationService.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => NotificationService.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => NotificationService.create({ ...values }).fetch();
|
||||
|
||||
const getByUserId = (userId) =>
|
||||
defaultFind({
|
||||
userId,
|
||||
});
|
||||
|
||||
const getByBoardId = (boardId) =>
|
||||
defaultFind({
|
||||
boardId,
|
||||
});
|
||||
|
||||
const getByBoardIds = (boardIds) =>
|
||||
defaultFind({
|
||||
boardId: boardIds,
|
||||
});
|
||||
|
||||
const getOneById = (id) => NotificationService.findOne(id);
|
||||
|
||||
const updateOne = (criteria, values) => NotificationService.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => NotificationService.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => NotificationService.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByUserId,
|
||||
getByBoardId,
|
||||
getByBoardIds,
|
||||
getOneById,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
67
server/api/hooks/query-methods/models/Project.js
Normal file
67
server/api/hooks/query-methods/models/Project.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => Project.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values, { user } = {}) =>
|
||||
sails.getDatastore().transaction(async (db) => {
|
||||
let project = await Project.create({ ...values })
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
|
||||
const projectManager = await ProjectManager.create({
|
||||
projectId: project.id,
|
||||
userId: user.id,
|
||||
})
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
|
||||
if (values.type === Project.Types.PRIVATE) {
|
||||
project = await Project.updateOne(project.id)
|
||||
.set({
|
||||
ownerProjectManagerId: projectManager.id,
|
||||
})
|
||||
.usingConnection(db);
|
||||
}
|
||||
|
||||
return { project, projectManager };
|
||||
});
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getShared = ({ exceptIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
ownerProjectManagerId: null,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getOneById = (id) => Project.findOne(id);
|
||||
|
||||
const updateOne = (criteria, values) => Project.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Project.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => Project.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getShared,
|
||||
getOneById,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
35
server/api/hooks/query-methods/models/ProjectFavorite.js
Normal file
35
server/api/hooks/query-methods/models/ProjectFavorite.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => ProjectFavorite.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => ProjectFavorite.create({ ...values }).fetch();
|
||||
|
||||
const getByProjectIdsAndUserId = (projectIds, userId) =>
|
||||
defaultFind({
|
||||
userId,
|
||||
projectId: projectIds,
|
||||
});
|
||||
|
||||
const getOneByProjectIdAndUserId = (projectId, userId) =>
|
||||
ProjectFavorite.findOne({
|
||||
projectId,
|
||||
userId,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => ProjectFavorite.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => ProjectFavorite.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByProjectIdsAndUserId,
|
||||
getOneByProjectIdAndUserId,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
80
server/api/hooks/query-methods/models/ProjectManager.js
Normal file
80
server/api/hooks/query-methods/models/ProjectManager.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => ProjectManager.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => ProjectManager.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByProjectId = (projectId, { exceptIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
projectId,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getByProjectIds = (projectIds, { exceptUserIdOrIds } = {}) => {
|
||||
const criteria = {
|
||||
projectId: projectIds,
|
||||
};
|
||||
|
||||
if (exceptUserIdOrIds) {
|
||||
criteria.userId = {
|
||||
'!=': exceptUserIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria);
|
||||
};
|
||||
|
||||
const getByUserId = (userId) =>
|
||||
defaultFind({
|
||||
userId,
|
||||
});
|
||||
|
||||
const getOneById = (id, { projectId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (projectId) {
|
||||
criteria.projectId = projectId;
|
||||
}
|
||||
|
||||
return ProjectManager.findOne(criteria);
|
||||
};
|
||||
|
||||
const getOneByProjectIdAndUserId = (projectId, userId) =>
|
||||
ProjectManager.findOne({
|
||||
projectId,
|
||||
userId,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => ProjectManager.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => ProjectManager.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getByProjectId,
|
||||
getByProjectIds,
|
||||
getByUserId,
|
||||
getOneById,
|
||||
getOneByProjectIdAndUserId,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
30
server/api/hooks/query-methods/models/Session.js
Normal file
30
server/api/hooks/query-methods/models/Session.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const createOne = (values) => Session.create({ ...values }).fetch();
|
||||
|
||||
const getOneUndeletedByAccessToken = (accessToken) =>
|
||||
Session.findOne({
|
||||
accessToken,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Session.destroy(criteria).fetch();
|
||||
|
||||
const deleteOneById = (id) =>
|
||||
Session.updateOne({
|
||||
id,
|
||||
deletedAt: null,
|
||||
}).set({
|
||||
deletedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getOneUndeletedByAccessToken,
|
||||
deleteOneById,
|
||||
delete: delete_,
|
||||
};
|
71
server/api/hooks/query-methods/models/Task.js
Normal file
71
server/api/hooks/query-methods/models/Task.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id', limit } = {}) =>
|
||||
Task.find(criteria).sort(sort).limit(limit);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => Task.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOne = (values) => Task.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByTaskListId = async (taskListId, { exceptIdOrIds, sort = ['position', 'id'] } = {}) => {
|
||||
const criteria = {
|
||||
taskListId,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria, { sort });
|
||||
};
|
||||
|
||||
const getByTaskListIds = async (taskListIds, { sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
taskListId: taskListIds,
|
||||
},
|
||||
{ sort },
|
||||
);
|
||||
|
||||
const getOneById = (id, { taskListId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (taskListId) {
|
||||
criteria.taskListId = taskListId;
|
||||
}
|
||||
|
||||
return Task.findOne(criteria);
|
||||
};
|
||||
|
||||
const update = (criteria, values) => Task.update(criteria).set(values).fetch();
|
||||
|
||||
const updateOne = (criteria, values) => Task.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => Task.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => Task.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByIds,
|
||||
getByTaskListId,
|
||||
getByTaskListIds,
|
||||
getOneById,
|
||||
update,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
67
server/api/hooks/query-methods/models/TaskList.js
Normal file
67
server/api/hooks/query-methods/models/TaskList.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id' } = {}) => TaskList.find(criteria).sort(sort);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) => TaskList.createEach(arrayOfValues).fetch();
|
||||
|
||||
const createOne = (values) => TaskList.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getByCardId = (cardId, { exceptIdOrIds, sort = ['position', 'id'] } = {}) => {
|
||||
const criteria = {
|
||||
cardId,
|
||||
};
|
||||
|
||||
if (exceptIdOrIds) {
|
||||
criteria.id = {
|
||||
'!=': exceptIdOrIds,
|
||||
};
|
||||
}
|
||||
|
||||
return defaultFind(criteria, { sort });
|
||||
};
|
||||
|
||||
const getByCardIds = (cardIds, { sort = ['position', 'id'] } = {}) =>
|
||||
defaultFind(
|
||||
{
|
||||
cardId: cardIds,
|
||||
},
|
||||
{ sort },
|
||||
);
|
||||
|
||||
const getOneById = (id, { cardId } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (cardId) {
|
||||
criteria.cardId = cardId;
|
||||
}
|
||||
|
||||
return TaskList.findOne(criteria);
|
||||
};
|
||||
|
||||
const updateOne = (criteria, values) => TaskList.updateOne(criteria).set({ ...values });
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const delete_ = (criteria) => TaskList.destroy(criteria).fetch();
|
||||
|
||||
const deleteOne = (criteria) => TaskList.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByIds,
|
||||
getByCardId,
|
||||
getByCardIds,
|
||||
getOneById,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
delete: delete_,
|
||||
};
|
98
server/api/hooks/query-methods/models/User.js
Normal file
98
server/api/hooks/query-methods/models/User.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const defaultFind = (criteria) => User.find(criteria).sort('id');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) => {
|
||||
if (sails.config.custom.activeUsersLimit) {
|
||||
return sails.getDatastore().transaction(async (db) => {
|
||||
const queryResult = await sails
|
||||
.sendNativeQuery('SELECT NULL FROM user_account WHERE is_deactivated = $1 FOR UPDATE', [
|
||||
false,
|
||||
])
|
||||
.usingConnection(db);
|
||||
|
||||
if (queryResult.rowCount >= sails.config.custom.activeUsersLimit) {
|
||||
throw 'activeLimitReached';
|
||||
}
|
||||
|
||||
return User.create({ ...values })
|
||||
.fetch()
|
||||
.usingConnection(db);
|
||||
});
|
||||
}
|
||||
|
||||
return User.create({ ...values }).fetch();
|
||||
};
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
const getAll = ({ roleOrRoles } = {}) =>
|
||||
defaultFind({
|
||||
role: roleOrRoles,
|
||||
});
|
||||
|
||||
const getOneById = (id, { withDeactivated = true } = {}) => {
|
||||
const criteria = {
|
||||
id,
|
||||
};
|
||||
|
||||
if (!withDeactivated) {
|
||||
criteria.isDeactivated = false;
|
||||
}
|
||||
|
||||
return User.findOne(criteria);
|
||||
};
|
||||
|
||||
const getOneByEmail = (email) =>
|
||||
User.findOne({
|
||||
email: email.toLowerCase(),
|
||||
});
|
||||
|
||||
const getOneActiveByEmailOrUsername = (emailOrUsername) => {
|
||||
const fieldName = emailOrUsername.includes('@') ? 'email' : 'username';
|
||||
|
||||
return User.findOne({
|
||||
[fieldName]: emailOrUsername.toLowerCase(),
|
||||
isDeactivated: false,
|
||||
});
|
||||
};
|
||||
|
||||
const updateOne = (criteria, values) => {
|
||||
if (values.isDeactivated === false && sails.config.custom.activeUsersLimit) {
|
||||
return sails.getDatastore().transaction(async (db) => {
|
||||
const queryResult = await sails
|
||||
.sendNativeQuery('SELECT NULL FROM user_account WHERE is_deactivated = $1 FOR UPDATE', [
|
||||
false,
|
||||
])
|
||||
.usingConnection(db);
|
||||
|
||||
if (queryResult.rowCount >= sails.config.custom.activeUsersLimit) {
|
||||
throw 'activeLimitReached';
|
||||
}
|
||||
|
||||
return User.updateOne(criteria)
|
||||
.set({ ...values })
|
||||
.usingConnection(db);
|
||||
});
|
||||
}
|
||||
|
||||
return User.updateOne(criteria).set({ ...values });
|
||||
};
|
||||
|
||||
const deleteOne = (criteria) => User.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByIds,
|
||||
getAll,
|
||||
getOneById,
|
||||
getOneByEmail,
|
||||
getOneActiveByEmailOrUsername,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
};
|
|
@ -1,5 +1,7 @@
|
|||
const { URL } = require('url');
|
||||
const { S3Client } = require('@aws-sdk/client-s3');
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* s3 hook
|
||||
|
@ -9,6 +11,9 @@ const { S3Client } = require('@aws-sdk/client-s3');
|
|||
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||
*/
|
||||
|
||||
const { URL } = require('url');
|
||||
const { S3Client } = require('@aws-sdk/client-s3');
|
||||
|
||||
module.exports = function defineS3Hook(sails) {
|
||||
let client = null;
|
||||
|
||||
|
@ -18,7 +23,7 @@ module.exports = function defineS3Hook(sails) {
|
|||
*/
|
||||
|
||||
async initialize() {
|
||||
if (!sails.config.custom.s3Endpoint && !sails.config.custom.s3Region) {
|
||||
if (!this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,8 +62,8 @@ module.exports = function defineS3Hook(sails) {
|
|||
return `https://${sails.config.custom.s3Bucket}.s3.${sails.config.custom.s3Region}.amazonaws.com`;
|
||||
},
|
||||
|
||||
isActive() {
|
||||
return client !== null;
|
||||
isEnabled() {
|
||||
return !!sails.config.custom.s3Endpoint || !!sails.config.custom.s3Region;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
const nodemailer = require('nodemailer');
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* smtp hook
|
||||
|
@ -8,6 +11,8 @@ const nodemailer = require('nodemailer');
|
|||
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||
*/
|
||||
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
module.exports = function defineSmtpHook(sails) {
|
||||
let transporter = null;
|
||||
|
||||
|
@ -17,7 +22,7 @@ module.exports = function defineSmtpHook(sails) {
|
|||
*/
|
||||
|
||||
async initialize() {
|
||||
if (!sails.config.custom.smtpHost) {
|
||||
if (!this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -43,8 +48,8 @@ module.exports = function defineSmtpHook(sails) {
|
|||
return transporter;
|
||||
},
|
||||
|
||||
isActive() {
|
||||
return transporter !== null;
|
||||
isEnabled() {
|
||||
return !!sails.config.custom.smtpHost;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* watcher hook
|
||||
*
|
||||
|
@ -8,7 +13,7 @@
|
|||
|
||||
module.exports = function defineWatcherHook(sails) {
|
||||
const checkSocketConnectionsToLogout = () => {
|
||||
Object.keys(sails.io.sockets.adapter.rooms).forEach((room) => {
|
||||
[...sails.io.sockets.adapter.rooms.keys()].forEach((room) => {
|
||||
if (!room.startsWith('@accessToken:')) {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue