Initial version
This commit is contained in:
commit
7e4fd38781
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
*/node_modules/
|
||||
app-shell/
|
||||
*/build/
|
||||
11
backend/.prettierrc
Normal file
11
backend/.prettierrc
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"printWidth": 80,
|
||||
"trailingComma": "all",
|
||||
"quoteProps": "as-needed",
|
||||
"jsxSingleQuote": true,
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "always"
|
||||
}
|
||||
7
backend/.sequelizerc
Normal file
7
backend/.sequelizerc
Normal file
@ -0,0 +1,7 @@
|
||||
const path = require('path');
|
||||
module.exports = {
|
||||
"config": path.resolve("src", "db", "db.config.js"),
|
||||
"models-path": path.resolve("src", "db", "models"),
|
||||
"seeders-path": path.resolve("src", "db", "seeders"),
|
||||
"migrations-path": path.resolve("src", "db", "migrations")
|
||||
};
|
||||
23
backend/Dockerfile
Normal file
23
backend/Dockerfile
Normal file
@ -0,0 +1,23 @@
|
||||
FROM node:20.15.1-alpine
|
||||
|
||||
RUN apk update && apk add bash
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
||||
# where available (npm@5+)
|
||||
COPY package*.json ./
|
||||
|
||||
RUN yarn install
|
||||
# If you are building your code for production
|
||||
# RUN npm ci --only=production
|
||||
|
||||
|
||||
# Bundle app source
|
||||
COPY . .
|
||||
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD [ "yarn", "start" ]
|
||||
67
backend/README.md
Normal file
67
backend/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
#gas - template backend,
|
||||
|
||||
#### Run App on local machine:
|
||||
|
||||
##### Install local dependencies:
|
||||
|
||||
- `yarn install`
|
||||
|
||||
---
|
||||
|
||||
##### Adjust local db:
|
||||
|
||||
###### 1. Install postgres:
|
||||
|
||||
- MacOS:
|
||||
|
||||
- `brew install postgres`
|
||||
|
||||
- Ubuntu:
|
||||
- `sudo apt update`
|
||||
- `sudo apt install postgresql postgresql-contrib`
|
||||
|
||||
###### 2. Create db and admin user:
|
||||
|
||||
- Before run and test connection, make sure you have created a database as described in the above configuration. You can use the `psql` command to create a user and database.
|
||||
|
||||
- `psql postgres --u postgres`
|
||||
|
||||
- Next, type this command for creating a new user with password then give access for creating the database.
|
||||
|
||||
- `postgres-# CREATE ROLE admin WITH LOGIN PASSWORD 'admin_pass';`
|
||||
- `postgres-# ALTER ROLE admin CREATEDB;`
|
||||
|
||||
- Quit `psql` then log in again using the new user that previously created.
|
||||
|
||||
- `postgres-# \q`
|
||||
- `psql postgres -U admin`
|
||||
|
||||
- Type this command to creating a new database.
|
||||
|
||||
- `postgres=> CREATE DATABASE db_gas;`
|
||||
|
||||
- Then give that new user privileges to the new database then quit the `psql`.
|
||||
- `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_gas TO admin;`
|
||||
- `postgres=> \q`
|
||||
|
||||
---
|
||||
|
||||
#### Api Documentation (Swagger)
|
||||
|
||||
http://localhost:8080/api-docs (local host)
|
||||
|
||||
http://host_name/api-docs
|
||||
|
||||
---
|
||||
|
||||
##### Setup database tables or update after schema change
|
||||
|
||||
- `yarn db:migrate`
|
||||
|
||||
##### Seed the initial data (admin accounts, relevant for the first setup):
|
||||
|
||||
- `yarn db:seed`
|
||||
|
||||
##### Start build:
|
||||
|
||||
- `yarn start`
|
||||
51
backend/package.json
Normal file
51
backend/package.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "gas",
|
||||
"description": "gas - template backend",
|
||||
"scripts": {
|
||||
"start": "npm run db:migrate && npm run db:seed && nodemon ./src/index.js",
|
||||
"db:migrate": "sequelize-cli db:migrate",
|
||||
"db:seed": "sequelize-cli db:seed:all",
|
||||
"db:drop": "sequelize-cli db:drop",
|
||||
"db:create": "sequelize-cli db:create"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google-cloud/storage": "^5.18.2",
|
||||
"axios": "^1.6.7",
|
||||
"bcrypt": "5.1.1",
|
||||
"cors": "2.8.5",
|
||||
"csv-parser": "^3.0.0",
|
||||
"express": "4.18.2",
|
||||
"formidable": "1.2.2",
|
||||
"helmet": "4.1.1",
|
||||
"json2csv": "^5.0.7",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.30.1",
|
||||
"multer": "^1.4.4",
|
||||
"mysql2": "2.2.5",
|
||||
"nodemailer": "6.9.9",
|
||||
"passport": "^0.7.0",
|
||||
"passport-google-oauth2": "^0.2.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-microsoft": "^0.1.0",
|
||||
"pg": "8.4.1",
|
||||
"pg-hstore": "2.3.4",
|
||||
"sequelize": "6.35.2",
|
||||
"sequelize-json-schema": "^2.1.1",
|
||||
"sqlite": "4.0.15",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"tedious": "^18.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"mocha": "8.1.3",
|
||||
"node-mocks-http": "1.9.0",
|
||||
"nodemon": "2.0.5",
|
||||
"sequelize-cli": "6.6.2"
|
||||
}
|
||||
}
|
||||
79
backend/src/auth/auth.js
Normal file
79
backend/src/auth/auth.js
Normal file
@ -0,0 +1,79 @@
|
||||
const config = require('../config');
|
||||
const providers = config.providers;
|
||||
const helpers = require('../helpers');
|
||||
const db = require('../db/models');
|
||||
|
||||
const passport = require('passport');
|
||||
const JWTstrategy = require('passport-jwt').Strategy;
|
||||
const ExtractJWT = require('passport-jwt').ExtractJwt;
|
||||
const GoogleStrategy = require('passport-google-oauth2').Strategy;
|
||||
const MicrosoftStrategy = require('passport-microsoft').Strategy;
|
||||
const UsersDBApi = require('../db/api/users');
|
||||
|
||||
passport.use(
|
||||
new JWTstrategy(
|
||||
{
|
||||
passReqToCallback: true,
|
||||
secretOrKey: config.secret_key,
|
||||
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
|
||||
},
|
||||
async (req, token, done) => {
|
||||
try {
|
||||
const user = await UsersDBApi.findBy({ email: token.user.email });
|
||||
|
||||
if (user && user.disabled) {
|
||||
return done(new Error(`User '${user.email}' is disabled`));
|
||||
}
|
||||
|
||||
req.currentUser = user;
|
||||
|
||||
return done(null, user);
|
||||
} catch (error) {
|
||||
done(error);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
passport.use(
|
||||
new GoogleStrategy(
|
||||
{
|
||||
clientID: config.google.clientId,
|
||||
clientSecret: config.google.clientSecret,
|
||||
callbackURL: config.apiUrl + '/auth/signin/google/callback',
|
||||
passReqToCallback: true,
|
||||
},
|
||||
function (request, accessToken, refreshToken, profile, done) {
|
||||
socialStrategy(profile.email, profile, providers.GOOGLE, done);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
passport.use(
|
||||
new MicrosoftStrategy(
|
||||
{
|
||||
clientID: config.microsoft.clientId,
|
||||
clientSecret: config.microsoft.clientSecret,
|
||||
callbackURL: config.apiUrl + '/auth/signin/microsoft/callback',
|
||||
passReqToCallback: true,
|
||||
},
|
||||
function (request, accessToken, refreshToken, profile, done) {
|
||||
const email = profile._json.mail || profile._json.userPrincipalName;
|
||||
socialStrategy(email, profile, providers.MICROSOFT, done);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
function socialStrategy(email, profile, provider, done) {
|
||||
db.users
|
||||
.findOrCreate({ where: { email, provider } })
|
||||
.then(([user, created]) => {
|
||||
const body = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: profile.displayName,
|
||||
};
|
||||
const token = helpers.jwtSign({ user: body });
|
||||
return done(null, { token });
|
||||
});
|
||||
}
|
||||
73
backend/src/config.js
Normal file
73
backend/src/config.js
Normal file
@ -0,0 +1,73 @@
|
||||
const os = require('os');
|
||||
|
||||
const config = {
|
||||
gcloud: {
|
||||
bucket: 'fldemo-files',
|
||||
hash: '11e6ecfee5f2de49e9b2a278e58b257b',
|
||||
},
|
||||
bcrypt: {
|
||||
saltRounds: 12,
|
||||
},
|
||||
admin_pass: 'password',
|
||||
admin_email: 'admin@flatlogic.com',
|
||||
providers: {
|
||||
LOCAL: 'local',
|
||||
GOOGLE: 'google',
|
||||
MICROSOFT: 'microsoft',
|
||||
},
|
||||
secret_key: 'HUEyqESqgQ1yTwzVlO6wprC9Kf1J1xuA',
|
||||
remote: '',
|
||||
port: process.env.NODE_ENV === 'production' ? '' : '8080',
|
||||
hostUI: process.env.NODE_ENV === 'production' ? '' : 'http://localhost',
|
||||
portUI: process.env.NODE_ENV === 'production' ? '' : '3000',
|
||||
|
||||
portUIProd: process.env.NODE_ENV === 'production' ? '' : ':3000',
|
||||
|
||||
swaggerUI: process.env.NODE_ENV === 'production' ? '' : 'http://localhost',
|
||||
swaggerPort: process.env.NODE_ENV === 'production' ? '' : ':8080',
|
||||
google: {
|
||||
clientId:
|
||||
'671001533244-kf1k1gmp6mnl0r030qmvdu6v36ghmim6.apps.googleusercontent.com',
|
||||
clientSecret: 'Yo4qbKZniqvojzUQ60iKlxqR',
|
||||
},
|
||||
microsoft: {
|
||||
clientId: '4696f457-31af-40de-897c-e00d7d4cff73',
|
||||
clientSecret: 'm8jzZ.5UpHF3=-dXzyxiZ4e[F8OF54@p',
|
||||
},
|
||||
uploadDir: os.tmpdir(),
|
||||
email: {
|
||||
from: 'gas <app@flatlogic.app>',
|
||||
host: 'email-smtp.us-east-1.amazonaws.com',
|
||||
port: 587,
|
||||
auth: {
|
||||
user: 'AKIAVEW7G4PQUBGM52OF',
|
||||
pass: process.env.EMAIL_PASS,
|
||||
},
|
||||
tls: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
},
|
||||
roles: {
|
||||
super_admin: 'Super Administrator',
|
||||
|
||||
admin: 'Administrator',
|
||||
user: 'User',
|
||||
},
|
||||
|
||||
project_uuid: '253f1bd3-13e2-43d7-ba52-454e8eb066a1',
|
||||
flHost:
|
||||
process.env.NODE_ENV === 'production' ||
|
||||
process.env.NODE_ENV === 'dev_stage'
|
||||
? 'https://flatlogic.com/projects'
|
||||
: 'http://localhost:3000/projects',
|
||||
};
|
||||
config.pexelsKey = 'Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18';
|
||||
config.pexelsQuery = 'abstract fitness concept art';
|
||||
config.host =
|
||||
process.env.NODE_ENV === 'production' ? config.remote : 'http://localhost';
|
||||
config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`;
|
||||
config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`;
|
||||
config.uiUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}/#`;
|
||||
config.backUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}`;
|
||||
|
||||
module.exports = config;
|
||||
461
backend/src/db/api/appointments.js
Normal file
461
backend/src/db/api/appointments.js
Normal file
@ -0,0 +1,461 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class AppointmentsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const appointments = await db.appointments.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
start_time: data.start_time || null,
|
||||
end_time: data.end_time || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await appointments.setMember(data.member || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await appointments.setStaff(data.staff || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await appointments.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return appointments;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const appointmentsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
start_time: item.start_time || null,
|
||||
end_time: item.end_time || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const appointments = await db.appointments.bulkCreate(appointmentsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return appointments;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const appointments = await db.appointments.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.start_time !== undefined)
|
||||
updatePayload.start_time = data.start_time;
|
||||
|
||||
if (data.end_time !== undefined) updatePayload.end_time = data.end_time;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await appointments.update(updatePayload, { transaction });
|
||||
|
||||
if (data.member !== undefined) {
|
||||
await appointments.setMember(
|
||||
data.member,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.staff !== undefined) {
|
||||
await appointments.setStaff(
|
||||
data.staff,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await appointments.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return appointments;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const appointments = await db.appointments.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of appointments) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of appointments) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return appointments;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const appointments = await db.appointments.findByPk(id, options);
|
||||
|
||||
await appointments.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await appointments.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return appointments;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const appointments = await db.appointments.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!appointments) {
|
||||
return appointments;
|
||||
}
|
||||
|
||||
const output = appointments.get({ plain: true });
|
||||
|
||||
output.member = await appointments.getMember({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.staff = await appointments.getStaff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await appointments.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.members,
|
||||
as: 'member',
|
||||
|
||||
where: filter.member
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.member
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.member
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.staff,
|
||||
as: 'staff',
|
||||
|
||||
where: filter.staff
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{
|
||||
start_time: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
{
|
||||
end_time: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.start_timeRange) {
|
||||
const [start, end] = filter.start_timeRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
start_time: {
|
||||
...where.start_time,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
start_time: {
|
||||
...where.start_time,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.end_timeRange) {
|
||||
const [start, end] = filter.end_timeRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
end_time: {
|
||||
...where.end_time,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
end_time: {
|
||||
...where.end_time,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.appointments.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('appointments', 'start_time', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.appointments.findAll({
|
||||
attributes: ['id', 'start_time'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['start_time', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.start_time,
|
||||
}));
|
||||
}
|
||||
};
|
||||
329
backend/src/db/api/branches.js
Normal file
329
backend/src/db/api/branches.js
Normal file
@ -0,0 +1,329 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class BranchesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const branches = await db.branches.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const branchesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const branches = await db.branches.bulkCreate(branchesData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const branches = await db.branches.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await branches.update(updatePayload, { transaction });
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const branches = await db.branches.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of branches) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of branches) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const branches = await db.branches.findByPk(id, options);
|
||||
|
||||
await branches.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await branches.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const branches = await db.branches.findOne({ where }, { transaction });
|
||||
|
||||
if (!branches) {
|
||||
return branches;
|
||||
}
|
||||
|
||||
const output = branches.get({ plain: true });
|
||||
|
||||
output.users_branches = await branches.getUsers_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.appointments_branches = await branches.getAppointments_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.equipment_maintenance_branches =
|
||||
await branches.getEquipment_maintenance_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.locker_assignments_branches =
|
||||
await branches.getLocker_assignments_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.medical_reports_branches =
|
||||
await branches.getMedical_reports_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.members_branches = await branches.getMembers_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.private_sessions_branches =
|
||||
await branches.getPrivate_sessions_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.progress_tracks_branches =
|
||||
await branches.getProgress_tracks_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.purchases_branches = await branches.getPurchases_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.sales_branches = await branches.getSales_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.shifts_branches = await branches.getShifts_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.staff_branches = await branches.getStaff_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.workout_logs_branches = await branches.getWorkout_logs_branches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('branches', 'name', filter.name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.branches.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('branches', 'name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.branches.findAll({
|
||||
attributes: ['id', 'name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
393
backend/src/db/api/equipment_maintenance.js
Normal file
393
backend/src/db/api/equipment_maintenance.js
Normal file
@ -0,0 +1,393 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class Equipment_maintenanceDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const equipment_maintenance = await db.equipment_maintenance.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
equipment_name: data.equipment_name || null,
|
||||
maintenance_date: data.maintenance_date || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await equipment_maintenance.setStaff(data.staff || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await equipment_maintenance.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return equipment_maintenance;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const equipment_maintenanceData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
equipment_name: item.equipment_name || null,
|
||||
maintenance_date: item.maintenance_date || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const equipment_maintenance = await db.equipment_maintenance.bulkCreate(
|
||||
equipment_maintenanceData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return equipment_maintenance;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const equipment_maintenance = await db.equipment_maintenance.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.equipment_name !== undefined)
|
||||
updatePayload.equipment_name = data.equipment_name;
|
||||
|
||||
if (data.maintenance_date !== undefined)
|
||||
updatePayload.maintenance_date = data.maintenance_date;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await equipment_maintenance.update(updatePayload, { transaction });
|
||||
|
||||
if (data.staff !== undefined) {
|
||||
await equipment_maintenance.setStaff(
|
||||
data.staff,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await equipment_maintenance.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return equipment_maintenance;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const equipment_maintenance = await db.equipment_maintenance.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of equipment_maintenance) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of equipment_maintenance) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return equipment_maintenance;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const equipment_maintenance = await db.equipment_maintenance.findByPk(
|
||||
id,
|
||||
options,
|
||||
);
|
||||
|
||||
await equipment_maintenance.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await equipment_maintenance.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return equipment_maintenance;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const equipment_maintenance = await db.equipment_maintenance.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!equipment_maintenance) {
|
||||
return equipment_maintenance;
|
||||
}
|
||||
|
||||
const output = equipment_maintenance.get({ plain: true });
|
||||
|
||||
output.staff = await equipment_maintenance.getStaff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await equipment_maintenance.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.staff,
|
||||
as: 'staff',
|
||||
|
||||
where: filter.staff
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.equipment_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'equipment_maintenance',
|
||||
'equipment_name',
|
||||
filter.equipment_name,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.maintenance_dateRange) {
|
||||
const [start, end] = filter.maintenance_dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
maintenance_date: {
|
||||
...where.maintenance_date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
maintenance_date: {
|
||||
...where.maintenance_date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.equipment_maintenance.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('equipment_maintenance', 'equipment_name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.equipment_maintenance.findAll({
|
||||
attributes: ['id', 'equipment_name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['equipment_name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.equipment_name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
73
backend/src/db/api/file.js
Normal file
73
backend/src/db/api/file.js
Normal file
@ -0,0 +1,73 @@
|
||||
const db = require('../models');
|
||||
const assert = require('assert');
|
||||
const services = require('../../services/file');
|
||||
|
||||
module.exports = class FileDBApi {
|
||||
static async replaceRelationFiles(relation, rawFiles, options) {
|
||||
assert(relation.belongsTo, 'belongsTo is required');
|
||||
assert(relation.belongsToColumn, 'belongsToColumn is required');
|
||||
assert(relation.belongsToId, 'belongsToId is required');
|
||||
|
||||
let files = [];
|
||||
|
||||
if (Array.isArray(rawFiles)) {
|
||||
files = rawFiles;
|
||||
} else {
|
||||
files = rawFiles ? [rawFiles] : [];
|
||||
}
|
||||
|
||||
await this._removeLegacyFiles(relation, files, options);
|
||||
await this._addFiles(relation, files, options);
|
||||
}
|
||||
|
||||
static async _addFiles(relation, files, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
|
||||
const inexistentFiles = files.filter((file) => !!file.new);
|
||||
|
||||
for (const file of inexistentFiles) {
|
||||
await db.file.create(
|
||||
{
|
||||
belongsTo: relation.belongsTo,
|
||||
belongsToColumn: relation.belongsToColumn,
|
||||
belongsToId: relation.belongsToId,
|
||||
name: file.name,
|
||||
sizeInBytes: file.sizeInBytes,
|
||||
privateUrl: file.privateUrl,
|
||||
publicUrl: file.publicUrl,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static async _removeLegacyFiles(relation, files, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const filesToDelete = await db.file.findAll({
|
||||
where: {
|
||||
belongsTo: relation.belongsTo,
|
||||
belongsToId: relation.belongsToId,
|
||||
belongsToColumn: relation.belongsToColumn,
|
||||
id: {
|
||||
[db.Sequelize.Op.notIn]: files
|
||||
.filter((file) => !file.new)
|
||||
.map((file) => file.id),
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
for (let file of filesToDelete) {
|
||||
await services.deleteGCloud(file.privateUrl);
|
||||
await file.destroy({
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
419
backend/src/db/api/locker_assignments.js
Normal file
419
backend/src/db/api/locker_assignments.js
Normal file
@ -0,0 +1,419 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class Locker_assignmentsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const locker_assignments = await db.locker_assignments.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
locker_number: data.locker_number || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await locker_assignments.setMember(data.member || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await locker_assignments.setStaff(data.staff || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await locker_assignments.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return locker_assignments;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const locker_assignmentsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
locker_number: item.locker_number || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const locker_assignments = await db.locker_assignments.bulkCreate(
|
||||
locker_assignmentsData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return locker_assignments;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const locker_assignments = await db.locker_assignments.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.locker_number !== undefined)
|
||||
updatePayload.locker_number = data.locker_number;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await locker_assignments.update(updatePayload, { transaction });
|
||||
|
||||
if (data.member !== undefined) {
|
||||
await locker_assignments.setMember(
|
||||
data.member,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.staff !== undefined) {
|
||||
await locker_assignments.setStaff(
|
||||
data.staff,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await locker_assignments.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return locker_assignments;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const locker_assignments = await db.locker_assignments.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of locker_assignments) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of locker_assignments) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return locker_assignments;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const locker_assignments = await db.locker_assignments.findByPk(
|
||||
id,
|
||||
options,
|
||||
);
|
||||
|
||||
await locker_assignments.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await locker_assignments.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return locker_assignments;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const locker_assignments = await db.locker_assignments.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!locker_assignments) {
|
||||
return locker_assignments;
|
||||
}
|
||||
|
||||
const output = locker_assignments.get({ plain: true });
|
||||
|
||||
output.member = await locker_assignments.getMember({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.staff = await locker_assignments.getStaff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await locker_assignments.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.members,
|
||||
as: 'member',
|
||||
|
||||
where: filter.member
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.member
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.member
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.staff,
|
||||
as: 'staff',
|
||||
|
||||
where: filter.staff
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.locker_numberRange) {
|
||||
const [start, end] = filter.locker_numberRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
locker_number: {
|
||||
...where.locker_number,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
locker_number: {
|
||||
...where.locker_number,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.locker_assignments.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('locker_assignments', 'locker_number', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.locker_assignments.findAll({
|
||||
attributes: ['id', 'locker_number'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['locker_number', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.locker_number,
|
||||
}));
|
||||
}
|
||||
};
|
||||
390
backend/src/db/api/medical_reports.js
Normal file
390
backend/src/db/api/medical_reports.js
Normal file
@ -0,0 +1,390 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class Medical_reportsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const medical_reports = await db.medical_reports.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
report_name: data.report_name || null,
|
||||
expiry_date: data.expiry_date || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await medical_reports.setMember(data.member || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await medical_reports.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return medical_reports;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const medical_reportsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
report_name: item.report_name || null,
|
||||
expiry_date: item.expiry_date || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const medical_reports = await db.medical_reports.bulkCreate(
|
||||
medical_reportsData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return medical_reports;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const medical_reports = await db.medical_reports.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.report_name !== undefined)
|
||||
updatePayload.report_name = data.report_name;
|
||||
|
||||
if (data.expiry_date !== undefined)
|
||||
updatePayload.expiry_date = data.expiry_date;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await medical_reports.update(updatePayload, { transaction });
|
||||
|
||||
if (data.member !== undefined) {
|
||||
await medical_reports.setMember(
|
||||
data.member,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await medical_reports.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return medical_reports;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const medical_reports = await db.medical_reports.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of medical_reports) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of medical_reports) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return medical_reports;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const medical_reports = await db.medical_reports.findByPk(id, options);
|
||||
|
||||
await medical_reports.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await medical_reports.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return medical_reports;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const medical_reports = await db.medical_reports.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!medical_reports) {
|
||||
return medical_reports;
|
||||
}
|
||||
|
||||
const output = medical_reports.get({ plain: true });
|
||||
|
||||
output.member = await medical_reports.getMember({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await medical_reports.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.members,
|
||||
as: 'member',
|
||||
|
||||
where: filter.member
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.member
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.member
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.report_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'medical_reports',
|
||||
'report_name',
|
||||
filter.report_name,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.expiry_dateRange) {
|
||||
const [start, end] = filter.expiry_dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
expiry_date: {
|
||||
...where.expiry_date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
expiry_date: {
|
||||
...where.expiry_date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.medical_reports.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('medical_reports', 'report_name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.medical_reports.findAll({
|
||||
attributes: ['id', 'report_name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['report_name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.report_name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
690
backend/src/db/api/members.js
Normal file
690
backend/src/db/api/members.js
Normal file
@ -0,0 +1,690 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class MembersDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const members = await db.members.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
first_name: data.first_name || null,
|
||||
last_name: data.last_name || null,
|
||||
email: data.email || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await members.setUser(data.user || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await members.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await members.setMedical_reports(data.medical_reports || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await members.setProgress_tracks(data.progress_tracks || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await members.setAppointments(data.appointments || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await members.setWorkout_logs(data.workout_logs || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await members.setPrivate_sessions(data.private_sessions || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await members.setPurchases(data.purchases || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const membersData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
first_name: item.first_name || null,
|
||||
last_name: item.last_name || null,
|
||||
email: item.email || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const members = await db.members.bulkCreate(membersData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const members = await db.members.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.first_name !== undefined)
|
||||
updatePayload.first_name = data.first_name;
|
||||
|
||||
if (data.last_name !== undefined) updatePayload.last_name = data.last_name;
|
||||
|
||||
if (data.email !== undefined) updatePayload.email = data.email;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await members.update(updatePayload, { transaction });
|
||||
|
||||
if (data.user !== undefined) {
|
||||
await members.setUser(
|
||||
data.user,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await members.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.medical_reports !== undefined) {
|
||||
await members.setMedical_reports(data.medical_reports, { transaction });
|
||||
}
|
||||
|
||||
if (data.progress_tracks !== undefined) {
|
||||
await members.setProgress_tracks(data.progress_tracks, { transaction });
|
||||
}
|
||||
|
||||
if (data.appointments !== undefined) {
|
||||
await members.setAppointments(data.appointments, { transaction });
|
||||
}
|
||||
|
||||
if (data.workout_logs !== undefined) {
|
||||
await members.setWorkout_logs(data.workout_logs, { transaction });
|
||||
}
|
||||
|
||||
if (data.private_sessions !== undefined) {
|
||||
await members.setPrivate_sessions(data.private_sessions, { transaction });
|
||||
}
|
||||
|
||||
if (data.purchases !== undefined) {
|
||||
await members.setPurchases(data.purchases, { transaction });
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const members = await db.members.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of members) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of members) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const members = await db.members.findByPk(id, options);
|
||||
|
||||
await members.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await members.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const members = await db.members.findOne({ where }, { transaction });
|
||||
|
||||
if (!members) {
|
||||
return members;
|
||||
}
|
||||
|
||||
const output = members.get({ plain: true });
|
||||
|
||||
output.appointments_member = await members.getAppointments_member({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.locker_assignments_member =
|
||||
await members.getLocker_assignments_member({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.medical_reports_member = await members.getMedical_reports_member({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.private_sessions_member = await members.getPrivate_sessions_member({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.progress_tracks_member = await members.getProgress_tracks_member({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.purchases_member = await members.getPurchases_member({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.workout_logs_member = await members.getWorkout_logs_member({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.user = await members.getUser({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.medical_reports = await members.getMedical_reports({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.progress_tracks = await members.getProgress_tracks({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.appointments = await members.getAppointments({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.workout_logs = await members.getWorkout_logs({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.private_sessions = await members.getPrivate_sessions({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.purchases = await members.getPurchases({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await members.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.users,
|
||||
as: 'user',
|
||||
|
||||
where: filter.user
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.user
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
firstName: {
|
||||
[Op.or]: filter.user
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.medical_reports,
|
||||
as: 'medical_reports',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.progress_tracks,
|
||||
as: 'progress_tracks',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.appointments,
|
||||
as: 'appointments',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.workout_logs,
|
||||
as: 'workout_logs',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.private_sessions,
|
||||
as: 'private_sessions',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.purchases,
|
||||
as: 'purchases',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.first_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('members', 'first_name', filter.first_name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.last_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('members', 'last_name', filter.last_name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.email) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('members', 'email', filter.email),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.medical_reports) {
|
||||
const searchTerms = filter.medical_reports.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.medical_reports,
|
||||
as: 'medical_reports_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
report_name: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.progress_tracks) {
|
||||
const searchTerms = filter.progress_tracks.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.progress_tracks,
|
||||
as: 'progress_tracks_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.appointments) {
|
||||
const searchTerms = filter.appointments.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.appointments,
|
||||
as: 'appointments_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
start_time: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.workout_logs) {
|
||||
const searchTerms = filter.workout_logs.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.workout_logs,
|
||||
as: 'workout_logs_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
workout_details: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.private_sessions) {
|
||||
const searchTerms = filter.private_sessions.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.private_sessions,
|
||||
as: 'private_sessions_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
session_date: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.purchases) {
|
||||
const searchTerms = filter.purchases.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.purchases,
|
||||
as: 'purchases_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
purchase_date: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.members.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('members', 'first_name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.members.findAll({
|
||||
attributes: ['id', 'first_name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['first_name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.first_name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
257
backend/src/db/api/permissions.js
Normal file
257
backend/src/db/api/permissions.js
Normal file
@ -0,0 +1,257 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class PermissionsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const permissions = await db.permissions.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const permissionsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const permissions = await db.permissions.bulkCreate(permissionsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const permissions = await db.permissions.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await permissions.update(updatePayload, { transaction });
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const permissions = await db.permissions.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of permissions) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of permissions) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const permissions = await db.permissions.findByPk(id, options);
|
||||
|
||||
await permissions.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await permissions.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const permissions = await db.permissions.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!permissions) {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
const output = permissions.get({ plain: true });
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('permissions', 'name', filter.name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.permissions.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset) {
|
||||
let where = {};
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('permissions', 'name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.permissions.findAll({
|
||||
attributes: ['id', 'name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
434
backend/src/db/api/private_sessions.js
Normal file
434
backend/src/db/api/private_sessions.js
Normal file
@ -0,0 +1,434 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class Private_sessionsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const private_sessions = await db.private_sessions.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
session_date: data.session_date || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await private_sessions.setMember(data.member || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await private_sessions.setStaff(data.staff || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await private_sessions.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return private_sessions;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const private_sessionsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
session_date: item.session_date || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const private_sessions = await db.private_sessions.bulkCreate(
|
||||
private_sessionsData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return private_sessions;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const private_sessions = await db.private_sessions.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.session_date !== undefined)
|
||||
updatePayload.session_date = data.session_date;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await private_sessions.update(updatePayload, { transaction });
|
||||
|
||||
if (data.member !== undefined) {
|
||||
await private_sessions.setMember(
|
||||
data.member,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.staff !== undefined) {
|
||||
await private_sessions.setStaff(
|
||||
data.staff,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await private_sessions.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return private_sessions;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const private_sessions = await db.private_sessions.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of private_sessions) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of private_sessions) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return private_sessions;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const private_sessions = await db.private_sessions.findByPk(id, options);
|
||||
|
||||
await private_sessions.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await private_sessions.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return private_sessions;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const private_sessions = await db.private_sessions.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!private_sessions) {
|
||||
return private_sessions;
|
||||
}
|
||||
|
||||
const output = private_sessions.get({ plain: true });
|
||||
|
||||
output.member = await private_sessions.getMember({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.staff = await private_sessions.getStaff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await private_sessions.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.members,
|
||||
as: 'member',
|
||||
|
||||
where: filter.member
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.member
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.member
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.staff,
|
||||
as: 'staff',
|
||||
|
||||
where: filter.staff
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{
|
||||
session_date: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
{
|
||||
session_date: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.session_dateRange) {
|
||||
const [start, end] = filter.session_dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
session_date: {
|
||||
...where.session_date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
session_date: {
|
||||
...where.session_date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.private_sessions.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('private_sessions', 'session_date', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.private_sessions.findAll({
|
||||
attributes: ['id', 'session_date'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['session_date', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.session_date,
|
||||
}));
|
||||
}
|
||||
};
|
||||
361
backend/src/db/api/progress_tracks.js
Normal file
361
backend/src/db/api/progress_tracks.js
Normal file
@ -0,0 +1,361 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class Progress_tracksDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const progress_tracks = await db.progress_tracks.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
description: data.description || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await progress_tracks.setMember(data.member || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await progress_tracks.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return progress_tracks;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const progress_tracksData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
description: item.description || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const progress_tracks = await db.progress_tracks.bulkCreate(
|
||||
progress_tracksData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return progress_tracks;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const progress_tracks = await db.progress_tracks.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.description !== undefined)
|
||||
updatePayload.description = data.description;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await progress_tracks.update(updatePayload, { transaction });
|
||||
|
||||
if (data.member !== undefined) {
|
||||
await progress_tracks.setMember(
|
||||
data.member,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await progress_tracks.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return progress_tracks;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const progress_tracks = await db.progress_tracks.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of progress_tracks) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of progress_tracks) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return progress_tracks;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const progress_tracks = await db.progress_tracks.findByPk(id, options);
|
||||
|
||||
await progress_tracks.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await progress_tracks.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return progress_tracks;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const progress_tracks = await db.progress_tracks.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!progress_tracks) {
|
||||
return progress_tracks;
|
||||
}
|
||||
|
||||
const output = progress_tracks.get({ plain: true });
|
||||
|
||||
output.member = await progress_tracks.getMember({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await progress_tracks.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.members,
|
||||
as: 'member',
|
||||
|
||||
where: filter.member
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.member
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.member
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.description) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'progress_tracks',
|
||||
'description',
|
||||
filter.description,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.progress_tracks.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('progress_tracks', 'description', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.progress_tracks.findAll({
|
||||
attributes: ['id', 'description'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['description', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.description,
|
||||
}));
|
||||
}
|
||||
};
|
||||
392
backend/src/db/api/purchases.js
Normal file
392
backend/src/db/api/purchases.js
Normal file
@ -0,0 +1,392 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class PurchasesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const purchases = await db.purchases.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
purchase_date: data.purchase_date || null,
|
||||
amount: data.amount || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await purchases.setMember(data.member || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await purchases.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return purchases;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const purchasesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
purchase_date: item.purchase_date || null,
|
||||
amount: item.amount || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const purchases = await db.purchases.bulkCreate(purchasesData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return purchases;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const purchases = await db.purchases.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.purchase_date !== undefined)
|
||||
updatePayload.purchase_date = data.purchase_date;
|
||||
|
||||
if (data.amount !== undefined) updatePayload.amount = data.amount;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await purchases.update(updatePayload, { transaction });
|
||||
|
||||
if (data.member !== undefined) {
|
||||
await purchases.setMember(
|
||||
data.member,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await purchases.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return purchases;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const purchases = await db.purchases.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of purchases) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of purchases) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return purchases;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const purchases = await db.purchases.findByPk(id, options);
|
||||
|
||||
await purchases.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await purchases.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return purchases;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const purchases = await db.purchases.findOne({ where }, { transaction });
|
||||
|
||||
if (!purchases) {
|
||||
return purchases;
|
||||
}
|
||||
|
||||
const output = purchases.get({ plain: true });
|
||||
|
||||
output.member = await purchases.getMember({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await purchases.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.members,
|
||||
as: 'member',
|
||||
|
||||
where: filter.member
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.member
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.member
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.purchase_dateRange) {
|
||||
const [start, end] = filter.purchase_dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
purchase_date: {
|
||||
...where.purchase_date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
purchase_date: {
|
||||
...where.purchase_date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.amountRange) {
|
||||
const [start, end] = filter.amountRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
amount: {
|
||||
...where.amount,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
amount: {
|
||||
...where.amount,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.purchases.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('purchases', 'purchase_date', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.purchases.findAll({
|
||||
attributes: ['id', 'purchase_date'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['purchase_date', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.purchase_date,
|
||||
}));
|
||||
}
|
||||
};
|
||||
343
backend/src/db/api/roles.js
Normal file
343
backend/src/db/api/roles.js
Normal file
@ -0,0 +1,343 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const config = require('../../config');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class RolesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const roles = await db.roles.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
role_customization: data.role_customization || null,
|
||||
globalAccess: data.globalAccess || false,
|
||||
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await roles.setPermissions(data.permissions || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const rolesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name || null,
|
||||
role_customization: item.role_customization || null,
|
||||
globalAccess: item.globalAccess || false,
|
||||
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const roles = await db.roles.bulkCreate(rolesData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const roles = await db.roles.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
if (data.role_customization !== undefined)
|
||||
updatePayload.role_customization = data.role_customization;
|
||||
|
||||
if (data.globalAccess !== undefined)
|
||||
updatePayload.globalAccess = data.globalAccess;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await roles.update(updatePayload, { transaction });
|
||||
|
||||
if (data.permissions !== undefined) {
|
||||
await roles.setPermissions(data.permissions, { transaction });
|
||||
}
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const roles = await db.roles.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of roles) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of roles) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const roles = await db.roles.findByPk(id, options);
|
||||
|
||||
await roles.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await roles.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const roles = await db.roles.findOne({ where }, { transaction });
|
||||
|
||||
if (!roles) {
|
||||
return roles;
|
||||
}
|
||||
|
||||
const output = roles.get({ plain: true });
|
||||
|
||||
output.users_app_role = await roles.getUsers_app_role({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.permissions = await roles.getPermissions({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.permissions,
|
||||
as: 'permissions',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('roles', 'name', filter.name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.role_customization) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'roles',
|
||||
'role_customization',
|
||||
filter.role_customization,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.globalAccess) {
|
||||
where = {
|
||||
...where,
|
||||
globalAccess: filter.globalAccess,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.permissions) {
|
||||
const searchTerms = filter.permissions.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.permissions,
|
||||
as: 'permissions_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!globalAccess) {
|
||||
where = { name: { [Op.ne]: config.roles.super_admin } };
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.roles.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess) {
|
||||
where = { name: { [Op.ne]: config.roles.super_admin } };
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('roles', 'name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.roles.findAll({
|
||||
attributes: ['id', 'name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
390
backend/src/db/api/sales.js
Normal file
390
backend/src/db/api/sales.js
Normal file
@ -0,0 +1,390 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class SalesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const sales = await db.sales.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
sale_date: data.sale_date || null,
|
||||
total_amount: data.total_amount || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await sales.setStaff(data.staff || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await sales.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return sales;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const salesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
sale_date: item.sale_date || null,
|
||||
total_amount: item.total_amount || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const sales = await db.sales.bulkCreate(salesData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return sales;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const sales = await db.sales.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.sale_date !== undefined) updatePayload.sale_date = data.sale_date;
|
||||
|
||||
if (data.total_amount !== undefined)
|
||||
updatePayload.total_amount = data.total_amount;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await sales.update(updatePayload, { transaction });
|
||||
|
||||
if (data.staff !== undefined) {
|
||||
await sales.setStaff(
|
||||
data.staff,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await sales.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return sales;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const sales = await db.sales.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of sales) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of sales) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return sales;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const sales = await db.sales.findByPk(id, options);
|
||||
|
||||
await sales.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await sales.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return sales;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const sales = await db.sales.findOne({ where }, { transaction });
|
||||
|
||||
if (!sales) {
|
||||
return sales;
|
||||
}
|
||||
|
||||
const output = sales.get({ plain: true });
|
||||
|
||||
output.staff = await sales.getStaff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await sales.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.staff,
|
||||
as: 'staff',
|
||||
|
||||
where: filter.staff
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.sale_dateRange) {
|
||||
const [start, end] = filter.sale_dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
sale_date: {
|
||||
...where.sale_date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
sale_date: {
|
||||
...where.sale_date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.total_amountRange) {
|
||||
const [start, end] = filter.total_amountRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
total_amount: {
|
||||
...where.total_amount,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
total_amount: {
|
||||
...where.total_amount,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.sales.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('sales', 'sale_date', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.sales.findAll({
|
||||
attributes: ['id', 'sale_date'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['sale_date', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.sale_date,
|
||||
}));
|
||||
}
|
||||
};
|
||||
408
backend/src/db/api/shifts.js
Normal file
408
backend/src/db/api/shifts.js
Normal file
@ -0,0 +1,408 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class ShiftsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const shifts = await db.shifts.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
start_time: data.start_time || null,
|
||||
end_time: data.end_time || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await shifts.setStaff(data.staff || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await shifts.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const shiftsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
start_time: item.start_time || null,
|
||||
end_time: item.end_time || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const shifts = await db.shifts.bulkCreate(shiftsData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const shifts = await db.shifts.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.start_time !== undefined)
|
||||
updatePayload.start_time = data.start_time;
|
||||
|
||||
if (data.end_time !== undefined) updatePayload.end_time = data.end_time;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await shifts.update(updatePayload, { transaction });
|
||||
|
||||
if (data.staff !== undefined) {
|
||||
await shifts.setStaff(
|
||||
data.staff,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await shifts.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const shifts = await db.shifts.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of shifts) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of shifts) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const shifts = await db.shifts.findByPk(id, options);
|
||||
|
||||
await shifts.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await shifts.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const shifts = await db.shifts.findOne({ where }, { transaction });
|
||||
|
||||
if (!shifts) {
|
||||
return shifts;
|
||||
}
|
||||
|
||||
const output = shifts.get({ plain: true });
|
||||
|
||||
output.staff = await shifts.getStaff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await shifts.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.staff,
|
||||
as: 'staff',
|
||||
|
||||
where: filter.staff
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.staff
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{
|
||||
start_time: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
{
|
||||
end_time: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.start_timeRange) {
|
||||
const [start, end] = filter.start_timeRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
start_time: {
|
||||
...where.start_time,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
start_time: {
|
||||
...where.start_time,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.end_timeRange) {
|
||||
const [start, end] = filter.end_timeRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
end_time: {
|
||||
...where.end_time,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
end_time: {
|
||||
...where.end_time,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.shifts.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('shifts', 'start_time', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.shifts.findAll({
|
||||
attributes: ['id', 'start_time'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['start_time', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.start_time,
|
||||
}));
|
||||
}
|
||||
};
|
||||
641
backend/src/db/api/staff.js
Normal file
641
backend/src/db/api/staff.js
Normal file
@ -0,0 +1,641 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class StaffDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const staff = await db.staff.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
first_name: data.first_name || null,
|
||||
last_name: data.last_name || null,
|
||||
email: data.email || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await staff.setUser(data.user || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await staff.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await staff.setShifts(data.shifts || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await staff.setAppointments(data.appointments || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await staff.setSales(data.sales || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await staff.setLocker_assignments(data.locker_assignments || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await staff.setEquipment_maintenance(data.equipment_maintenance || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return staff;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const staffData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
first_name: item.first_name || null,
|
||||
last_name: item.last_name || null,
|
||||
email: item.email || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const staff = await db.staff.bulkCreate(staffData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return staff;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const staff = await db.staff.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.first_name !== undefined)
|
||||
updatePayload.first_name = data.first_name;
|
||||
|
||||
if (data.last_name !== undefined) updatePayload.last_name = data.last_name;
|
||||
|
||||
if (data.email !== undefined) updatePayload.email = data.email;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await staff.update(updatePayload, { transaction });
|
||||
|
||||
if (data.user !== undefined) {
|
||||
await staff.setUser(
|
||||
data.user,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await staff.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.shifts !== undefined) {
|
||||
await staff.setShifts(data.shifts, { transaction });
|
||||
}
|
||||
|
||||
if (data.appointments !== undefined) {
|
||||
await staff.setAppointments(data.appointments, { transaction });
|
||||
}
|
||||
|
||||
if (data.sales !== undefined) {
|
||||
await staff.setSales(data.sales, { transaction });
|
||||
}
|
||||
|
||||
if (data.locker_assignments !== undefined) {
|
||||
await staff.setLocker_assignments(data.locker_assignments, {
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.equipment_maintenance !== undefined) {
|
||||
await staff.setEquipment_maintenance(data.equipment_maintenance, {
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
|
||||
return staff;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const staff = await db.staff.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of staff) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of staff) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return staff;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const staff = await db.staff.findByPk(id, options);
|
||||
|
||||
await staff.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await staff.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return staff;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const staff = await db.staff.findOne({ where }, { transaction });
|
||||
|
||||
if (!staff) {
|
||||
return staff;
|
||||
}
|
||||
|
||||
const output = staff.get({ plain: true });
|
||||
|
||||
output.appointments_staff = await staff.getAppointments_staff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.equipment_maintenance_staff =
|
||||
await staff.getEquipment_maintenance_staff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.locker_assignments_staff = await staff.getLocker_assignments_staff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.private_sessions_staff = await staff.getPrivate_sessions_staff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.sales_staff = await staff.getSales_staff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.shifts_staff = await staff.getShifts_staff({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.user = await staff.getUser({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.shifts = await staff.getShifts({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.appointments = await staff.getAppointments({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.sales = await staff.getSales({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.locker_assignments = await staff.getLocker_assignments({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.equipment_maintenance = await staff.getEquipment_maintenance({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await staff.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.users,
|
||||
as: 'user',
|
||||
|
||||
where: filter.user
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.user
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
firstName: {
|
||||
[Op.or]: filter.user
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.shifts,
|
||||
as: 'shifts',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.appointments,
|
||||
as: 'appointments',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.sales,
|
||||
as: 'sales',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.locker_assignments,
|
||||
as: 'locker_assignments',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.equipment_maintenance,
|
||||
as: 'equipment_maintenance',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.first_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('staff', 'first_name', filter.first_name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.last_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('staff', 'last_name', filter.last_name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.email) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('staff', 'email', filter.email),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.shifts) {
|
||||
const searchTerms = filter.shifts.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.shifts,
|
||||
as: 'shifts_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
start_time: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.appointments) {
|
||||
const searchTerms = filter.appointments.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.appointments,
|
||||
as: 'appointments_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
start_time: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.sales) {
|
||||
const searchTerms = filter.sales.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.sales,
|
||||
as: 'sales_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
sale_date: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.locker_assignments) {
|
||||
const searchTerms = filter.locker_assignments.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.locker_assignments,
|
||||
as: 'locker_assignments_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
locker_number: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.equipment_maintenance) {
|
||||
const searchTerms = filter.equipment_maintenance.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.equipment_maintenance,
|
||||
as: 'equipment_maintenance_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
equipment_name: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.staff.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('staff', 'first_name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.staff.findAll({
|
||||
attributes: ['id', 'first_name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['first_name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.first_name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
807
backend/src/db/api/users.js
Normal file
807
backend/src/db/api/users.js
Normal file
@ -0,0 +1,807 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const bcrypt = require('bcrypt');
|
||||
const config = require('../../config');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class UsersDBApi {
|
||||
static async create(data, globalAccess, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.create(
|
||||
{
|
||||
id: data.data.id || undefined,
|
||||
|
||||
firstName: data.data.firstName || null,
|
||||
lastName: data.data.lastName || null,
|
||||
phoneNumber: data.data.phoneNumber || null,
|
||||
email: data.data.email || null,
|
||||
disabled: data.data.disabled || false,
|
||||
|
||||
password: data.data.password || null,
|
||||
emailVerified: data.data.emailVerified || true,
|
||||
|
||||
emailVerificationToken: data.data.emailVerificationToken || null,
|
||||
emailVerificationTokenExpiresAt:
|
||||
data.data.emailVerificationTokenExpiresAt || null,
|
||||
passwordResetToken: data.data.passwordResetToken || null,
|
||||
passwordResetTokenExpiresAt:
|
||||
data.data.passwordResetTokenExpiresAt || null,
|
||||
provider: data.data.provider || null,
|
||||
importHash: data.data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!data.data.app_role) {
|
||||
const role = await db.roles.findOne({
|
||||
where: { name: 'User' },
|
||||
});
|
||||
if (role) {
|
||||
await users.setApp_role(role, {
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await users.setApp_role(data.data.app_role || null, {
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
|
||||
await users.setBranches(data.data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await users.setCustom_permissions(data.data.custom_permissions || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.users.getTableName(),
|
||||
belongsToColumn: 'avatar',
|
||||
belongsToId: users.id,
|
||||
},
|
||||
data.data.avatar,
|
||||
options,
|
||||
);
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const usersData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
firstName: item.firstName || null,
|
||||
lastName: item.lastName || null,
|
||||
phoneNumber: item.phoneNumber || null,
|
||||
email: item.email || null,
|
||||
disabled: item.disabled || false,
|
||||
|
||||
password: item.password || null,
|
||||
emailVerified: item.emailVerified || false,
|
||||
|
||||
emailVerificationToken: item.emailVerificationToken || null,
|
||||
emailVerificationTokenExpiresAt:
|
||||
item.emailVerificationTokenExpiresAt || null,
|
||||
passwordResetToken: item.passwordResetToken || null,
|
||||
passwordResetTokenExpiresAt: item.passwordResetTokenExpiresAt || null,
|
||||
provider: item.provider || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const users = await db.users.bulkCreate(usersData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.users.getTableName(),
|
||||
belongsToColumn: 'avatar',
|
||||
belongsToId: users[i].id,
|
||||
},
|
||||
data[i].avatar,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
static async update(id, data, globalAccess, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.findByPk(id, {}, { transaction });
|
||||
|
||||
if (!data?.app_role) {
|
||||
data.app_role = users?.app_role?.id;
|
||||
}
|
||||
if (!data?.custom_permissions) {
|
||||
data.custom_permissions = users?.custom_permissions?.map(
|
||||
(item) => item.id,
|
||||
);
|
||||
}
|
||||
|
||||
if (data.password) {
|
||||
data.password = bcrypt.hashSync(data.password, config.bcrypt.saltRounds);
|
||||
} else {
|
||||
data.password = users.password;
|
||||
}
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.firstName !== undefined) updatePayload.firstName = data.firstName;
|
||||
|
||||
if (data.lastName !== undefined) updatePayload.lastName = data.lastName;
|
||||
|
||||
if (data.phoneNumber !== undefined)
|
||||
updatePayload.phoneNumber = data.phoneNumber;
|
||||
|
||||
if (data.email !== undefined) updatePayload.email = data.email;
|
||||
|
||||
if (data.disabled !== undefined) updatePayload.disabled = data.disabled;
|
||||
|
||||
if (data.password !== undefined) updatePayload.password = data.password;
|
||||
|
||||
if (data.emailVerified !== undefined)
|
||||
updatePayload.emailVerified = data.emailVerified;
|
||||
else updatePayload.emailVerified = true;
|
||||
|
||||
if (data.emailVerificationToken !== undefined)
|
||||
updatePayload.emailVerificationToken = data.emailVerificationToken;
|
||||
|
||||
if (data.emailVerificationTokenExpiresAt !== undefined)
|
||||
updatePayload.emailVerificationTokenExpiresAt =
|
||||
data.emailVerificationTokenExpiresAt;
|
||||
|
||||
if (data.passwordResetToken !== undefined)
|
||||
updatePayload.passwordResetToken = data.passwordResetToken;
|
||||
|
||||
if (data.passwordResetTokenExpiresAt !== undefined)
|
||||
updatePayload.passwordResetTokenExpiresAt =
|
||||
data.passwordResetTokenExpiresAt;
|
||||
|
||||
if (data.provider !== undefined) updatePayload.provider = data.provider;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await users.update(updatePayload, { transaction });
|
||||
|
||||
if (data.app_role !== undefined) {
|
||||
await users.setApp_role(
|
||||
data.app_role,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await users.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.custom_permissions !== undefined) {
|
||||
await users.setCustom_permissions(data.custom_permissions, {
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.users.getTableName(),
|
||||
belongsToColumn: 'avatar',
|
||||
belongsToId: users.id,
|
||||
},
|
||||
data.avatar,
|
||||
options,
|
||||
);
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of users) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of users) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.findByPk(id, options);
|
||||
|
||||
await users.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await users.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.findOne({ where }, { transaction });
|
||||
|
||||
if (!users) {
|
||||
return users;
|
||||
}
|
||||
|
||||
const output = users.get({ plain: true });
|
||||
|
||||
output.members_user = await users.getMembers_user({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.staff_user = await users.getStaff_user({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.avatar = await users.getAvatar({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.app_role = await users.getApp_role({
|
||||
transaction,
|
||||
});
|
||||
|
||||
if (output.app_role) {
|
||||
output.app_role_permissions = await output.app_role.getPermissions({
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
|
||||
output.custom_permissions = await users.getCustom_permissions({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await users.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.roles,
|
||||
as: 'app_role',
|
||||
|
||||
where: filter.app_role
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.app_role
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.app_role
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.permissions,
|
||||
as: 'custom_permissions',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.file,
|
||||
as: 'avatar',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.firstName) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('users', 'firstName', filter.firstName),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.lastName) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('users', 'lastName', filter.lastName),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.phoneNumber) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('users', 'phoneNumber', filter.phoneNumber),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.email) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('users', 'email', filter.email),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.password) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('users', 'password', filter.password),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.emailVerificationToken) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'users',
|
||||
'emailVerificationToken',
|
||||
filter.emailVerificationToken,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.passwordResetToken) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'users',
|
||||
'passwordResetToken',
|
||||
filter.passwordResetToken,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.provider) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('users', 'provider', filter.provider),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.emailVerificationTokenExpiresAtRange) {
|
||||
const [start, end] = filter.emailVerificationTokenExpiresAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
emailVerificationTokenExpiresAt: {
|
||||
...where.emailVerificationTokenExpiresAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
emailVerificationTokenExpiresAt: {
|
||||
...where.emailVerificationTokenExpiresAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.passwordResetTokenExpiresAtRange) {
|
||||
const [start, end] = filter.passwordResetTokenExpiresAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
passwordResetTokenExpiresAt: {
|
||||
...where.passwordResetTokenExpiresAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
passwordResetTokenExpiresAt: {
|
||||
...where.passwordResetTokenExpiresAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.disabled) {
|
||||
where = {
|
||||
...where,
|
||||
disabled: filter.disabled,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.emailVerified) {
|
||||
where = {
|
||||
...where,
|
||||
emailVerified: filter.emailVerified,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.custom_permissions) {
|
||||
const searchTerms = filter.custom_permissions.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.permissions,
|
||||
as: 'custom_permissions_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.users.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('users', 'firstName', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.users.findAll({
|
||||
attributes: ['id', 'firstName'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['firstName', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.firstName,
|
||||
}));
|
||||
}
|
||||
|
||||
static async createFromAuth(data, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const users = await db.users.create(
|
||||
{
|
||||
email: data.email,
|
||||
firstName: data.firstName,
|
||||
authenticationUid: data.authenticationUid,
|
||||
password: data.password,
|
||||
|
||||
organizationId: data.organizationId,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const app_role = await db.roles.findOne({
|
||||
where: { name: 'User' },
|
||||
});
|
||||
if (app_role?.id) {
|
||||
await users.setApp_role(app_role?.id || null, {
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
|
||||
await users.update(
|
||||
{
|
||||
authenticationUid: users.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
delete users.password;
|
||||
return users;
|
||||
}
|
||||
|
||||
static async updatePassword(id, password, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.findByPk(id, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await users.update(
|
||||
{
|
||||
password,
|
||||
authenticationUid: id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
static async generateEmailVerificationToken(email, options) {
|
||||
return this._generateToken(
|
||||
['emailVerificationToken', 'emailVerificationTokenExpiresAt'],
|
||||
email,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
static async generatePasswordResetToken(email, options) {
|
||||
return this._generateToken(
|
||||
['passwordResetToken', 'passwordResetTokenExpiresAt'],
|
||||
email,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
static async findByPasswordResetToken(token, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
return db.users.findOne(
|
||||
{
|
||||
where: {
|
||||
passwordResetToken: token,
|
||||
passwordResetTokenExpiresAt: {
|
||||
[db.Sequelize.Op.gt]: Date.now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
static async findByEmailVerificationToken(token, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
return db.users.findOne(
|
||||
{
|
||||
where: {
|
||||
emailVerificationToken: token,
|
||||
emailVerificationTokenExpiresAt: {
|
||||
[db.Sequelize.Op.gt]: Date.now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
static async markEmailVerified(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.findByPk(id, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await users.update(
|
||||
{
|
||||
emailVerified: true,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static async _generateToken(keyNames, email, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const users = await db.users.findOne(
|
||||
{
|
||||
where: { email: email.toLowerCase() },
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
const token = crypto.randomBytes(20).toString('hex');
|
||||
const tokenExpiresAt = Date.now() + 360000;
|
||||
|
||||
if (users) {
|
||||
await users.update(
|
||||
{
|
||||
[keyNames[0]]: token,
|
||||
[keyNames[1]]: tokenExpiresAt,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
};
|
||||
388
backend/src/db/api/workout_logs.js
Normal file
388
backend/src/db/api/workout_logs.js
Normal file
@ -0,0 +1,388 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class Workout_logsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const workout_logs = await db.workout_logs.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
date: data.date || null,
|
||||
workout_details: data.workout_details || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await workout_logs.setMember(data.member || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await workout_logs.setBranches(data.branches || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return workout_logs;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const workout_logsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
date: item.date || null,
|
||||
workout_details: item.workout_details || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const workout_logs = await db.workout_logs.bulkCreate(workout_logsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return workout_logs;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const workout_logs = await db.workout_logs.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.date !== undefined) updatePayload.date = data.date;
|
||||
|
||||
if (data.workout_details !== undefined)
|
||||
updatePayload.workout_details = data.workout_details;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await workout_logs.update(updatePayload, { transaction });
|
||||
|
||||
if (data.member !== undefined) {
|
||||
await workout_logs.setMember(
|
||||
data.member,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.branches !== undefined) {
|
||||
await workout_logs.setBranches(
|
||||
data.branches,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return workout_logs;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const workout_logs = await db.workout_logs.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of workout_logs) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of workout_logs) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return workout_logs;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const workout_logs = await db.workout_logs.findByPk(id, options);
|
||||
|
||||
await workout_logs.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await workout_logs.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return workout_logs;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const workout_logs = await db.workout_logs.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!workout_logs) {
|
||||
return workout_logs;
|
||||
}
|
||||
|
||||
const output = workout_logs.get({ plain: true });
|
||||
|
||||
output.member = await workout_logs.getMember({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.branches = await workout_logs.getBranches({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, globalAccess, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userBranches = (user && user.branches?.id) || null;
|
||||
|
||||
if (userBranches) {
|
||||
if (options?.currentUser?.branchesId) {
|
||||
where.branchesId = options.currentUser.branchesId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.members,
|
||||
as: 'member',
|
||||
|
||||
where: filter.member
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.member
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
first_name: {
|
||||
[Op.or]: filter.member
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.branches,
|
||||
as: 'branches',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.workout_details) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'workout_logs',
|
||||
'workout_details',
|
||||
filter.workout_details,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.dateRange) {
|
||||
const [start, end] = filter.dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
date: {
|
||||
...where.date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
date: {
|
||||
...where.date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.branches) {
|
||||
const listItems = filter.branches.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
branchesId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.branchesId;
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.workout_logs.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
) {
|
||||
let where = {};
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('workout_logs', 'workout_details', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.workout_logs.findAll({
|
||||
attributes: ['id', 'workout_details'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['workout_details', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.workout_details,
|
||||
}));
|
||||
}
|
||||
};
|
||||
31
backend/src/db/db.config.js
Normal file
31
backend/src/db/db.config.js
Normal file
@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
production: {
|
||||
dialect: 'postgres',
|
||||
username: process.env.DB_USER,
|
||||
password: process.env.DB_PASS,
|
||||
database: process.env.DB_NAME,
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
logging: console.log,
|
||||
seederStorage: 'sequelize',
|
||||
},
|
||||
development: {
|
||||
username: 'postgres',
|
||||
dialect: 'postgres',
|
||||
password: '',
|
||||
database: 'db_gas',
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
logging: console.log,
|
||||
seederStorage: 'sequelize',
|
||||
},
|
||||
dev_stage: {
|
||||
dialect: 'postgres',
|
||||
username: process.env.DB_USER,
|
||||
password: process.env.DB_PASS,
|
||||
database: process.env.DB_NAME,
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
logging: console.log,
|
||||
seederStorage: 'sequelize',
|
||||
},
|
||||
};
|
||||
1623
backend/src/db/migrations/1741628387660.js
Normal file
1623
backend/src/db/migrations/1741628387660.js
Normal file
File diff suppressed because it is too large
Load Diff
77
backend/src/db/models/appointments.js
Normal file
77
backend/src/db/models/appointments.js
Normal file
@ -0,0 +1,77 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const appointments = sequelize.define(
|
||||
'appointments',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
start_time: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
end_time: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
appointments.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.appointments.belongsTo(db.members, {
|
||||
as: 'member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.appointments.belongsTo(db.staff, {
|
||||
as: 'staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.appointments.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.appointments.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.appointments.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return appointments;
|
||||
};
|
||||
153
backend/src/db/models/branches.js
Normal file
153
backend/src/db/models/branches.js
Normal file
@ -0,0 +1,153 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const branches = sequelize.define(
|
||||
'branches',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
branches.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.branches.hasMany(db.users, {
|
||||
as: 'users_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.appointments, {
|
||||
as: 'appointments_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.equipment_maintenance, {
|
||||
as: 'equipment_maintenance_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.locker_assignments, {
|
||||
as: 'locker_assignments_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.medical_reports, {
|
||||
as: 'medical_reports_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.members, {
|
||||
as: 'members_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.private_sessions, {
|
||||
as: 'private_sessions_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.progress_tracks, {
|
||||
as: 'progress_tracks_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.purchases, {
|
||||
as: 'purchases_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.sales, {
|
||||
as: 'sales_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.shifts, {
|
||||
as: 'shifts_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.staff, {
|
||||
as: 'staff_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.branches.hasMany(db.workout_logs, {
|
||||
as: 'workout_logs_branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.branches.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.branches.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return branches;
|
||||
};
|
||||
69
backend/src/db/models/equipment_maintenance.js
Normal file
69
backend/src/db/models/equipment_maintenance.js
Normal file
@ -0,0 +1,69 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const equipment_maintenance = sequelize.define(
|
||||
'equipment_maintenance',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
equipment_name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
maintenance_date: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
equipment_maintenance.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.equipment_maintenance.belongsTo(db.staff, {
|
||||
as: 'staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.equipment_maintenance.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.equipment_maintenance.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.equipment_maintenance.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return equipment_maintenance;
|
||||
};
|
||||
53
backend/src/db/models/file.js
Normal file
53
backend/src/db/models/file.js
Normal file
@ -0,0 +1,53 @@
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const file = sequelize.define(
|
||||
'file',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
belongsTo: DataTypes.STRING(255),
|
||||
belongsToId: DataTypes.UUID,
|
||||
belongsToColumn: DataTypes.STRING(255),
|
||||
name: {
|
||||
type: DataTypes.STRING(2083),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
},
|
||||
sizeInBytes: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
privateUrl: {
|
||||
type: DataTypes.STRING(2083),
|
||||
allowNull: true,
|
||||
},
|
||||
publicUrl: {
|
||||
type: DataTypes.STRING(2083),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
},
|
||||
);
|
||||
|
||||
file.associate = (db) => {
|
||||
db.file.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.file.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return file;
|
||||
};
|
||||
47
backend/src/db/models/index.js
Normal file
47
backend/src/db/models/index.js
Normal file
@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Sequelize = require('sequelize');
|
||||
const basename = path.basename(__filename);
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
const config = require('../db.config')[env];
|
||||
const db = {};
|
||||
|
||||
let sequelize;
|
||||
console.log(env);
|
||||
if (config.use_env_variable) {
|
||||
sequelize = new Sequelize(process.env[config.use_env_variable], config);
|
||||
} else {
|
||||
sequelize = new Sequelize(
|
||||
config.database,
|
||||
config.username,
|
||||
config.password,
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
fs.readdirSync(__dirname)
|
||||
.filter((file) => {
|
||||
return (
|
||||
file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js'
|
||||
);
|
||||
})
|
||||
.forEach((file) => {
|
||||
const model = require(path.join(__dirname, file))(
|
||||
sequelize,
|
||||
Sequelize.DataTypes,
|
||||
);
|
||||
db[model.name] = model;
|
||||
});
|
||||
|
||||
Object.keys(db).forEach((modelName) => {
|
||||
if (db[modelName].associate) {
|
||||
db[modelName].associate(db);
|
||||
}
|
||||
});
|
||||
|
||||
db.sequelize = sequelize;
|
||||
db.Sequelize = Sequelize;
|
||||
|
||||
module.exports = db;
|
||||
73
backend/src/db/models/locker_assignments.js
Normal file
73
backend/src/db/models/locker_assignments.js
Normal file
@ -0,0 +1,73 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const locker_assignments = sequelize.define(
|
||||
'locker_assignments',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
locker_number: {
|
||||
type: DataTypes.INTEGER,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
locker_assignments.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.locker_assignments.belongsTo(db.members, {
|
||||
as: 'member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.locker_assignments.belongsTo(db.staff, {
|
||||
as: 'staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.locker_assignments.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.locker_assignments.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.locker_assignments.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return locker_assignments;
|
||||
};
|
||||
69
backend/src/db/models/medical_reports.js
Normal file
69
backend/src/db/models/medical_reports.js
Normal file
@ -0,0 +1,69 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const medical_reports = sequelize.define(
|
||||
'medical_reports',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
report_name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
expiry_date: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
medical_reports.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.medical_reports.belongsTo(db.members, {
|
||||
as: 'member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.medical_reports.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.medical_reports.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.medical_reports.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return medical_reports;
|
||||
};
|
||||
237
backend/src/db/models/members.js
Normal file
237
backend/src/db/models/members.js
Normal file
@ -0,0 +1,237 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const members = sequelize.define(
|
||||
'members',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
first_name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
last_name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
email: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
members.associate = (db) => {
|
||||
db.members.belongsToMany(db.medical_reports, {
|
||||
as: 'medical_reports',
|
||||
foreignKey: {
|
||||
name: 'members_medical_reportsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersMedical_reportsMedical_reports',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.medical_reports, {
|
||||
as: 'medical_reports_filter',
|
||||
foreignKey: {
|
||||
name: 'members_medical_reportsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersMedical_reportsMedical_reports',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.progress_tracks, {
|
||||
as: 'progress_tracks',
|
||||
foreignKey: {
|
||||
name: 'members_progress_tracksId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersProgress_tracksProgress_tracks',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.progress_tracks, {
|
||||
as: 'progress_tracks_filter',
|
||||
foreignKey: {
|
||||
name: 'members_progress_tracksId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersProgress_tracksProgress_tracks',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.appointments, {
|
||||
as: 'appointments',
|
||||
foreignKey: {
|
||||
name: 'members_appointmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersAppointmentsAppointments',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.appointments, {
|
||||
as: 'appointments_filter',
|
||||
foreignKey: {
|
||||
name: 'members_appointmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersAppointmentsAppointments',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.workout_logs, {
|
||||
as: 'workout_logs',
|
||||
foreignKey: {
|
||||
name: 'members_workout_logsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersWorkout_logsWorkout_logs',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.workout_logs, {
|
||||
as: 'workout_logs_filter',
|
||||
foreignKey: {
|
||||
name: 'members_workout_logsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersWorkout_logsWorkout_logs',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.private_sessions, {
|
||||
as: 'private_sessions',
|
||||
foreignKey: {
|
||||
name: 'members_private_sessionsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersPrivate_sessionsPrivate_sessions',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.private_sessions, {
|
||||
as: 'private_sessions_filter',
|
||||
foreignKey: {
|
||||
name: 'members_private_sessionsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersPrivate_sessionsPrivate_sessions',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.purchases, {
|
||||
as: 'purchases',
|
||||
foreignKey: {
|
||||
name: 'members_purchasesId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersPurchasesPurchases',
|
||||
});
|
||||
|
||||
db.members.belongsToMany(db.purchases, {
|
||||
as: 'purchases_filter',
|
||||
foreignKey: {
|
||||
name: 'members_purchasesId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'membersPurchasesPurchases',
|
||||
});
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.members.hasMany(db.appointments, {
|
||||
as: 'appointments_member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.hasMany(db.locker_assignments, {
|
||||
as: 'locker_assignments_member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.hasMany(db.medical_reports, {
|
||||
as: 'medical_reports_member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.hasMany(db.private_sessions, {
|
||||
as: 'private_sessions_member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.hasMany(db.progress_tracks, {
|
||||
as: 'progress_tracks_member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.hasMany(db.purchases, {
|
||||
as: 'purchases_member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.hasMany(db.workout_logs, {
|
||||
as: 'workout_logs_member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.members.belongsTo(db.users, {
|
||||
as: 'user',
|
||||
foreignKey: {
|
||||
name: 'userId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.members.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.members.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return members;
|
||||
};
|
||||
49
backend/src/db/models/permissions.js
Normal file
49
backend/src/db/models/permissions.js
Normal file
@ -0,0 +1,49 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const permissions = sequelize.define(
|
||||
'permissions',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
permissions.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.permissions.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.permissions.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return permissions;
|
||||
};
|
||||
73
backend/src/db/models/private_sessions.js
Normal file
73
backend/src/db/models/private_sessions.js
Normal file
@ -0,0 +1,73 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const private_sessions = sequelize.define(
|
||||
'private_sessions',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
session_date: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
private_sessions.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.private_sessions.belongsTo(db.members, {
|
||||
as: 'member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.private_sessions.belongsTo(db.staff, {
|
||||
as: 'staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.private_sessions.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.private_sessions.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.private_sessions.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return private_sessions;
|
||||
};
|
||||
65
backend/src/db/models/progress_tracks.js
Normal file
65
backend/src/db/models/progress_tracks.js
Normal file
@ -0,0 +1,65 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const progress_tracks = sequelize.define(
|
||||
'progress_tracks',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
progress_tracks.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.progress_tracks.belongsTo(db.members, {
|
||||
as: 'member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.progress_tracks.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.progress_tracks.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.progress_tracks.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return progress_tracks;
|
||||
};
|
||||
69
backend/src/db/models/purchases.js
Normal file
69
backend/src/db/models/purchases.js
Normal file
@ -0,0 +1,69 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const purchases = sequelize.define(
|
||||
'purchases',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
purchase_date: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
amount: {
|
||||
type: DataTypes.DECIMAL,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
purchases.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.purchases.belongsTo(db.members, {
|
||||
as: 'member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.purchases.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.purchases.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.purchases.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return purchases;
|
||||
};
|
||||
86
backend/src/db/models/roles.js
Normal file
86
backend/src/db/models/roles.js
Normal file
@ -0,0 +1,86 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const roles = sequelize.define(
|
||||
'roles',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
role_customization: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
globalAccess: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
roles.associate = (db) => {
|
||||
db.roles.belongsToMany(db.permissions, {
|
||||
as: 'permissions',
|
||||
foreignKey: {
|
||||
name: 'roles_permissionsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'rolesPermissionsPermissions',
|
||||
});
|
||||
|
||||
db.roles.belongsToMany(db.permissions, {
|
||||
as: 'permissions_filter',
|
||||
foreignKey: {
|
||||
name: 'roles_permissionsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'rolesPermissionsPermissions',
|
||||
});
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.roles.hasMany(db.users, {
|
||||
as: 'users_app_role',
|
||||
foreignKey: {
|
||||
name: 'app_roleId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.roles.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.roles.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return roles;
|
||||
};
|
||||
69
backend/src/db/models/sales.js
Normal file
69
backend/src/db/models/sales.js
Normal file
@ -0,0 +1,69 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const sales = sequelize.define(
|
||||
'sales',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
sale_date: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
total_amount: {
|
||||
type: DataTypes.DECIMAL,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
sales.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.sales.belongsTo(db.staff, {
|
||||
as: 'staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.sales.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.sales.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.sales.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return sales;
|
||||
};
|
||||
69
backend/src/db/models/shifts.js
Normal file
69
backend/src/db/models/shifts.js
Normal file
@ -0,0 +1,69 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const shifts = sequelize.define(
|
||||
'shifts',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
start_time: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
end_time: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
shifts.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.shifts.belongsTo(db.staff, {
|
||||
as: 'staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.shifts.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.shifts.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.shifts.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return shifts;
|
||||
};
|
||||
211
backend/src/db/models/staff.js
Normal file
211
backend/src/db/models/staff.js
Normal file
@ -0,0 +1,211 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const staff = sequelize.define(
|
||||
'staff',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
first_name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
last_name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
email: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
staff.associate = (db) => {
|
||||
db.staff.belongsToMany(db.shifts, {
|
||||
as: 'shifts',
|
||||
foreignKey: {
|
||||
name: 'staff_shiftsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffShiftsShifts',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.shifts, {
|
||||
as: 'shifts_filter',
|
||||
foreignKey: {
|
||||
name: 'staff_shiftsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffShiftsShifts',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.appointments, {
|
||||
as: 'appointments',
|
||||
foreignKey: {
|
||||
name: 'staff_appointmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffAppointmentsAppointments',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.appointments, {
|
||||
as: 'appointments_filter',
|
||||
foreignKey: {
|
||||
name: 'staff_appointmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffAppointmentsAppointments',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.sales, {
|
||||
as: 'sales',
|
||||
foreignKey: {
|
||||
name: 'staff_salesId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffSalesSales',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.sales, {
|
||||
as: 'sales_filter',
|
||||
foreignKey: {
|
||||
name: 'staff_salesId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffSalesSales',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.locker_assignments, {
|
||||
as: 'locker_assignments',
|
||||
foreignKey: {
|
||||
name: 'staff_locker_assignmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffLocker_assignmentsLocker_assignments',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.locker_assignments, {
|
||||
as: 'locker_assignments_filter',
|
||||
foreignKey: {
|
||||
name: 'staff_locker_assignmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffLocker_assignmentsLocker_assignments',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.equipment_maintenance, {
|
||||
as: 'equipment_maintenance',
|
||||
foreignKey: {
|
||||
name: 'staff_equipment_maintenanceId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffEquipment_maintenanceEquipment_maintenance',
|
||||
});
|
||||
|
||||
db.staff.belongsToMany(db.equipment_maintenance, {
|
||||
as: 'equipment_maintenance_filter',
|
||||
foreignKey: {
|
||||
name: 'staff_equipment_maintenanceId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'staffEquipment_maintenanceEquipment_maintenance',
|
||||
});
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.staff.hasMany(db.appointments, {
|
||||
as: 'appointments_staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.staff.hasMany(db.equipment_maintenance, {
|
||||
as: 'equipment_maintenance_staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.staff.hasMany(db.locker_assignments, {
|
||||
as: 'locker_assignments_staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.staff.hasMany(db.private_sessions, {
|
||||
as: 'private_sessions_staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.staff.hasMany(db.sales, {
|
||||
as: 'sales_staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.staff.hasMany(db.shifts, {
|
||||
as: 'shifts_staff',
|
||||
foreignKey: {
|
||||
name: 'staffId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.staff.belongsTo(db.users, {
|
||||
as: 'user',
|
||||
foreignKey: {
|
||||
name: 'userId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.staff.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.staff.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.staff.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return staff;
|
||||
};
|
||||
195
backend/src/db/models/users.js
Normal file
195
backend/src/db/models/users.js
Normal file
@ -0,0 +1,195 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const users = sequelize.define(
|
||||
'users',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
firstName: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
lastName: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
phoneNumber: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
email: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
disabled: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
password: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
emailVerified: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
emailVerificationToken: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
emailVerificationTokenExpiresAt: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
passwordResetToken: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
passwordResetTokenExpiresAt: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
provider: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
users.associate = (db) => {
|
||||
db.users.belongsToMany(db.permissions, {
|
||||
as: 'custom_permissions',
|
||||
foreignKey: {
|
||||
name: 'users_custom_permissionsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'usersCustom_permissionsPermissions',
|
||||
});
|
||||
|
||||
db.users.belongsToMany(db.permissions, {
|
||||
as: 'custom_permissions_filter',
|
||||
foreignKey: {
|
||||
name: 'users_custom_permissionsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'usersCustom_permissionsPermissions',
|
||||
});
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.users.hasMany(db.members, {
|
||||
as: 'members_user',
|
||||
foreignKey: {
|
||||
name: 'userId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.users.hasMany(db.staff, {
|
||||
as: 'staff_user',
|
||||
foreignKey: {
|
||||
name: 'userId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.users.belongsTo(db.roles, {
|
||||
as: 'app_role',
|
||||
foreignKey: {
|
||||
name: 'app_roleId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.users.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.users.hasMany(db.file, {
|
||||
as: 'avatar',
|
||||
foreignKey: 'belongsToId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
belongsTo: db.users.getTableName(),
|
||||
belongsToColumn: 'avatar',
|
||||
},
|
||||
});
|
||||
|
||||
db.users.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.users.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
users.beforeCreate((users, options) => {
|
||||
users = trimStringFields(users);
|
||||
|
||||
if (
|
||||
users.provider !== providers.LOCAL &&
|
||||
Object.values(providers).indexOf(users.provider) > -1
|
||||
) {
|
||||
users.emailVerified = true;
|
||||
|
||||
if (!users.password) {
|
||||
const password = crypto.randomBytes(20).toString('hex');
|
||||
|
||||
const hashedPassword = bcrypt.hashSync(
|
||||
password,
|
||||
config.bcrypt.saltRounds,
|
||||
);
|
||||
|
||||
users.password = hashedPassword;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
users.beforeUpdate((users, options) => {
|
||||
users = trimStringFields(users);
|
||||
});
|
||||
|
||||
return users;
|
||||
};
|
||||
|
||||
function trimStringFields(users) {
|
||||
users.email = users.email.trim();
|
||||
|
||||
users.firstName = users.firstName ? users.firstName.trim() : null;
|
||||
|
||||
users.lastName = users.lastName ? users.lastName.trim() : null;
|
||||
|
||||
return users;
|
||||
}
|
||||
69
backend/src/db/models/workout_logs.js
Normal file
69
backend/src/db/models/workout_logs.js
Normal file
@ -0,0 +1,69 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const workout_logs = sequelize.define(
|
||||
'workout_logs',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
date: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
workout_details: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
workout_logs.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.workout_logs.belongsTo(db.members, {
|
||||
as: 'member',
|
||||
foreignKey: {
|
||||
name: 'memberId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.workout_logs.belongsTo(db.branches, {
|
||||
as: 'branches',
|
||||
foreignKey: {
|
||||
name: 'branchesId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.workout_logs.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.workout_logs.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return workout_logs;
|
||||
};
|
||||
16
backend/src/db/reset.js
Normal file
16
backend/src/db/reset.js
Normal file
@ -0,0 +1,16 @@
|
||||
const db = require('./models');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
console.log('Resetting Database');
|
||||
|
||||
db.sequelize
|
||||
.sync({ force: true })
|
||||
.then(() => {
|
||||
execSync('sequelize db:seed:all');
|
||||
console.log('OK');
|
||||
process.exit();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
80
backend/src/db/seeders/20200430130759-admin-user.js
Normal file
80
backend/src/db/seeders/20200430130759-admin-user.js
Normal file
@ -0,0 +1,80 @@
|
||||
'use strict';
|
||||
const bcrypt = require('bcrypt');
|
||||
const config = require('../../config');
|
||||
|
||||
const ids = [
|
||||
'193bf4b5-9f07-4bd5-9a43-e7e41f3e96af',
|
||||
'af5a87be-8f9c-4630-902a-37a60b7005ba',
|
||||
'5bc531ab-611f-41f3-9373-b7cc5d09c93d',
|
||||
'ab4cf9bf-4eef-4107-b73d-9d0274cf69bc',
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
let hash = bcrypt.hashSync(config.admin_pass, config.bcrypt.saltRounds);
|
||||
|
||||
try {
|
||||
await queryInterface.bulkInsert('users', [
|
||||
{
|
||||
id: ids[0],
|
||||
firstName: 'Admin',
|
||||
email: config.admin_email,
|
||||
emailVerified: true,
|
||||
provider: config.providers.LOCAL,
|
||||
password: hash,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: ids[1],
|
||||
firstName: 'John',
|
||||
email: 'john@doe.com',
|
||||
emailVerified: true,
|
||||
provider: config.providers.LOCAL,
|
||||
password: hash,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: ids[2],
|
||||
firstName: 'Client',
|
||||
email: 'client@hello.com',
|
||||
emailVerified: true,
|
||||
provider: config.providers.LOCAL,
|
||||
password: hash,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: ids[3],
|
||||
firstName: 'Super Admin',
|
||||
email: 'super_admin@flatlogic.com',
|
||||
emailVerified: true,
|
||||
provider: config.providers.LOCAL,
|
||||
password: hash,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error('Error during bulkInsert:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
try {
|
||||
await queryInterface.bulkDelete(
|
||||
'users',
|
||||
{
|
||||
id: {
|
||||
[Sequelize.Op.in]: ids,
|
||||
},
|
||||
},
|
||||
{},
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error during bulkDelete:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
1975
backend/src/db/seeders/20200430130760-user-roles.js
Normal file
1975
backend/src/db/seeders/20200430130760-user-roles.js
Normal file
File diff suppressed because it is too large
Load Diff
1648
backend/src/db/seeders/20231127130745-sample-data.js
Normal file
1648
backend/src/db/seeders/20231127130745-sample-data.js
Normal file
File diff suppressed because it is too large
Load Diff
24
backend/src/db/utils.js
Normal file
24
backend/src/db/utils.js
Normal file
@ -0,0 +1,24 @@
|
||||
const validator = require('validator');
|
||||
const { v4: uuid } = require('uuid');
|
||||
const Sequelize = require('./models').Sequelize;
|
||||
|
||||
module.exports = class Utils {
|
||||
static uuid(value) {
|
||||
let id = value;
|
||||
|
||||
if (!validator.isUUID(id)) {
|
||||
id = uuid();
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static ilike(model, column, value) {
|
||||
return Sequelize.where(
|
||||
Sequelize.fn('lower', Sequelize.col(`${model}.${column}`)),
|
||||
{
|
||||
[Sequelize.Op.like]: `%${value}%`.toLowerCase(),
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
23
backend/src/helpers.js
Normal file
23
backend/src/helpers.js
Normal file
@ -0,0 +1,23 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const config = require('./config');
|
||||
|
||||
module.exports = class Helpers {
|
||||
static wrapAsync(fn) {
|
||||
return function (req, res, next) {
|
||||
fn(req, res, next).catch(next);
|
||||
};
|
||||
}
|
||||
|
||||
static commonErrorHandler(error, req, res, next) {
|
||||
if ([400, 403, 404].includes(error.code)) {
|
||||
return res.status(error.code).send(error.message);
|
||||
}
|
||||
|
||||
console.error(error);
|
||||
return res.status(500).send(error.message);
|
||||
}
|
||||
|
||||
static jwtSign(data) {
|
||||
return jwt.sign(data, config.secret_key, { expiresIn: '6h' });
|
||||
}
|
||||
};
|
||||
245
backend/src/index.js
Normal file
245
backend/src/index.js
Normal file
@ -0,0 +1,245 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const app = express();
|
||||
const passport = require('passport');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const bodyParser = require('body-parser');
|
||||
const db = require('./db/models');
|
||||
const config = require('./config');
|
||||
const swaggerUI = require('swagger-ui-express');
|
||||
const swaggerJsDoc = require('swagger-jsdoc');
|
||||
|
||||
const authRoutes = require('./routes/auth');
|
||||
const fileRoutes = require('./routes/file');
|
||||
const searchRoutes = require('./routes/search');
|
||||
const pexelsRoutes = require('./routes/pexels');
|
||||
|
||||
const organizationForAuthRoutes = require('./routes/organizationLogin');
|
||||
|
||||
const openaiRoutes = require('./routes/openai');
|
||||
|
||||
const contactFormRoutes = require('./routes/contactForm');
|
||||
|
||||
const usersRoutes = require('./routes/users');
|
||||
|
||||
const appointmentsRoutes = require('./routes/appointments');
|
||||
|
||||
const equipment_maintenanceRoutes = require('./routes/equipment_maintenance');
|
||||
|
||||
const locker_assignmentsRoutes = require('./routes/locker_assignments');
|
||||
|
||||
const medical_reportsRoutes = require('./routes/medical_reports');
|
||||
|
||||
const membersRoutes = require('./routes/members');
|
||||
|
||||
const private_sessionsRoutes = require('./routes/private_sessions');
|
||||
|
||||
const progress_tracksRoutes = require('./routes/progress_tracks');
|
||||
|
||||
const purchasesRoutes = require('./routes/purchases');
|
||||
|
||||
const salesRoutes = require('./routes/sales');
|
||||
|
||||
const shiftsRoutes = require('./routes/shifts');
|
||||
|
||||
const staffRoutes = require('./routes/staff');
|
||||
|
||||
const workout_logsRoutes = require('./routes/workout_logs');
|
||||
|
||||
const rolesRoutes = require('./routes/roles');
|
||||
|
||||
const permissionsRoutes = require('./routes/permissions');
|
||||
|
||||
const branchesRoutes = require('./routes/branches');
|
||||
|
||||
const options = {
|
||||
definition: {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
version: '1.0.0',
|
||||
title: 'gas',
|
||||
description:
|
||||
'gas Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.',
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: config.swaggerUrl,
|
||||
description: 'Development server',
|
||||
},
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
bearerAuth: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
UnauthorizedError: {
|
||||
description: 'Access token is missing or invalid',
|
||||
},
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{
|
||||
bearerAuth: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
apis: ['./src/routes/*.js'],
|
||||
};
|
||||
|
||||
const specs = swaggerJsDoc(options);
|
||||
app.use(
|
||||
'/api-docs',
|
||||
function (req, res, next) {
|
||||
swaggerUI.host = req.get('host');
|
||||
next();
|
||||
},
|
||||
swaggerUI.serve,
|
||||
swaggerUI.setup(specs),
|
||||
);
|
||||
|
||||
app.use(cors({ origin: true }));
|
||||
require('./auth/auth');
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/file', fileRoutes);
|
||||
app.use('/api/pexels', pexelsRoutes);
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(
|
||||
'/api/users',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
usersRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/appointments',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
appointmentsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/equipment_maintenance',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
equipment_maintenanceRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/locker_assignments',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
locker_assignmentsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/medical_reports',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
medical_reportsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/members',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
membersRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/private_sessions',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
private_sessionsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/progress_tracks',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
progress_tracksRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/purchases',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
purchasesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/sales',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
salesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/shifts',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
shiftsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/staff',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
staffRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/workout_logs',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
workout_logsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/roles',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
rolesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/permissions',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
permissionsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/branches',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
branchesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/openai',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
openaiRoutes,
|
||||
);
|
||||
|
||||
app.use('/api/contact-form', contactFormRoutes);
|
||||
|
||||
app.use(
|
||||
'/api/search',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
searchRoutes,
|
||||
);
|
||||
|
||||
app.use('/api/org-for-auth', organizationForAuthRoutes);
|
||||
|
||||
const publicDir = path.join(__dirname, '../public');
|
||||
|
||||
if (fs.existsSync(publicDir)) {
|
||||
app.use('/', express.static(publicDir));
|
||||
|
||||
app.get('*', function (request, response) {
|
||||
response.sendFile(path.resolve(publicDir, 'index.html'));
|
||||
});
|
||||
}
|
||||
|
||||
const PORT = process.env.NODE_ENV === 'dev_stage' ? 3000 : 8080;
|
||||
|
||||
db.sequelize.sync().then(function () {
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Listening on port ${PORT}`);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
64
backend/src/middlewares/check-permissions.js
Normal file
64
backend/src/middlewares/check-permissions.js
Normal file
@ -0,0 +1,64 @@
|
||||
const ValidationError = require('../services/notifications/errors/validation');
|
||||
|
||||
/**
|
||||
* @param {string} permission
|
||||
* @return {import("express").RequestHandler}
|
||||
*/
|
||||
function checkPermissions(permission) {
|
||||
return (req, res, next) => {
|
||||
const { currentUser } = req;
|
||||
if (currentUser) {
|
||||
if (currentUser.id === req.params.id || currentUser.id === req.body.id) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
const userPermission = currentUser.custom_permissions.find(
|
||||
(cp) => cp.name === permission,
|
||||
);
|
||||
|
||||
if (userPermission) {
|
||||
next();
|
||||
} else {
|
||||
if (!currentUser.app_role) {
|
||||
return next(new ValidationError('auth.forbidden'));
|
||||
}
|
||||
currentUser.app_role
|
||||
.getPermissions()
|
||||
.then((permissions) => {
|
||||
if (permissions.find((p) => p.name === permission)) {
|
||||
next();
|
||||
} else {
|
||||
next(new ValidationError('auth.forbidden'));
|
||||
}
|
||||
})
|
||||
.catch((e) => next(e));
|
||||
}
|
||||
} else {
|
||||
next(new ValidationError('auth.unauthorized'));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const METHOD_MAP = {
|
||||
POST: 'CREATE',
|
||||
GET: 'READ',
|
||||
PUT: 'UPDATE',
|
||||
PATCH: 'UPDATE',
|
||||
DELETE: 'DELETE',
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {import("express").RequestHandler}
|
||||
*/
|
||||
function checkCrudPermissions(name) {
|
||||
return (req, res, next) => {
|
||||
const permissionName = `${METHOD_MAP[req.method]}_${name.toUpperCase()}`;
|
||||
checkPermissions(permissionName)(req, res, next);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkPermissions,
|
||||
checkCrudPermissions,
|
||||
};
|
||||
11
backend/src/middlewares/upload.js
Normal file
11
backend/src/middlewares/upload.js
Normal file
@ -0,0 +1,11 @@
|
||||
const util = require('util');
|
||||
const Multer = require('multer');
|
||||
const maxSize = 10 * 1024 * 1024;
|
||||
|
||||
let processFile = Multer({
|
||||
storage: Multer.memoryStorage(),
|
||||
limits: { fileSize: maxSize },
|
||||
}).single('file');
|
||||
|
||||
let processFileMiddleware = util.promisify(processFile);
|
||||
module.exports = processFileMiddleware;
|
||||
452
backend/src/routes/appointments.js
Normal file
452
backend/src/routes/appointments.js
Normal file
@ -0,0 +1,452 @@
|
||||
const express = require('express');
|
||||
|
||||
const AppointmentsService = require('../services/appointments');
|
||||
const AppointmentsDBApi = require('../db/api/appointments');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('appointments'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Appointments:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Appointments
|
||||
* description: The Appointments managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await AppointmentsService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await AppointmentsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await AppointmentsService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await AppointmentsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await AppointmentsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Get all appointments
|
||||
* description: Get all appointments
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Appointments list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await AppointmentsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'start_time', 'end_time'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Count all appointments
|
||||
* description: Count all appointments
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Appointments count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await AppointmentsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Find all appointments that match search criteria
|
||||
* description: Find all appointments that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Appointments list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await AppointmentsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/appointments/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Appointments]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Appointments"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await AppointmentsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
270
backend/src/routes/auth.js
Normal file
270
backend/src/routes/auth.js
Normal file
@ -0,0 +1,270 @@
|
||||
const express = require('express');
|
||||
const passport = require('passport');
|
||||
|
||||
const config = require('../config');
|
||||
const AuthService = require('../services/auth');
|
||||
const ForbiddenError = require('../services/notifications/errors/forbidden');
|
||||
const EmailSender = require('../services/email');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Auth:
|
||||
* type: object
|
||||
* required:
|
||||
* - email
|
||||
* - password
|
||||
* properties:
|
||||
* email:
|
||||
* type: string
|
||||
* default: admin@flatlogic.com
|
||||
* description: User email
|
||||
* password:
|
||||
* type: string
|
||||
* default: password
|
||||
* description: User password
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Auth
|
||||
* description: Authorization operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/signin/local:
|
||||
* post:
|
||||
* tags: [Auth]
|
||||
* summary: Logs user into the system
|
||||
* description: Logs user into the system
|
||||
* requestBody:
|
||||
* description: Set valid user email and password
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Auth"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Successful login
|
||||
* 400:
|
||||
* description: Invalid username/password supplied
|
||||
* x-codegen-request-body-name: body
|
||||
*/
|
||||
|
||||
router.post(
|
||||
'/signin/local',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await AuthService.signin(
|
||||
req.body.email,
|
||||
req.body.password,
|
||||
req,
|
||||
);
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/me:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Auth]
|
||||
* summary: Get current authorized user info
|
||||
* description: Get current authorized user info
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Successful retrieval of current authorized user data
|
||||
* 400:
|
||||
* description: Invalid username/password supplied
|
||||
* x-codegen-request-body-name: body
|
||||
*/
|
||||
|
||||
router.get(
|
||||
'/me',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
(req, res) => {
|
||||
if (!req.currentUser || !req.currentUser.id) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
|
||||
const payload = req.currentUser;
|
||||
delete payload.password;
|
||||
res.status(200).send(payload);
|
||||
},
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/password-reset',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await AuthService.passwordReset(
|
||||
req.body.token,
|
||||
req.body.password,
|
||||
req,
|
||||
);
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/password-update',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await AuthService.passwordUpdate(
|
||||
req.body.currentPassword,
|
||||
req.body.newPassword,
|
||||
req,
|
||||
);
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/send-email-address-verification-email',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
wrapAsync(async (req, res) => {
|
||||
if (!req.currentUser) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
|
||||
await AuthService.sendEmailAddressVerificationEmail(req.currentUser.email);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/send-password-reset-email',
|
||||
wrapAsync(async (req, res) => {
|
||||
const link = new URL(req.headers.referer);
|
||||
await AuthService.sendPasswordResetEmail(
|
||||
req.body.email,
|
||||
'register',
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/signup:
|
||||
* post:
|
||||
* tags: [Auth]
|
||||
* summary: Register new user into the system
|
||||
* description: Register new user into the system
|
||||
* requestBody:
|
||||
* description: Set valid user email and password
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Auth"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: New user successfully signed up
|
||||
* 400:
|
||||
* description: Invalid username/password supplied
|
||||
* 500:
|
||||
* description: Some server error
|
||||
* x-codegen-request-body-name: body
|
||||
*/
|
||||
|
||||
router.post(
|
||||
'/signup',
|
||||
wrapAsync(async (req, res) => {
|
||||
const link = new URL(req.headers.referer);
|
||||
const payload = await AuthService.signup(
|
||||
req.body.email,
|
||||
req.body.password,
|
||||
|
||||
req.body.organizationId,
|
||||
|
||||
req,
|
||||
link.host,
|
||||
);
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/profile',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
wrapAsync(async (req, res) => {
|
||||
if (!req.currentUser || !req.currentUser.id) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
|
||||
await AuthService.updateProfile(req.body.profile, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/verify-email',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await AuthService.verifyEmail(
|
||||
req.body.token,
|
||||
req,
|
||||
req.headers.referer,
|
||||
);
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.get('/email-configured', (req, res) => {
|
||||
const payload = EmailSender.isConfigured;
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
router.get('/signin/google', (req, res, next) => {
|
||||
passport.authenticate('google', {
|
||||
scope: ['profile', 'email'],
|
||||
state: req.query.app,
|
||||
})(req, res, next);
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/signin/google/callback',
|
||||
passport.authenticate('google', {
|
||||
failureRedirect: '/login',
|
||||
session: false,
|
||||
}),
|
||||
|
||||
function (req, res) {
|
||||
socialRedirect(res, req.query.state, req.user.token, config);
|
||||
},
|
||||
);
|
||||
|
||||
router.get('/signin/microsoft', (req, res, next) => {
|
||||
passport.authenticate('microsoft', {
|
||||
scope: ['https://graph.microsoft.com/user.read openid'],
|
||||
state: req.query.app,
|
||||
})(req, res, next);
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/signin/microsoft/callback',
|
||||
passport.authenticate('microsoft', {
|
||||
failureRedirect: '/login',
|
||||
session: false,
|
||||
}),
|
||||
function (req, res) {
|
||||
socialRedirect(res, req.query.state, req.user.token, config);
|
||||
},
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
function socialRedirect(res, state, token, config) {
|
||||
res.redirect(config.uiUrl + '/login?token=' + token);
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
452
backend/src/routes/branches.js
Normal file
452
backend/src/routes/branches.js
Normal file
@ -0,0 +1,452 @@
|
||||
const express = require('express');
|
||||
|
||||
const BranchesService = require('../services/branches');
|
||||
const BranchesDBApi = require('../db/api/branches');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('branches'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Branches:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Branches
|
||||
* description: The Branches managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await BranchesService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await BranchesService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await BranchesService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await BranchesService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await BranchesService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Get all branches
|
||||
* description: Get all branches
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Branches list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await BranchesDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'name'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Count all branches
|
||||
* description: Count all branches
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Branches count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await BranchesDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Find all branches that match search criteria
|
||||
* description: Find all branches that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Branches list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await BranchesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/branches/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Branches]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Branches"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await BranchesDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
33
backend/src/routes/contactForm.js
Normal file
33
backend/src/routes/contactForm.js
Normal file
@ -0,0 +1,33 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const EmailSender = require('../services/email');
|
||||
|
||||
router.post('/send', async (req, res) => {
|
||||
try {
|
||||
const { email, subject, message } = req.body;
|
||||
|
||||
if (!email || !subject || !message) {
|
||||
return res.status(400).json({ error: 'All fields are required' });
|
||||
}
|
||||
|
||||
const emailSender = new EmailSender({
|
||||
to: 'fitworldegypt@gmail.com',
|
||||
subject: subject,
|
||||
html: () => `
|
||||
<p><strong>From:</strong> ${email}</p>
|
||||
<p><strong>Subject:</strong> ${subject}</p>
|
||||
<p><strong>Message:</strong></p>
|
||||
<p>${message}</p>
|
||||
`,
|
||||
text: () => `From: ${email}\nSubject: ${subject}\nMessage:\n${message}`,
|
||||
});
|
||||
|
||||
await emailSender.send();
|
||||
res.status(200).json({ message: 'Email sent successfully' });
|
||||
} catch (error) {
|
||||
console.error('Error sending email:', error);
|
||||
res.status(500).json({ error: 'Error sending email' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
464
backend/src/routes/equipment_maintenance.js
Normal file
464
backend/src/routes/equipment_maintenance.js
Normal file
@ -0,0 +1,464 @@
|
||||
const express = require('express');
|
||||
|
||||
const Equipment_maintenanceService = require('../services/equipment_maintenance');
|
||||
const Equipment_maintenanceDBApi = require('../db/api/equipment_maintenance');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('equipment_maintenance'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Equipment_maintenance:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* equipment_name:
|
||||
* type: string
|
||||
* default: equipment_name
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Equipment_maintenance
|
||||
* description: The Equipment_maintenance managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Equipment_maintenanceService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Equipment_maintenanceService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Equipment_maintenanceService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Equipment_maintenanceService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Equipment_maintenanceService.deleteByIds(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Get all equipment_maintenance
|
||||
* description: Get all equipment_maintenance
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Equipment_maintenance list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Equipment_maintenanceDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ currentUser },
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'equipment_name', 'maintenance_date'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Count all equipment_maintenance
|
||||
* description: Count all equipment_maintenance
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Equipment_maintenance count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Equipment_maintenanceDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ countOnly: true, currentUser },
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Find all equipment_maintenance that match search criteria
|
||||
* description: Find all equipment_maintenance that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Equipment_maintenance list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await Equipment_maintenanceDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/equipment_maintenance/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Equipment_maintenance]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Equipment_maintenance"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await Equipment_maintenanceDBApi.findBy({
|
||||
id: req.params.id,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
40
backend/src/routes/file.js
Normal file
40
backend/src/routes/file.js
Normal file
@ -0,0 +1,40 @@
|
||||
const express = require('express');
|
||||
const config = require('../config');
|
||||
const path = require('path');
|
||||
const passport = require('passport');
|
||||
const services = require('../services/file');
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/download', (req, res) => {
|
||||
if (
|
||||
process.env.NODE_ENV == 'production' ||
|
||||
process.env.NEXT_PUBLIC_BACK_API
|
||||
) {
|
||||
services.downloadGCloud(req, res);
|
||||
} else {
|
||||
services.downloadLocal(req, res);
|
||||
}
|
||||
});
|
||||
|
||||
router.post(
|
||||
'/upload/:table/:field',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
(req, res) => {
|
||||
const fileName = `${req.params.table}/${req.params.field}`;
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV == 'production' ||
|
||||
process.env.NEXT_PUBLIC_BACK_API
|
||||
) {
|
||||
services.uploadGCloud(fileName, req, res);
|
||||
} else {
|
||||
services.uploadLocal(fileName, {
|
||||
entity: null,
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
folderIncludesAuthenticationUid: false,
|
||||
})(req, res);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
459
backend/src/routes/locker_assignments.js
Normal file
459
backend/src/routes/locker_assignments.js
Normal file
@ -0,0 +1,459 @@
|
||||
const express = require('express');
|
||||
|
||||
const Locker_assignmentsService = require('../services/locker_assignments');
|
||||
const Locker_assignmentsDBApi = require('../db/api/locker_assignments');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('locker_assignments'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Locker_assignments:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* locker_number:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Locker_assignments
|
||||
* description: The Locker_assignments managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Locker_assignmentsService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Locker_assignmentsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Locker_assignmentsService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Locker_assignmentsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Locker_assignmentsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Get all locker_assignments
|
||||
* description: Get all locker_assignments
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Locker_assignments list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Locker_assignmentsDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ currentUser },
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'locker_number'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Count all locker_assignments
|
||||
* description: Count all locker_assignments
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Locker_assignments count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Locker_assignmentsDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ countOnly: true, currentUser },
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Find all locker_assignments that match search criteria
|
||||
* description: Find all locker_assignments that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Locker_assignments list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await Locker_assignmentsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/locker_assignments/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Locker_assignments]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Locker_assignments"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await Locker_assignmentsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
459
backend/src/routes/medical_reports.js
Normal file
459
backend/src/routes/medical_reports.js
Normal file
@ -0,0 +1,459 @@
|
||||
const express = require('express');
|
||||
|
||||
const Medical_reportsService = require('../services/medical_reports');
|
||||
const Medical_reportsDBApi = require('../db/api/medical_reports');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('medical_reports'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Medical_reports:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* report_name:
|
||||
* type: string
|
||||
* default: report_name
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Medical_reports
|
||||
* description: The Medical_reports managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Medical_reportsService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Medical_reportsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Medical_reportsService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Medical_reportsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Medical_reportsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Get all medical_reports
|
||||
* description: Get all medical_reports
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Medical_reports list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Medical_reportsDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ currentUser },
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'report_name', 'expiry_date'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Count all medical_reports
|
||||
* description: Count all medical_reports
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Medical_reports count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Medical_reportsDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ countOnly: true, currentUser },
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Find all medical_reports that match search criteria
|
||||
* description: Find all medical_reports that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Medical_reports list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await Medical_reportsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/medical_reports/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Medical_reports]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Medical_reports"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await Medical_reportsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
458
backend/src/routes/members.js
Normal file
458
backend/src/routes/members.js
Normal file
@ -0,0 +1,458 @@
|
||||
const express = require('express');
|
||||
|
||||
const MembersService = require('../services/members');
|
||||
const MembersDBApi = require('../db/api/members');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('members'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Members:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* first_name:
|
||||
* type: string
|
||||
* default: first_name
|
||||
* last_name:
|
||||
* type: string
|
||||
* default: last_name
|
||||
* email:
|
||||
* type: string
|
||||
* default: email
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Members
|
||||
* description: The Members managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await MembersService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await MembersService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await MembersService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await MembersService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await MembersService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Get all members
|
||||
* description: Get all members
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Members list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await MembersDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'first_name', 'last_name', 'email'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Count all members
|
||||
* description: Count all members
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Members count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await MembersDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Find all members that match search criteria
|
||||
* description: Find all members that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Members list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await MembersDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/members/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Members]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Members"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await MembersDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
180
backend/src/routes/openai.js
Normal file
180
backend/src/routes/openai.js
Normal file
@ -0,0 +1,180 @@
|
||||
const express = require('express');
|
||||
const db = require('../db/models');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
const router = express.Router();
|
||||
const sjs = require('sequelize-json-schema');
|
||||
const { getWidget } = require('../services/openai');
|
||||
const RolesService = require('../services/roles');
|
||||
const RolesDBApi = require('../db/api/roles');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/roles-info/{infoId}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Remove role information by ID
|
||||
* description: Remove specific role information by ID
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: infoId
|
||||
* description: ID of role information to remove
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* - in: query
|
||||
* name: userId
|
||||
* description: ID of the user
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* - in: query
|
||||
* name: key
|
||||
* description: Key of the role information to remove
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Role information successfully removed
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* user:
|
||||
* type: string
|
||||
* description: The user information
|
||||
* 400:
|
||||
* description: Invalid ID or key supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Role not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
|
||||
router.delete(
|
||||
'/roles-info/:infoId',
|
||||
wrapAsync(async (req, res) => {
|
||||
const role = await RolesService.removeRoleInfoById(
|
||||
req.query.infoId,
|
||||
req.query.roleId,
|
||||
req.query.key,
|
||||
req.currentUser,
|
||||
);
|
||||
|
||||
res.status(200).send(role);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/role-info/{roleId}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Get role information by key
|
||||
* description: Get specific role information by key
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: roleId
|
||||
* description: ID of role to get information for
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* - in: query
|
||||
* name: key
|
||||
* description: Key of the role information to retrieve
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Role information successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* info:
|
||||
* type: string
|
||||
* description: The role information
|
||||
* 400:
|
||||
* description: Invalid ID or key supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Role not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
|
||||
router.get(
|
||||
'/info-by-key',
|
||||
wrapAsync(async (req, res) => {
|
||||
const roleId = req.query.roleId;
|
||||
const key = req.query.key;
|
||||
const currentUser = req.currentUser;
|
||||
let info = await RolesService.getRoleInfoByKey(key, roleId, currentUser);
|
||||
const role = await RolesDBApi.findBy({ id: roleId });
|
||||
if (!role?.role_customization) {
|
||||
await Promise.all(
|
||||
['pie', 'bar'].map(async (e) => {
|
||||
const schema = await sjs.getSequelizeSchema(db.sequelize, {});
|
||||
const payload = {
|
||||
description: `Create some cool ${e} chart`,
|
||||
modelDefinition: schema.definitions,
|
||||
};
|
||||
const widgetId = await getWidget(payload, currentUser?.id, roleId);
|
||||
if (widgetId) {
|
||||
await RolesService.addRoleInfo(
|
||||
roleId,
|
||||
currentUser?.id,
|
||||
'widgets',
|
||||
widgetId,
|
||||
req.currentUser,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
info = await RolesService.getRoleInfoByKey(key, roleId, currentUser);
|
||||
}
|
||||
res.status(200).send(info);
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/create_widget',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { description, userId, roleId } = req.body;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const schema = await sjs.getSequelizeSchema(db.sequelize, {});
|
||||
const payload = {
|
||||
description,
|
||||
modelDefinition: schema.definitions,
|
||||
};
|
||||
|
||||
const widgetId = await getWidget(payload, userId, roleId);
|
||||
|
||||
if (widgetId) {
|
||||
await RolesService.addRoleInfo(
|
||||
roleId,
|
||||
userId,
|
||||
'widgets',
|
||||
widgetId,
|
||||
currentUser,
|
||||
);
|
||||
|
||||
return res.status(200).send(widgetId);
|
||||
} else {
|
||||
return res.status(400).send(widgetId);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
46
backend/src/routes/organizationLogin.js
Normal file
46
backend/src/routes/organizationLogin.js
Normal file
@ -0,0 +1,46 @@
|
||||
const express = require('express');
|
||||
|
||||
const BranchesDBApi = require('../db/api/branches');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/organizations:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Organizations]
|
||||
* summary: Get all organizations
|
||||
* description: Get all organizations
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Organizations list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Organizations"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await BranchesDBApi.findAll(req.query);
|
||||
const simplifiedPayload = payload.rows.map((org) => ({
|
||||
id: org.id,
|
||||
name: org.name,
|
||||
}));
|
||||
res.status(200).send(simplifiedPayload);
|
||||
}),
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
442
backend/src/routes/permissions.js
Normal file
442
backend/src/routes/permissions.js
Normal file
@ -0,0 +1,442 @@
|
||||
const express = require('express');
|
||||
|
||||
const PermissionsService = require('../services/permissions');
|
||||
const PermissionsDBApi = require('../db/api/permissions');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('permissions'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Permissions:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Permissions
|
||||
* description: The Permissions managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await PermissionsService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await PermissionsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await PermissionsService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await PermissionsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await PermissionsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Get all permissions
|
||||
* description: Get all permissions
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Permissions list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await PermissionsDBApi.findAll(req.query, { currentUser });
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'name'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Count all permissions
|
||||
* description: Count all permissions
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Permissions count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await PermissionsDBApi.findAll(req.query, null, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Find all permissions that match search criteria
|
||||
* description: Find all permissions that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Permissions list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const payload = await PermissionsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/permissions/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Permissions]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Permissions"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await PermissionsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
106
backend/src/routes/pexels.js
Normal file
106
backend/src/routes/pexels.js
Normal file
@ -0,0 +1,106 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { pexelsKey, pexelsQuery } = require('../config');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const KEY = pexelsKey;
|
||||
|
||||
router.get('/image', async (req, res) => {
|
||||
const headers = {
|
||||
Authorization: `${KEY}`,
|
||||
};
|
||||
const query = pexelsQuery || 'nature';
|
||||
const orientation = 'portrait';
|
||||
const perPage = 1;
|
||||
const url = `https://api.pexels.com/v1/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
res.status(200).json(data.photos[0]);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch image' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/video', async (req, res) => {
|
||||
const headers = {
|
||||
Authorization: `${KEY}`,
|
||||
};
|
||||
const query = pexelsQuery || 'nature';
|
||||
const orientation = 'portrait';
|
||||
const perPage = 1;
|
||||
const url = `https://api.pexels.com/videos/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
res.status(200).json(data.videos[0]);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch video' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/multiple-images', async (req, res) => {
|
||||
const headers = {
|
||||
Authorization: `${KEY}`,
|
||||
};
|
||||
|
||||
const queries = req.query.queries
|
||||
? req.query.queries.split(',')
|
||||
: ['home', 'apple', 'pizza', 'mountains', 'cat'];
|
||||
const orientation = 'square';
|
||||
const perPage = 1;
|
||||
|
||||
const fallbackImage = {
|
||||
src: 'https://images.pexels.com/photos/8199252/pexels-photo-8199252.jpeg',
|
||||
photographer: 'Yan Krukau',
|
||||
photographer_url: 'https://www.pexels.com/@yankrukov',
|
||||
};
|
||||
const fetchFallbackImage = async () => {
|
||||
try {
|
||||
const response = await fetch('https://picsum.photos/600');
|
||||
return {
|
||||
src: response.url,
|
||||
photographer: 'Random Picsum',
|
||||
photographer_url: 'https://picsum.photos/',
|
||||
};
|
||||
} catch (error) {
|
||||
return fallbackImage;
|
||||
}
|
||||
};
|
||||
const fetchImage = async (query) => {
|
||||
const url = `https://api.pexels.com/v1/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`;
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
return data.photos[0] || null;
|
||||
};
|
||||
|
||||
const imagePromises = queries.map((query) => fetchImage(query));
|
||||
const imagesResults = await Promise.allSettled(imagePromises);
|
||||
|
||||
const formattedImages = await Promise.all(
|
||||
imagesResults.map(async (result) => {
|
||||
if (result.status === 'fulfilled' && result.value) {
|
||||
const image = result.value;
|
||||
return {
|
||||
src: image.src?.original || fallbackImage.src,
|
||||
photographer: image.photographer || fallbackImage.photographer,
|
||||
photographer_url:
|
||||
image.photographer_url || fallbackImage.photographer_url,
|
||||
};
|
||||
} else {
|
||||
const fallback = await fetchFallbackImage();
|
||||
return {
|
||||
src: fallback.src || '',
|
||||
photographer: fallback.photographer || 'Unknown',
|
||||
photographer_url: fallback.photographer_url || '',
|
||||
};
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
res.json(formattedImages);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
455
backend/src/routes/private_sessions.js
Normal file
455
backend/src/routes/private_sessions.js
Normal file
@ -0,0 +1,455 @@
|
||||
const express = require('express');
|
||||
|
||||
const Private_sessionsService = require('../services/private_sessions');
|
||||
const Private_sessionsDBApi = require('../db/api/private_sessions');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('private_sessions'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Private_sessions:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Private_sessions
|
||||
* description: The Private_sessions managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Private_sessionsService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Private_sessionsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Private_sessionsService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Private_sessionsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Private_sessionsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Get all private_sessions
|
||||
* description: Get all private_sessions
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Private_sessions list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Private_sessionsDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ currentUser },
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'session_date'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Count all private_sessions
|
||||
* description: Count all private_sessions
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Private_sessions count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Private_sessionsDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ countOnly: true, currentUser },
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Find all private_sessions that match search criteria
|
||||
* description: Find all private_sessions that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Private_sessions list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await Private_sessionsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/private_sessions/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Private_sessions]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Private_sessions"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await Private_sessionsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
459
backend/src/routes/progress_tracks.js
Normal file
459
backend/src/routes/progress_tracks.js
Normal file
@ -0,0 +1,459 @@
|
||||
const express = require('express');
|
||||
|
||||
const Progress_tracksService = require('../services/progress_tracks');
|
||||
const Progress_tracksDBApi = require('../db/api/progress_tracks');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('progress_tracks'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Progress_tracks:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* description:
|
||||
* type: string
|
||||
* default: description
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Progress_tracks
|
||||
* description: The Progress_tracks managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Progress_tracksService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Progress_tracksService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Progress_tracksService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Progress_tracksService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Progress_tracksService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Get all progress_tracks
|
||||
* description: Get all progress_tracks
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Progress_tracks list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Progress_tracksDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ currentUser },
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'description'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Count all progress_tracks
|
||||
* description: Count all progress_tracks
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Progress_tracks count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Progress_tracksDBApi.findAll(
|
||||
req.query,
|
||||
globalAccess,
|
||||
{ countOnly: true, currentUser },
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Find all progress_tracks that match search criteria
|
||||
* description: Find all progress_tracks that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Progress_tracks list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await Progress_tracksDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/progress_tracks/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Progress_tracks]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Progress_tracks"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await Progress_tracksDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
452
backend/src/routes/purchases.js
Normal file
452
backend/src/routes/purchases.js
Normal file
@ -0,0 +1,452 @@
|
||||
const express = require('express');
|
||||
|
||||
const PurchasesService = require('../services/purchases');
|
||||
const PurchasesDBApi = require('../db/api/purchases');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('purchases'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Purchases:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* amount:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Purchases
|
||||
* description: The Purchases managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await PurchasesService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await PurchasesService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await PurchasesService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await PurchasesService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await PurchasesService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Get all purchases
|
||||
* description: Get all purchases
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Purchases list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await PurchasesDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'amount', 'purchase_date'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Count all purchases
|
||||
* description: Count all purchases
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Purchases count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await PurchasesDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Find all purchases that match search criteria
|
||||
* description: Find all purchases that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Purchases list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await PurchasesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/purchases/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Purchases]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Purchases"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await PurchasesDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
444
backend/src/routes/roles.js
Normal file
444
backend/src/routes/roles.js
Normal file
@ -0,0 +1,444 @@
|
||||
const express = require('express');
|
||||
|
||||
const RolesService = require('../services/roles');
|
||||
const RolesDBApi = require('../db/api/roles');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('roles'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Roles:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Roles
|
||||
* description: The Roles managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await RolesService.create(req.body.data, req.currentUser, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await RolesService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await RolesService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await RolesService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await RolesService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Get all roles
|
||||
* description: Get all roles
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Roles list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await RolesDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'name'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Count all roles
|
||||
* description: Count all roles
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Roles count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await RolesDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Find all roles that match search criteria
|
||||
* description: Find all roles that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Roles list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const payload = await RolesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/roles/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Roles]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Roles"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await RolesDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
447
backend/src/routes/sales.js
Normal file
447
backend/src/routes/sales.js
Normal file
@ -0,0 +1,447 @@
|
||||
const express = require('express');
|
||||
|
||||
const SalesService = require('../services/sales');
|
||||
const SalesDBApi = require('../db/api/sales');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('sales'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Sales:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* total_amount:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Sales
|
||||
* description: The Sales managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await SalesService.create(req.body.data, req.currentUser, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await SalesService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await SalesService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await SalesService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await SalesService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Get all sales
|
||||
* description: Get all sales
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Sales list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await SalesDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'total_amount', 'sale_date'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Count all sales
|
||||
* description: Count all sales
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Sales count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await SalesDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Find all sales that match search criteria
|
||||
* description: Find all sales that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Sales list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await SalesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sales/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Sales]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Sales"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await SalesDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
60
backend/src/routes/search.js
Normal file
60
backend/src/routes/search.js
Normal file
@ -0,0 +1,60 @@
|
||||
const express = require('express');
|
||||
const SearchService = require('../services/search');
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
router.use(checkCrudPermissions('search'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* path:
|
||||
* /api/search:
|
||||
* post:
|
||||
* summary: Search
|
||||
* description: Search results across multiple tables
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* searchQuery:
|
||||
* type: string
|
||||
* required:
|
||||
* - searchQuery
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Successful request
|
||||
* 400:
|
||||
* description: Invalid request
|
||||
* 500:
|
||||
* description: Internal server error
|
||||
*/
|
||||
|
||||
router.post('/', async (req, res) => {
|
||||
const { searchQuery, organizationId } = req.body;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
if (!searchQuery) {
|
||||
return res.status(400).json({ error: 'Please enter a search query' });
|
||||
}
|
||||
|
||||
try {
|
||||
const foundMatches = await SearchService.search(
|
||||
searchQuery,
|
||||
req.currentUser,
|
||||
organizationId,
|
||||
globalAccess,
|
||||
);
|
||||
res.json(foundMatches);
|
||||
} catch (error) {
|
||||
console.error('Internal Server Error', error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
443
backend/src/routes/shifts.js
Normal file
443
backend/src/routes/shifts.js
Normal file
@ -0,0 +1,443 @@
|
||||
const express = require('express');
|
||||
|
||||
const ShiftsService = require('../services/shifts');
|
||||
const ShiftsDBApi = require('../db/api/shifts');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('shifts'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Shifts:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Shifts
|
||||
* description: The Shifts managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await ShiftsService.create(req.body.data, req.currentUser, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await ShiftsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await ShiftsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await ShiftsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await ShiftsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Get all shifts
|
||||
* description: Get all shifts
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Shifts list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await ShiftsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'start_time', 'end_time'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Count all shifts
|
||||
* description: Count all shifts
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Shifts count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await ShiftsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Find all shifts that match search criteria
|
||||
* description: Find all shifts that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Shifts list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await ShiftsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/shifts/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Shifts]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Shifts"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await ShiftsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
453
backend/src/routes/staff.js
Normal file
453
backend/src/routes/staff.js
Normal file
@ -0,0 +1,453 @@
|
||||
const express = require('express');
|
||||
|
||||
const StaffService = require('../services/staff');
|
||||
const StaffDBApi = require('../db/api/staff');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('staff'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Staff:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* first_name:
|
||||
* type: string
|
||||
* default: first_name
|
||||
* last_name:
|
||||
* type: string
|
||||
* default: last_name
|
||||
* email:
|
||||
* type: string
|
||||
* default: email
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Staff
|
||||
* description: The Staff managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await StaffService.create(req.body.data, req.currentUser, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await StaffService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await StaffService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await StaffService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await StaffService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Get all staff
|
||||
* description: Get all staff
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Staff list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await StaffDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'first_name', 'last_name', 'email'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Count all staff
|
||||
* description: Count all staff
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Staff count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await StaffDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Find all staff that match search criteria
|
||||
* description: Find all staff that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Staff list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await StaffDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/staff/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Staff]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Staff"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await StaffDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
458
backend/src/routes/users.js
Normal file
458
backend/src/routes/users.js
Normal file
@ -0,0 +1,458 @@
|
||||
const express = require('express');
|
||||
|
||||
const UsersService = require('../services/users');
|
||||
const UsersDBApi = require('../db/api/users');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('users'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Users:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* firstName:
|
||||
* type: string
|
||||
* default: firstName
|
||||
* lastName:
|
||||
* type: string
|
||||
* default: lastName
|
||||
* phoneNumber:
|
||||
* type: string
|
||||
* default: phoneNumber
|
||||
* email:
|
||||
* type: string
|
||||
* default: email
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Users
|
||||
* description: The Users managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await UsersService.create(req.body.data, req.currentUser, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await UsersService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await UsersService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await UsersService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await UsersService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Get all users
|
||||
* description: Get all users
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Users list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await UsersDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'firstName', 'lastName', 'phoneNumber', 'email'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Count all users
|
||||
* description: Count all users
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Users count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await UsersDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Find all users that match search criteria
|
||||
* description: Find all users that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Users list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await UsersDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Users]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Users"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await UsersDBApi.findBy({ id: req.params.id });
|
||||
|
||||
delete payload.password;
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
456
backend/src/routes/workout_logs.js
Normal file
456
backend/src/routes/workout_logs.js
Normal file
@ -0,0 +1,456 @@
|
||||
const express = require('express');
|
||||
|
||||
const Workout_logsService = require('../services/workout_logs');
|
||||
const Workout_logsDBApi = require('../db/api/workout_logs');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('workout_logs'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Workout_logs:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* workout_details:
|
||||
* type: string
|
||||
* default: workout_details
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Workout_logs
|
||||
* description: The Workout_logs managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Workout_logsService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
link.host,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/budgets/bulk-import:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* data:
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
* description: Invalid input data
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*
|
||||
*/
|
||||
router.post(
|
||||
'/bulk-import',
|
||||
wrapAsync(async (req, res) => {
|
||||
const referer =
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Workout_logsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to update
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* requestBody:
|
||||
* description: Set new item data
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Workout_logsService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: Item ID to delete
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Workout_logsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Workout_logsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Get all workout_logs
|
||||
* description: Get all workout_logs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Workout_logs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Workout_logsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'workout_details', 'date'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
res.status(200).attachment(csv);
|
||||
res.send(csv);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Count all workout_logs
|
||||
* description: Count all workout_logs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Workout_logs count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Workout_logsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Find all workout_logs that match search criteria
|
||||
* description: Find all workout_logs that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Workout_logs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const organizationId = req.currentUser.organization?.id;
|
||||
|
||||
const payload = await Workout_logsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workout_logs/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Workout_logs]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of item to get
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Workout_logs"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await Workout_logsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
117
backend/src/services/appointments.js
Normal file
117
backend/src/services/appointments.js
Normal file
@ -0,0 +1,117 @@
|
||||
const db = require('../db/models');
|
||||
const AppointmentsDBApi = require('../db/api/appointments');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class AppointmentsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await AppointmentsDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await AppointmentsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let appointments = await AppointmentsDBApi.findBy(
|
||||
{ id },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!appointments) {
|
||||
throw new ValidationError('appointmentsNotFound');
|
||||
}
|
||||
|
||||
const updatedAppointments = await AppointmentsDBApi.update(id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
return updatedAppointments;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await AppointmentsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await AppointmentsDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
228
backend/src/services/auth.js
Normal file
228
backend/src/services/auth.js
Normal file
@ -0,0 +1,228 @@
|
||||
const UsersDBApi = require('../db/api/users');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const ForbiddenError = require('./notifications/errors/forbidden');
|
||||
const bcrypt = require('bcrypt');
|
||||
const EmailAddressVerificationEmail = require('./email/list/addressVerification');
|
||||
const InvitationEmail = require('./email/list/invitation');
|
||||
const PasswordResetEmail = require('./email/list/passwordReset');
|
||||
const EmailSender = require('./email');
|
||||
const config = require('../config');
|
||||
const helpers = require('../helpers');
|
||||
|
||||
class Auth {
|
||||
static async signup(email, password, organizationId, options = {}, host) {
|
||||
const user = await UsersDBApi.findBy({ email });
|
||||
|
||||
const hashedPassword = await bcrypt.hash(
|
||||
password,
|
||||
config.bcrypt.saltRounds,
|
||||
);
|
||||
|
||||
if (user) {
|
||||
if (user.authenticationUid) {
|
||||
throw new ValidationError('auth.emailAlreadyInUse');
|
||||
}
|
||||
|
||||
if (user.disabled) {
|
||||
throw new ValidationError('auth.userDisabled');
|
||||
}
|
||||
|
||||
await UsersDBApi.updatePassword(user.id, hashedPassword, options);
|
||||
|
||||
if (EmailSender.isConfigured) {
|
||||
await this.sendEmailAddressVerificationEmail(user.email, host);
|
||||
}
|
||||
|
||||
const data = {
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
},
|
||||
};
|
||||
|
||||
return helpers.jwtSign(data);
|
||||
}
|
||||
|
||||
const newUser = await UsersDBApi.createFromAuth(
|
||||
{
|
||||
firstName: email.split('@')[0],
|
||||
password: hashedPassword,
|
||||
email: email,
|
||||
|
||||
organizationId: organizationId,
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
if (EmailSender.isConfigured) {
|
||||
await this.sendEmailAddressVerificationEmail(newUser.email, host);
|
||||
}
|
||||
|
||||
const data = {
|
||||
user: {
|
||||
id: newUser.id,
|
||||
email: newUser.email,
|
||||
},
|
||||
};
|
||||
|
||||
return helpers.jwtSign(data);
|
||||
}
|
||||
|
||||
static async signin(email, password, options = {}) {
|
||||
const user = await UsersDBApi.findBy({ email });
|
||||
|
||||
if (!user) {
|
||||
throw new ValidationError('auth.userNotFound');
|
||||
}
|
||||
|
||||
if (user.disabled) {
|
||||
throw new ValidationError('auth.userDisabled');
|
||||
}
|
||||
|
||||
if (!user.password) {
|
||||
throw new ValidationError('auth.wrongPassword');
|
||||
}
|
||||
|
||||
if (!EmailSender.isConfigured) {
|
||||
user.emailVerified = true;
|
||||
}
|
||||
|
||||
if (!user.emailVerified) {
|
||||
throw new ValidationError('auth.userNotVerified');
|
||||
}
|
||||
|
||||
const passwordsMatch = await bcrypt.compare(password, user.password);
|
||||
|
||||
if (!passwordsMatch) {
|
||||
throw new ValidationError('auth.wrongPassword');
|
||||
}
|
||||
|
||||
const data = {
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
},
|
||||
};
|
||||
|
||||
return helpers.jwtSign(data);
|
||||
}
|
||||
|
||||
static async sendEmailAddressVerificationEmail(email, host) {
|
||||
let link;
|
||||
try {
|
||||
const token = await UsersDBApi.generateEmailVerificationToken(email);
|
||||
link = `${host}/verify-email?token=${token}`;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new ValidationError('auth.emailAddressVerificationEmail.error');
|
||||
}
|
||||
|
||||
const emailAddressVerificationEmail = new EmailAddressVerificationEmail(
|
||||
email,
|
||||
link,
|
||||
);
|
||||
|
||||
return new EmailSender(emailAddressVerificationEmail).send();
|
||||
}
|
||||
|
||||
static async sendPasswordResetEmail(email, type = 'register', host) {
|
||||
let link;
|
||||
|
||||
try {
|
||||
const token = await UsersDBApi.generatePasswordResetToken(email);
|
||||
link = `${host}/password-reset?token=${token}`;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new ValidationError('auth.passwordReset.error');
|
||||
}
|
||||
|
||||
let passwordResetEmail;
|
||||
if (type === 'register') {
|
||||
passwordResetEmail = new PasswordResetEmail(email, link);
|
||||
}
|
||||
if (type === 'invitation') {
|
||||
passwordResetEmail = new InvitationEmail(email, link);
|
||||
}
|
||||
|
||||
return new EmailSender(passwordResetEmail).send();
|
||||
}
|
||||
|
||||
static async verifyEmail(token, options = {}) {
|
||||
const user = await UsersDBApi.findByEmailVerificationToken(token, options);
|
||||
|
||||
if (!user) {
|
||||
throw new ValidationError(
|
||||
'auth.emailAddressVerificationEmail.invalidToken',
|
||||
);
|
||||
}
|
||||
|
||||
return UsersDBApi.markEmailVerified(user.id, options);
|
||||
}
|
||||
|
||||
static async passwordUpdate(currentPassword, newPassword, options) {
|
||||
const currentUser = options.currentUser || null;
|
||||
if (!currentUser) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
|
||||
const currentPasswordMatch = await bcrypt.compare(
|
||||
currentPassword,
|
||||
currentUser.password,
|
||||
);
|
||||
|
||||
if (!currentPasswordMatch) {
|
||||
throw new ValidationError('auth.wrongPassword');
|
||||
}
|
||||
|
||||
const newPasswordMatch = await bcrypt.compare(
|
||||
newPassword,
|
||||
currentUser.password,
|
||||
);
|
||||
|
||||
if (newPasswordMatch) {
|
||||
throw new ValidationError('auth.passwordUpdate.samePassword');
|
||||
}
|
||||
|
||||
const hashedPassword = await bcrypt.hash(
|
||||
newPassword,
|
||||
config.bcrypt.saltRounds,
|
||||
);
|
||||
|
||||
return UsersDBApi.updatePassword(currentUser.id, hashedPassword, options);
|
||||
}
|
||||
|
||||
static async passwordReset(token, password, options = {}) {
|
||||
const user = await UsersDBApi.findByPasswordResetToken(token, options);
|
||||
|
||||
if (!user) {
|
||||
throw new ValidationError('auth.passwordReset.invalidToken');
|
||||
}
|
||||
|
||||
const hashedPassword = await bcrypt.hash(
|
||||
password,
|
||||
config.bcrypt.saltRounds,
|
||||
);
|
||||
|
||||
return UsersDBApi.updatePassword(user.id, hashedPassword, options);
|
||||
}
|
||||
|
||||
static async updateProfile(data, currentUser) {
|
||||
let transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await UsersDBApi.findBy({ id: currentUser.id }, { transaction });
|
||||
|
||||
await UsersDBApi.update(currentUser.id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Auth;
|
||||
114
backend/src/services/branches.js
Normal file
114
backend/src/services/branches.js
Normal file
@ -0,0 +1,114 @@
|
||||
const db = require('../db/models');
|
||||
const BranchesDBApi = require('../db/api/branches');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class BranchesService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await BranchesDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await BranchesDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let branches = await BranchesDBApi.findBy({ id }, { transaction });
|
||||
|
||||
if (!branches) {
|
||||
throw new ValidationError('branchesNotFound');
|
||||
}
|
||||
|
||||
const updatedBranches = await BranchesDBApi.update(id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
return updatedBranches;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await BranchesDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await BranchesDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.email-header {
|
||||
background-color: #3498db;
|
||||
color: #fff;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.email-body {
|
||||
padding: 16px;
|
||||
}
|
||||
.email-footer {
|
||||
padding: 16px;
|
||||
background-color: #f7fafc;
|
||||
text-align: center;
|
||||
color: #4a5568;
|
||||
font-size: 14px;
|
||||
}
|
||||
.link-primary {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header">Verify your email for {appTitle}!</div>
|
||||
<div class="email-body">
|
||||
<p>Hello,</p>
|
||||
<p>Follow this link to verify your email address.</p>
|
||||
<p>
|
||||
If you didn't ask to verify this address, you can ignore this email.
|
||||
</p>
|
||||
<p><a href="{signupUrl}" class="link-primary">{signupUrl}</a></p>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
Thanks,<br />
|
||||
The {appTitle} Team
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.email-header {
|
||||
background-color: #3498db;
|
||||
color: #fff;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.email-body {
|
||||
padding: 16px;
|
||||
}
|
||||
.email-footer {
|
||||
padding: 16px;
|
||||
background-color: #f7fafc;
|
||||
text-align: center;
|
||||
color: #4a5568;
|
||||
font-size: 14px;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: #3498db;
|
||||
color: #fff !important;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header">Welcome to {appTitle}!</div>
|
||||
<div class="email-body">
|
||||
<p>Hello,</p>
|
||||
<p>
|
||||
You've been invited to join {appTitle}. Please click the button below
|
||||
to set up your account.
|
||||
</p>
|
||||
<a href="{signupUrl}" class="btn-primary">Set up account</a>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
Thanks,<br />
|
||||
The {appTitle} Team
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.email-header {
|
||||
background-color: #3498db;
|
||||
color: #fff;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.email-body {
|
||||
padding: 16px;
|
||||
}
|
||||
.email-footer {
|
||||
padding: 16px;
|
||||
background-color: #f7fafc;
|
||||
text-align: center;
|
||||
color: #4a5568;
|
||||
font-size: 14px;
|
||||
}
|
||||
.link-primary {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header">Reset your password for {appTitle}</div>
|
||||
<div class="email-body">
|
||||
<p>Hello,</p>
|
||||
<p>
|
||||
Follow this link to reset your {appTitle} password for your
|
||||
{accountName} account.
|
||||
</p>
|
||||
<p><a href="{resetUrl}" class="link-primary">{resetUrl}</a></p>
|
||||
<p>
|
||||
If you didn't ask to reset your password, you can ignore this email.
|
||||
</p>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
Thanks,<br />
|
||||
The {appTitle} Team
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
41
backend/src/services/email/index.js
Normal file
41
backend/src/services/email/index.js
Normal file
@ -0,0 +1,41 @@
|
||||
const config = require('../../config');
|
||||
const assert = require('assert');
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
module.exports = class EmailSender {
|
||||
constructor(email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
async send() {
|
||||
assert(this.email, 'email is required');
|
||||
assert(this.email.to, 'email.to is required');
|
||||
assert(this.email.subject, 'email.subject is required');
|
||||
assert(this.email.html, 'email.html is required');
|
||||
|
||||
const htmlContent = await this.email.html();
|
||||
|
||||
const transporter = nodemailer.createTransport(this.transportConfig);
|
||||
|
||||
const mailOptions = {
|
||||
from: this.from,
|
||||
to: this.email.to,
|
||||
subject: this.email.subject,
|
||||
html: htmlContent,
|
||||
};
|
||||
|
||||
return transporter.sendMail(mailOptions);
|
||||
}
|
||||
|
||||
static get isConfigured() {
|
||||
return !!config.email?.auth?.pass && !!config.email?.auth?.user;
|
||||
}
|
||||
|
||||
get transportConfig() {
|
||||
return config.email;
|
||||
}
|
||||
|
||||
get from() {
|
||||
return config.email.from;
|
||||
}
|
||||
};
|
||||
41
backend/src/services/email/list/addressVerification.js
Normal file
41
backend/src/services/email/list/addressVerification.js
Normal file
@ -0,0 +1,41 @@
|
||||
const { getNotification } = require('../../notifications/helpers');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
|
||||
module.exports = class EmailAddressVerificationEmail {
|
||||
constructor(to, link) {
|
||||
this.to = to;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
get subject() {
|
||||
return getNotification(
|
||||
'emails.emailAddressVerification.subject',
|
||||
getNotification('app.title'),
|
||||
);
|
||||
}
|
||||
|
||||
async html() {
|
||||
try {
|
||||
const templatePath = path.join(
|
||||
__dirname,
|
||||
'../../email/htmlTemplates/addressVerification/emailAddressVerification.html',
|
||||
);
|
||||
|
||||
const template = await fs.readFile(templatePath, 'utf8');
|
||||
|
||||
const appTitle = getNotification('app.title');
|
||||
const signupUrl = this.link;
|
||||
|
||||
let html = template
|
||||
.replace(/{appTitle}/g, appTitle)
|
||||
.replace(/{signupUrl}/g, signupUrl)
|
||||
.replace(/{to}/g, this.to);
|
||||
|
||||
return html;
|
||||
} catch (error) {
|
||||
console.error('Error generating invitation email HTML:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
41
backend/src/services/email/list/invitation.js
Normal file
41
backend/src/services/email/list/invitation.js
Normal file
@ -0,0 +1,41 @@
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const { getNotification } = require('../../notifications/helpers');
|
||||
|
||||
module.exports = class InvitationEmail {
|
||||
constructor(to, host) {
|
||||
this.to = to;
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
get subject() {
|
||||
return getNotification(
|
||||
'emails.invitation.subject',
|
||||
getNotification('app.title'),
|
||||
);
|
||||
}
|
||||
|
||||
async html() {
|
||||
try {
|
||||
const templatePath = path.join(
|
||||
__dirname,
|
||||
'../../email/htmlTemplates/invitation/invitationTemplate.html',
|
||||
);
|
||||
|
||||
const template = await fs.readFile(templatePath, 'utf8');
|
||||
|
||||
const appTitle = getNotification('app.title');
|
||||
const signupUrl = `${this.host}&invitation=true`;
|
||||
|
||||
let html = template
|
||||
.replace(/{appTitle}/g, appTitle)
|
||||
.replace(/{signupUrl}/g, signupUrl)
|
||||
.replace(/{to}/g, this.to);
|
||||
|
||||
return html;
|
||||
} catch (error) {
|
||||
console.error('Error generating invitation email HTML:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
42
backend/src/services/email/list/passwordReset.js
Normal file
42
backend/src/services/email/list/passwordReset.js
Normal file
@ -0,0 +1,42 @@
|
||||
const { getNotification } = require('../../notifications/helpers');
|
||||
const path = require('path');
|
||||
const { promises: fs } = require('fs');
|
||||
|
||||
module.exports = class PasswordResetEmail {
|
||||
constructor(to, link) {
|
||||
this.to = to;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
get subject() {
|
||||
return getNotification(
|
||||
'emails.passwordReset.subject',
|
||||
getNotification('app.title'),
|
||||
);
|
||||
}
|
||||
|
||||
async html() {
|
||||
try {
|
||||
const templatePath = path.join(
|
||||
__dirname,
|
||||
'../../email/htmlTemplates/passwordReset/passwordResetEmail.html',
|
||||
);
|
||||
|
||||
const template = await fs.readFile(templatePath, 'utf8');
|
||||
|
||||
const appTitle = getNotification('app.title');
|
||||
const resetUrl = this.link;
|
||||
const accountName = this.to;
|
||||
|
||||
let html = template
|
||||
.replace(/{appTitle}/g, appTitle)
|
||||
.replace(/{resetUrl}/g, resetUrl)
|
||||
.replace(/{accountName}/g, accountName);
|
||||
|
||||
return html;
|
||||
} catch (error) {
|
||||
console.error('Error generating invitation email HTML:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
118
backend/src/services/equipment_maintenance.js
Normal file
118
backend/src/services/equipment_maintenance.js
Normal file
@ -0,0 +1,118 @@
|
||||
const db = require('../db/models');
|
||||
const Equipment_maintenanceDBApi = require('../db/api/equipment_maintenance');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class Equipment_maintenanceService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await Equipment_maintenanceDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await Equipment_maintenanceDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let equipment_maintenance = await Equipment_maintenanceDBApi.findBy(
|
||||
{ id },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!equipment_maintenance) {
|
||||
throw new ValidationError('equipment_maintenanceNotFound');
|
||||
}
|
||||
|
||||
const updatedEquipment_maintenance =
|
||||
await Equipment_maintenanceDBApi.update(id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
return updatedEquipment_maintenance;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Equipment_maintenanceDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Equipment_maintenanceDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
202
backend/src/services/file.js
Normal file
202
backend/src/services/file.js
Normal file
@ -0,0 +1,202 @@
|
||||
const formidable = require('formidable');
|
||||
const fs = require('fs');
|
||||
const config = require('../config');
|
||||
const path = require('path');
|
||||
const { format } = require('util');
|
||||
|
||||
const ensureDirectoryExistence = (filePath) => {
|
||||
const dirname = path.dirname(filePath);
|
||||
|
||||
if (fs.existsSync(dirname)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ensureDirectoryExistence(dirname);
|
||||
fs.mkdirSync(dirname);
|
||||
};
|
||||
|
||||
const uploadLocal = (
|
||||
folder,
|
||||
validations = {
|
||||
entity: null,
|
||||
maxFileSize: null,
|
||||
folderIncludesAuthenticationUid: false,
|
||||
},
|
||||
) => {
|
||||
return (req, res) => {
|
||||
if (!req.currentUser) {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
if (validations.entity) {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
if (validations.folderIncludesAuthenticationUid) {
|
||||
folder = folder.replace(':userId', req.currentUser.authenticationUid);
|
||||
if (
|
||||
!req.currentUser.authenticationUid ||
|
||||
!folder.includes(req.currentUser.authenticationUid)
|
||||
) {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const form = new formidable.IncomingForm();
|
||||
form.uploadDir = config.uploadDir;
|
||||
|
||||
if (validations && validations.maxFileSize) {
|
||||
form.maxFileSize = validations.maxFileSize;
|
||||
}
|
||||
|
||||
form.parse(req, function (err, fields, files) {
|
||||
const filename = String(fields.filename);
|
||||
const fileTempUrl = files.file.path;
|
||||
|
||||
if (!filename) {
|
||||
fs.unlinkSync(fileTempUrl);
|
||||
res.sendStatus(500);
|
||||
return;
|
||||
}
|
||||
|
||||
const privateUrl = path.join(form.uploadDir, folder, filename);
|
||||
ensureDirectoryExistence(privateUrl);
|
||||
fs.renameSync(fileTempUrl, privateUrl);
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
form.on('error', function (err) {
|
||||
res.status(500).send(err);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const downloadLocal = async (req, res) => {
|
||||
const privateUrl = req.query.privateUrl;
|
||||
if (!privateUrl) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
res.download(path.join(config.uploadDir, privateUrl));
|
||||
};
|
||||
|
||||
const initGCloud = () => {
|
||||
const processFile = require('../middlewares/upload');
|
||||
const { Storage } = require('@google-cloud/storage');
|
||||
|
||||
const crypto = require('crypto');
|
||||
const hash = config.gcloud.hash;
|
||||
|
||||
const privateKey = process.env.GC_PRIVATE_KEY.replace(/\\\n/g, '\n');
|
||||
|
||||
const storage = new Storage({
|
||||
projectId: process.env.GC_PROJECT_ID,
|
||||
credentials: {
|
||||
client_email: process.env.GC_CLIENT_EMAIL,
|
||||
private_key: privateKey,
|
||||
},
|
||||
});
|
||||
|
||||
const bucket = storage.bucket(config.gcloud.bucket);
|
||||
return { hash, bucket, processFile };
|
||||
};
|
||||
|
||||
const uploadGCloud = async (folder, req, res) => {
|
||||
try {
|
||||
const { hash, bucket, processFile } = initGCloud();
|
||||
await processFile(req, res);
|
||||
let buffer = await req.file.buffer;
|
||||
let filename = await req.body.filename;
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).send({ message: 'Please upload a file!' });
|
||||
}
|
||||
|
||||
let path = `${hash}/${folder}/${filename}`;
|
||||
let blob = bucket.file(path);
|
||||
|
||||
console.log(path);
|
||||
|
||||
const blobStream = blob.createWriteStream({
|
||||
resumable: false,
|
||||
});
|
||||
|
||||
blobStream.on('error', (err) => {
|
||||
console.log('Upload error');
|
||||
console.log(err.message);
|
||||
res.status(500).send({ message: err.message });
|
||||
});
|
||||
|
||||
console.log(`https://storage.googleapis.com/${bucket.name}/${blob.name}`);
|
||||
|
||||
blobStream.on('finish', async (data) => {
|
||||
const publicUrl = format(
|
||||
`https://storage.googleapis.com/${bucket.name}/${blob.name}`,
|
||||
);
|
||||
|
||||
res.status(200).send({
|
||||
message: 'Uploaded the file successfully: ' + path,
|
||||
url: publicUrl,
|
||||
});
|
||||
});
|
||||
|
||||
blobStream.end(buffer);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
|
||||
res.status(500).send({
|
||||
message: `Could not upload the file. ${err}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const downloadGCloud = async (req, res) => {
|
||||
try {
|
||||
const { hash, bucket, processFile } = initGCloud();
|
||||
|
||||
const privateUrl = await req.query.privateUrl;
|
||||
const filePath = `${hash}/${privateUrl}`;
|
||||
const file = bucket.file(filePath);
|
||||
const fileExists = await file.exists();
|
||||
|
||||
if (fileExists[0]) {
|
||||
const stream = file.createReadStream();
|
||||
stream.pipe(res);
|
||||
} else {
|
||||
res.status(404).send({
|
||||
message: 'Could not download the file. ' + err,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(404).send({
|
||||
message: 'Could not download the file. ' + err,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const deleteGCloud = async (privateUrl) => {
|
||||
try {
|
||||
const { hash, bucket, processFile } = initGCloud();
|
||||
const filePath = `${hash}/${privateUrl}`;
|
||||
|
||||
const file = bucket.file(filePath);
|
||||
const fileExists = await file.exists();
|
||||
|
||||
if (fileExists[0]) {
|
||||
file.delete();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`Cannot find the file ${privateUrl}`);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
initGCloud,
|
||||
uploadLocal,
|
||||
downloadLocal,
|
||||
deleteGCloud,
|
||||
uploadGCloud,
|
||||
downloadGCloud,
|
||||
};
|
||||
121
backend/src/services/locker_assignments.js
Normal file
121
backend/src/services/locker_assignments.js
Normal file
@ -0,0 +1,121 @@
|
||||
const db = require('../db/models');
|
||||
const Locker_assignmentsDBApi = require('../db/api/locker_assignments');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class Locker_assignmentsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await Locker_assignmentsDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await Locker_assignmentsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let locker_assignments = await Locker_assignmentsDBApi.findBy(
|
||||
{ id },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!locker_assignments) {
|
||||
throw new ValidationError('locker_assignmentsNotFound');
|
||||
}
|
||||
|
||||
const updatedLocker_assignments = await Locker_assignmentsDBApi.update(
|
||||
id,
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
return updatedLocker_assignments;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Locker_assignmentsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Locker_assignmentsDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
121
backend/src/services/medical_reports.js
Normal file
121
backend/src/services/medical_reports.js
Normal file
@ -0,0 +1,121 @@
|
||||
const db = require('../db/models');
|
||||
const Medical_reportsDBApi = require('../db/api/medical_reports');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class Medical_reportsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await Medical_reportsDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await Medical_reportsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let medical_reports = await Medical_reportsDBApi.findBy(
|
||||
{ id },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!medical_reports) {
|
||||
throw new ValidationError('medical_reportsNotFound');
|
||||
}
|
||||
|
||||
const updatedMedical_reports = await Medical_reportsDBApi.update(
|
||||
id,
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
return updatedMedical_reports;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Medical_reportsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Medical_reportsDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
114
backend/src/services/members.js
Normal file
114
backend/src/services/members.js
Normal file
@ -0,0 +1,114 @@
|
||||
const db = require('../db/models');
|
||||
const MembersDBApi = require('../db/api/members');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class MembersService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await MembersDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await MembersDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let members = await MembersDBApi.findBy({ id }, { transaction });
|
||||
|
||||
if (!members) {
|
||||
throw new ValidationError('membersNotFound');
|
||||
}
|
||||
|
||||
const updatedMembers = await MembersDBApi.update(id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
return updatedMembers;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await MembersDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await MembersDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
16
backend/src/services/notifications/errors/forbidden.js
Normal file
16
backend/src/services/notifications/errors/forbidden.js
Normal file
@ -0,0 +1,16 @@
|
||||
const { getNotification, isNotification } = require('../helpers');
|
||||
|
||||
module.exports = class ForbiddenError extends Error {
|
||||
constructor(messageCode) {
|
||||
let message;
|
||||
|
||||
if (messageCode && isNotification(messageCode)) {
|
||||
message = getNotification(messageCode);
|
||||
}
|
||||
|
||||
message = message || getNotification('errors.forbidden.message');
|
||||
|
||||
super(message);
|
||||
this.code = 403;
|
||||
}
|
||||
};
|
||||
16
backend/src/services/notifications/errors/validation.js
Normal file
16
backend/src/services/notifications/errors/validation.js
Normal file
@ -0,0 +1,16 @@
|
||||
const { getNotification, isNotification } = require('../helpers');
|
||||
|
||||
module.exports = class ValidationError extends Error {
|
||||
constructor(messageCode) {
|
||||
let message;
|
||||
|
||||
if (messageCode && isNotification(messageCode)) {
|
||||
message = getNotification(messageCode);
|
||||
}
|
||||
|
||||
message = message || getNotification('errors.validation.message');
|
||||
|
||||
super(message);
|
||||
this.code = 400;
|
||||
}
|
||||
};
|
||||
30
backend/src/services/notifications/helpers.js
Normal file
30
backend/src/services/notifications/helpers.js
Normal file
@ -0,0 +1,30 @@
|
||||
const _get = require('lodash/get');
|
||||
const errors = require('./list');
|
||||
|
||||
function format(message, args) {
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return message.replace(/{(\d+)}/g, function (match, number) {
|
||||
return typeof args[number] != 'undefined' ? args[number] : match;
|
||||
});
|
||||
}
|
||||
|
||||
const isNotification = (key) => {
|
||||
const message = _get(errors, key);
|
||||
return !!message;
|
||||
};
|
||||
|
||||
const getNotification = (key, ...args) => {
|
||||
const message = _get(errors, key);
|
||||
|
||||
if (!message) {
|
||||
return key;
|
||||
}
|
||||
|
||||
return format(message, args);
|
||||
};
|
||||
|
||||
exports.getNotification = getNotification;
|
||||
exports.isNotification = isNotification;
|
||||
100
backend/src/services/notifications/list.js
Normal file
100
backend/src/services/notifications/list.js
Normal file
@ -0,0 +1,100 @@
|
||||
const errors = {
|
||||
app: {
|
||||
title: 'gas',
|
||||
},
|
||||
|
||||
auth: {
|
||||
userDisabled: 'Your account is disabled',
|
||||
forbidden: 'Forbidden',
|
||||
unauthorized: 'Unauthorized',
|
||||
userNotFound: `Sorry, we don't recognize your credentials`,
|
||||
wrongPassword: `Sorry, we don't recognize your credentials`,
|
||||
weakPassword: 'This password is too weak',
|
||||
emailAlreadyInUse: 'Email is already in use',
|
||||
invalidEmail: 'Please provide a valid email',
|
||||
passwordReset: {
|
||||
invalidToken: 'Password reset link is invalid or has expired',
|
||||
error: `Email not recognized`,
|
||||
},
|
||||
passwordUpdate: {
|
||||
samePassword: `You can't use the same password. Please create new password`,
|
||||
},
|
||||
userNotVerified: `Sorry, your email has not been verified yet`,
|
||||
emailAddressVerificationEmail: {
|
||||
invalidToken: 'Email verification link is invalid or has expired',
|
||||
error: `Email not recognized`,
|
||||
},
|
||||
},
|
||||
|
||||
iam: {
|
||||
errors: {
|
||||
userAlreadyExists: 'User with this email already exists',
|
||||
userNotFound: 'User not found',
|
||||
disablingHimself: `You can't disable yourself`,
|
||||
revokingOwnPermission: `You can't revoke your own owner permission`,
|
||||
deletingHimself: `You can't delete yourself`,
|
||||
emailRequired: 'Email is required',
|
||||
},
|
||||
},
|
||||
|
||||
importer: {
|
||||
errors: {
|
||||
invalidFileEmpty: 'The file is empty',
|
||||
invalidFileExcel: 'Only excel (.xlsx) files are allowed',
|
||||
invalidFileUpload:
|
||||
'Invalid file. Make sure you are using the last version of the template.',
|
||||
importHashRequired: 'Import hash is required',
|
||||
importHashExistent: 'Data has already been imported',
|
||||
userEmailMissing: 'Some items in the CSV do not have an email',
|
||||
},
|
||||
},
|
||||
|
||||
errors: {
|
||||
forbidden: {
|
||||
message: 'Forbidden',
|
||||
},
|
||||
validation: {
|
||||
message: 'An error occurred',
|
||||
},
|
||||
searchQueryRequired: {
|
||||
message: 'Search query is required',
|
||||
},
|
||||
},
|
||||
|
||||
emails: {
|
||||
invitation: {
|
||||
subject: `You've been invited to {0}`,
|
||||
body: `
|
||||
<p>Hello,</p>
|
||||
<p>You've been invited to {0} set password for your {1} account.</p>
|
||||
<p><a href='{2}'>{2}</a></p>
|
||||
<p>Thanks,</p>
|
||||
<p>Your {0} team</p>
|
||||
`,
|
||||
},
|
||||
emailAddressVerification: {
|
||||
subject: `Verify your email for {0}`,
|
||||
body: `
|
||||
<p>Hello,</p>
|
||||
<p>Follow this link to verify your email address.</p>
|
||||
<p><a href='{0}'>{0}</a></p>
|
||||
<p>If you didn't ask to verify this address, you can ignore this email.</p>
|
||||
<p>Thanks,</p>
|
||||
<p>Your {1} team</p>
|
||||
`,
|
||||
},
|
||||
passwordReset: {
|
||||
subject: `Reset your password for {0}`,
|
||||
body: `
|
||||
<p>Hello,</p>
|
||||
<p>Follow this link to reset your {0} password for your {1} account.</p>
|
||||
<p><a href='{2}'>{2}</a></p>
|
||||
<p>If you didn't ask to reset your password, you can ignore this email.</p>
|
||||
<p>Thanks,</p>
|
||||
<p>Your {0} team</p>
|
||||
`,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = errors;
|
||||
22
backend/src/services/openai.js
Normal file
22
backend/src/services/openai.js
Normal file
@ -0,0 +1,22 @@
|
||||
const axios = require('axios');
|
||||
const { v4: uuid } = require('uuid');
|
||||
const RoleService = require('./roles');
|
||||
const config = require('../config');
|
||||
|
||||
module.exports = class OpenAiService {
|
||||
static async getWidget(payload, userId, roleId) {
|
||||
const response = await axios.post(
|
||||
`${config.flHost}/${config.project_uuid}/project_customization_widgets.json`,
|
||||
payload,
|
||||
);
|
||||
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
const { widget_id } = await response.data;
|
||||
await RoleService.addRoleInfo(roleId, userId, 'widgets', widget_id);
|
||||
return widget_id;
|
||||
} else {
|
||||
console.error('=======error=======', response.data);
|
||||
return { value: null, error: response.data };
|
||||
}
|
||||
}
|
||||
};
|
||||
114
backend/src/services/permissions.js
Normal file
114
backend/src/services/permissions.js
Normal file
@ -0,0 +1,114 @@
|
||||
const db = require('../db/models');
|
||||
const PermissionsDBApi = require('../db/api/permissions');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class PermissionsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await PermissionsDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await PermissionsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let permissions = await PermissionsDBApi.findBy({ id }, { transaction });
|
||||
|
||||
if (!permissions) {
|
||||
throw new ValidationError('permissionsNotFound');
|
||||
}
|
||||
|
||||
const updatedPermissions = await PermissionsDBApi.update(id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
return updatedPermissions;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await PermissionsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await PermissionsDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
121
backend/src/services/private_sessions.js
Normal file
121
backend/src/services/private_sessions.js
Normal file
@ -0,0 +1,121 @@
|
||||
const db = require('../db/models');
|
||||
const Private_sessionsDBApi = require('../db/api/private_sessions');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class Private_sessionsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await Private_sessionsDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await Private_sessionsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let private_sessions = await Private_sessionsDBApi.findBy(
|
||||
{ id },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!private_sessions) {
|
||||
throw new ValidationError('private_sessionsNotFound');
|
||||
}
|
||||
|
||||
const updatedPrivate_sessions = await Private_sessionsDBApi.update(
|
||||
id,
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
return updatedPrivate_sessions;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Private_sessionsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Private_sessionsDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
121
backend/src/services/progress_tracks.js
Normal file
121
backend/src/services/progress_tracks.js
Normal file
@ -0,0 +1,121 @@
|
||||
const db = require('../db/models');
|
||||
const Progress_tracksDBApi = require('../db/api/progress_tracks');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class Progress_tracksService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await Progress_tracksDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await Progress_tracksDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
currentUser: req.currentUser,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let progress_tracks = await Progress_tracksDBApi.findBy(
|
||||
{ id },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!progress_tracks) {
|
||||
throw new ValidationError('progress_tracksNotFound');
|
||||
}
|
||||
|
||||
const updatedProgress_tracks = await Progress_tracksDBApi.update(
|
||||
id,
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
return updatedProgress_tracks;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Progress_tracksDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async remove(id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Progress_tracksDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user