Initial version
This commit is contained in:
commit
1eaf6057f6
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
*/node_modules/
|
||||
*/build/
|
||||
187
502.html
Normal file
187
502.html
Normal file
@ -0,0 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Service Starting</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background-color: #EFF2FF;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
padding: 30px 40px;
|
||||
background-color: #fff;
|
||||
border-radius: 20px;
|
||||
margin-bottom: 20px;
|
||||
max-width: 538px;
|
||||
width: 100%;
|
||||
box-shadow: 0 13px 34px 0 rgba(167, 187, 242, 0.2);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#status-heading {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #02004E;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #666;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-weight: 300;
|
||||
font-size: 17px;
|
||||
line-height: 150%;
|
||||
letter-spacing: 0;
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.loader-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 100px;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
radial-gradient(farthest-side, #5C7EF1 94%, #0000) top/8px 8px no-repeat,
|
||||
conic-gradient(#0000 30%, #5C7EF1);
|
||||
-webkit-mask: radial-gradient(farthest-side, #0000 calc(100% - 8px), #000 0);
|
||||
animation: l13 2s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes l13 {
|
||||
100% {
|
||||
transform: rotate(1turn)
|
||||
}
|
||||
}
|
||||
|
||||
.app-logo {
|
||||
position: absolute;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
padding: 0 18px;
|
||||
display: none;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.project-info {
|
||||
border: 1px solid #8C9DFF;
|
||||
border-radius: 10px;
|
||||
padding: 12px 16px;
|
||||
max-width: 600px;
|
||||
margin: 40px auto;
|
||||
background-color: #FBFCFF;
|
||||
}
|
||||
|
||||
.project-info h2 {
|
||||
color: #02004E;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.project-info p {
|
||||
color: #686791;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2 id="status-heading">Loading the app, just a moment…</h2>
|
||||
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
||||
available.</p>
|
||||
<div class="project-info">
|
||||
<h2>Varmennappi</h2>
|
||||
<p>Build an ESRS/XBRL assurance copilot for validated outputs.</p>
|
||||
</div>
|
||||
<div class="loader-container">
|
||||
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
||||
class="app-logo">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<video width="100%" height="315" controls loop>
|
||||
<source
|
||||
src="https://flatlogic.com/blog/wp-content/uploads/2025/04/20250430_1336_professional_dynamo_spinner_simple_compose_01jt349yvtenxt7xhg8hhr85j8.mp4"
|
||||
type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function checkAvailability() {
|
||||
fetch('/')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
setTimeout(checkAvailability, 5000);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setTimeout(checkAvailability, 5000);
|
||||
});
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', checkAvailability);
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const appTitle = document.querySelector('#status-heading');
|
||||
const panel = document.querySelector('.panel');
|
||||
const video = panel.querySelector('video');
|
||||
let clickCount = 0;
|
||||
|
||||
appTitle.addEventListener('click', function () {
|
||||
clickCount++;
|
||||
if (clickCount === 5) {
|
||||
panel.classList.toggle('show');
|
||||
if (panel.classList.contains('show')) {
|
||||
video.play();
|
||||
} else {
|
||||
video.pause();
|
||||
}
|
||||
clickCount = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM node:20.15.1-alpine AS builder
|
||||
RUN apk add --no-cache git
|
||||
WORKDIR /app
|
||||
COPY frontend/package.json frontend/yarn.lock ./
|
||||
RUN yarn install --pure-lockfile
|
||||
COPY frontend .
|
||||
RUN yarn build
|
||||
|
||||
FROM node:20.15.1-alpine
|
||||
WORKDIR /app
|
||||
COPY backend/package.json backend/yarn.lock ./
|
||||
RUN yarn install --pure-lockfile
|
||||
COPY backend .
|
||||
|
||||
COPY --from=builder /app/build /app/public
|
||||
CMD ["yarn", "start"]
|
||||
|
||||
85
Dockerfile.dev
Normal file
85
Dockerfile.dev
Normal file
@ -0,0 +1,85 @@
|
||||
# Base image for Node.js dependencies
|
||||
FROM node:20.15.1-alpine AS frontend-deps
|
||||
RUN apk add --no-cache git
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package.json frontend/yarn.lock ./
|
||||
RUN yarn install --pure-lockfile
|
||||
|
||||
FROM node:20.15.1-alpine AS backend-deps
|
||||
RUN apk add --no-cache git
|
||||
WORKDIR /app/backend
|
||||
COPY backend/package.json backend/yarn.lock ./
|
||||
RUN yarn install --pure-lockfile
|
||||
|
||||
FROM node:20.15.1-alpine AS app-shell-deps
|
||||
RUN apk add --no-cache git
|
||||
WORKDIR /app/app-shell
|
||||
COPY app-shell/package.json app-shell/yarn.lock ./
|
||||
RUN yarn install --pure-lockfile
|
||||
|
||||
# Nginx setup and application build
|
||||
FROM node:20.15.1-alpine AS build
|
||||
RUN apk add --no-cache git nginx curl
|
||||
RUN apk add --no-cache lsof procps
|
||||
RUN yarn global add concurrently
|
||||
|
||||
RUN apk add --no-cache \
|
||||
chromium \
|
||||
nss \
|
||||
freetype \
|
||||
harfbuzz \
|
||||
ttf-freefont \
|
||||
fontconfig
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||
|
||||
RUN mkdir -p /app/pids
|
||||
|
||||
# Make sure to add yarn global bin to PATH
|
||||
ENV PATH /root/.yarn/bin:/root/.config/yarn/global/node_modules/.bin:$PATH
|
||||
|
||||
# Copy dependencies
|
||||
WORKDIR /app
|
||||
COPY --from=frontend-deps /app/frontend /app/frontend
|
||||
COPY --from=backend-deps /app/backend /app/backend
|
||||
COPY --from=app-shell-deps /app/app-shell /app/app-shell
|
||||
|
||||
COPY frontend /app/frontend
|
||||
COPY backend /app/backend
|
||||
COPY app-shell /app/app-shell
|
||||
COPY docker /app/docker
|
||||
|
||||
# Copy all files from root to /app
|
||||
COPY . /app
|
||||
|
||||
# Copy Nginx configuration
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Copy custom error page
|
||||
COPY 502.html /usr/share/nginx/html/502.html
|
||||
|
||||
# Change owner and permissions of the error page
|
||||
RUN chown nginx:nginx /usr/share/nginx/html/502.html && \
|
||||
chmod 644 /usr/share/nginx/html/502.html
|
||||
|
||||
# Expose the port the app runs on
|
||||
EXPOSE 8080
|
||||
ENV NODE_ENV=dev_stage
|
||||
ENV FRONT_PORT=3001
|
||||
ENV BACKEND_PORT=3000
|
||||
ENV APP_SHELL_PORT=4000
|
||||
|
||||
|
||||
CMD ["sh", "-c", "\
|
||||
yarn --cwd /app/frontend dev & echo $! > /app/pids/frontend.pid && \
|
||||
yarn --cwd /app/backend start & echo $! > /app/pids/backend.pid && \
|
||||
sleep 10 && nginx -g 'daemon off;' & \
|
||||
NGINX_PID=$! && \
|
||||
echo 'Waiting for backend (port 3000) to be available...' && \
|
||||
while ! nc -z localhost ${BACKEND_PORT}; do \
|
||||
sleep 2; \
|
||||
done && \
|
||||
echo 'Backend is up. Starting app_shell for Git check...' && \
|
||||
yarn --cwd /app/app-shell start && \
|
||||
wait $NGINX_PID"]
|
||||
200
README.md
Normal file
200
README.md
Normal file
@ -0,0 +1,200 @@
|
||||
|
||||
|
||||
# Varmennappi
|
||||
|
||||
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
|
||||
|
||||
- Frontend: [React.js](https://flatlogic.com/templates?framework%5B%5D=react&sort=default)
|
||||
|
||||
- Backend: [NodeJS](https://flatlogic.com/templates?backend%5B%5D=nodejs&sort=default)
|
||||
|
||||
<details><summary>Backend Folder Structure</summary>
|
||||
|
||||
The generated application has the following backend folder structure:
|
||||
|
||||
`src` folder which contains your working files that will be used later to create the build. The src folder contains folders as:
|
||||
|
||||
- `auth` - config the library for authentication and authorization;
|
||||
|
||||
- `db` - contains such folders as:
|
||||
|
||||
- `api` - documentation that is automatically generated by jsdoc or other tools;
|
||||
|
||||
- `migrations` - is a skeleton of the database or all the actions that users do with the database;
|
||||
|
||||
- `models`- what will represent the database for the backend;
|
||||
|
||||
- `seeders` - the entity that creates the data for the database.
|
||||
|
||||
- `routes` - this folder would contain all the routes that you have created using Express Router and what they do would be exported from a Controller file;
|
||||
|
||||
- `services` - contains such folders as `emails` and `notifications`.
|
||||
</details>
|
||||
|
||||
- Database: PostgreSQL
|
||||
|
||||
- app-shel: Core application framework that provides essential infrastructure services
|
||||
for the entire application.
|
||||
-----------------------
|
||||
### We offer 2 ways how to start the project locally: by running Frontend and Backend or with Docker.
|
||||
-----------------------
|
||||
|
||||
## To start the project:
|
||||
|
||||
### Backend:
|
||||
|
||||
> Please change current folder: `cd backend`
|
||||
|
||||
#### Install local dependencies:
|
||||
`yarn install`
|
||||
|
||||
------------
|
||||
|
||||
#### Adjust local db:
|
||||
##### 1. Install postgres:
|
||||
|
||||
MacOS:
|
||||
|
||||
`brew install postgres`
|
||||
|
||||
> if you don’t have ‘brew‘ please install it (https://brew.sh) and repeat step `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_{your_project_name};`
|
||||
|
||||
Then give that new user privileges to the new database then quit the `psql`.
|
||||
|
||||
`postgres=> GRANT ALL PRIVILEGES ON DATABASE db_{your_project_name} TO admin;`
|
||||
|
||||
`postgres=> \q`
|
||||
|
||||
------------
|
||||
|
||||
#### Create database:
|
||||
`yarn db:create`
|
||||
|
||||
#### Start production build:
|
||||
`yarn start`
|
||||
|
||||
### Frontend:
|
||||
|
||||
> Please change current folder: `cd frontend`
|
||||
|
||||
## To start the project with Docker:
|
||||
### Description:
|
||||
|
||||
The project contains the **docker folder** and the `Dockerfile`.
|
||||
|
||||
The `Dockerfile` is used to Deploy the project to Google Cloud.
|
||||
|
||||
The **docker folder** contains a couple of helper scripts:
|
||||
|
||||
- `docker-compose.yml` (all our services: web, backend, db are described here)
|
||||
- `start-backend.sh` (starts backend, but only after the database)
|
||||
- `wait-for-it.sh` (imported from https://github.com/vishnubob/wait-for-it)
|
||||
|
||||
> To avoid breaking the application, we recommend you don't edit the following files: everything that includes the **docker folder** and `Dokerfile`.
|
||||
|
||||
## Run services:
|
||||
|
||||
1. Install docker compose (https://docs.docker.com/compose/install/)
|
||||
|
||||
2. Move to `docker` folder. All next steps should be done from this folder.
|
||||
|
||||
``` cd docker ```
|
||||
|
||||
3. Make executables from `wait-for-it.sh` and `start-backend.sh`:
|
||||
|
||||
``` chmod +x start-backend.sh && chmod +x wait-for-it.sh ```
|
||||
|
||||
4. Download dependend projects for services.
|
||||
|
||||
5. Review the docker-compose.yml file. Make sure that all services have Dockerfiles. Only db service doesn't require a Dockerfile.
|
||||
|
||||
6. Make sure you have needed ports (see them in `ports`) available on your local machine.
|
||||
|
||||
7. Start services:
|
||||
|
||||
7.1. With an empty database `rm -rf data && docker-compose up`
|
||||
|
||||
7.2. With a stored (from previus runs) database data `docker-compose up`
|
||||
|
||||
8. Check http://localhost:3000
|
||||
|
||||
9. Stop services:
|
||||
|
||||
9.1. Just press `Ctr+C`
|
||||
|
||||
## Most common errors:
|
||||
|
||||
1. `connection refused`
|
||||
|
||||
There could be many reasons, but the most common are:
|
||||
|
||||
- The port is not open on the destination machine.
|
||||
|
||||
- The port is open on the destination machine, but its backlog of pending connections is full.
|
||||
|
||||
- A firewall between the client and server is blocking access (also check local firewalls).
|
||||
|
||||
After checking for firewalls and that the port is open, use telnet to connect to the IP/port to test connectivity. This removes any potential issues from your application.
|
||||
|
||||
***MacOS:***
|
||||
|
||||
If you suspect that your SSH service might be down, you can run this command to find out:
|
||||
|
||||
`sudo service ssh status`
|
||||
|
||||
If the command line returns a status of down, then you’ve likely found the reason behind your connectivity error.
|
||||
|
||||
***Ubuntu:***
|
||||
|
||||
Sometimes a connection refused error can also indicate that there is an IP address conflict on your network. You can search for possible IP conflicts by running:
|
||||
|
||||
`arp-scan -I eth0 -l | grep <ipaddress>`
|
||||
|
||||
`arp-scan -I eth0 -l | grep <ipaddress>`
|
||||
|
||||
and
|
||||
|
||||
`arping <ipaddress>`
|
||||
|
||||
2. `yarn db:create` creates database with the assembled tables (on MacOS with Postgres database)
|
||||
|
||||
The workaround - put the next commands to your Postgres database terminal:
|
||||
|
||||
`DROP SCHEMA public CASCADE;`
|
||||
|
||||
`CREATE SCHEMA public;`
|
||||
|
||||
`GRANT ALL ON SCHEMA public TO postgres;`
|
||||
|
||||
`GRANT ALL ON SCHEMA public TO public;`
|
||||
|
||||
Afterwards, continue to start your project in the backend directory by running:
|
||||
|
||||
`yarn start`
|
||||
26
app-shell/.eslintrc.cjs
Normal file
26
app-shell/.eslintrc.cjs
Normal file
@ -0,0 +1,26 @@
|
||||
const globals = require('globals');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
files: ['**/*.js', '**/*.ts', '**/*.tsx'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2021,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
rules: {
|
||||
'no-unused-vars': 'warn',
|
||||
'no-console': 'off',
|
||||
'indent': ['error', 2],
|
||||
'quotes': ['error', 'single'],
|
||||
'semi': ['error', 'always'],
|
||||
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
11
app-shell/.prettierrc
Normal file
11
app-shell/.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
app-shell/.sequelizerc
Normal file
7
app-shell/.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
app-shell/Dockerfile
Normal file
23
app-shell/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 4000
|
||||
|
||||
CMD [ "yarn", "start" ]
|
||||
13
app-shell/README.md
Normal file
13
app-shell/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
#test - template backend,
|
||||
|
||||
#### Run App on local machine:
|
||||
|
||||
##### Install local dependencies:
|
||||
|
||||
- `yarn install`
|
||||
|
||||
---
|
||||
|
||||
##### Start build:
|
||||
|
||||
- `yarn start`
|
||||
43
app-shell/package.json
Normal file
43
app-shell/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "app-shell",
|
||||
"description": "app-shell",
|
||||
"scripts": {
|
||||
"start": "node ./src/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.7",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.6.7",
|
||||
"bcrypt": "5.1.1",
|
||||
"cors": "2.8.5",
|
||||
"eslint": "^9.13.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",
|
||||
"passport": "^0.7.0",
|
||||
"passport-google-oauth2": "^0.2.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-microsoft": "^0.1.0",
|
||||
"postcss": "^8.5.1",
|
||||
"puppeteer": "^24.10.0",
|
||||
"sequelize-json-schema": "^2.1.1",
|
||||
"pg": "^8.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^8.12.2",
|
||||
"@typescript-eslint/parser": "^8.12.2",
|
||||
"cross-env": "7.0.3",
|
||||
"mocha": "8.1.3",
|
||||
"nodemon": "^3.1.7",
|
||||
"sequelize-cli": "6.6.2"
|
||||
}
|
||||
}
|
||||
5
app-shell/src/_schema.json
Normal file
5
app-shell/src/_schema.json
Normal file
File diff suppressed because one or more lines are too long
18
app-shell/src/config.js
Normal file
18
app-shell/src/config.js
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
const config = {
|
||||
admin_pass: "5ba91a78",
|
||||
admin_email: "admin@flatlogic.com",
|
||||
schema_encryption_key: process.env.SCHEMA_ENCRYPTION_KEY || '',
|
||||
|
||||
project_uuid: '5ba91a78-3caa-44ed-8f25-45f266a8bef8',
|
||||
flHost: process.env.NODE_ENV === 'production' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
||||
|
||||
gitea_domain: process.env.GITEA_DOMAIN || 'gitea.flatlogic.app',
|
||||
gitea_username: process.env.GITEA_USERNAME || 'admin',
|
||||
gitea_api_token: process.env.GITEA_API_TOKEN || null,
|
||||
github_repo_url: process.env.GITHUB_REPO_URL || null,
|
||||
github_token: process.env.GITHUB_TOKEN || null,
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
23
app-shell/src/helpers.js
Normal file
23
app-shell/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' });
|
||||
}
|
||||
};
|
||||
54
app-shell/src/index.js
Normal file
54
app-shell/src/index.js
Normal file
@ -0,0 +1,54 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const app = express();
|
||||
const bodyParser = require('body-parser');
|
||||
const checkPermissions = require('./middlewares/check-permissions');
|
||||
const modifyPath = require('./middlewares/modify-path');
|
||||
const VCS = require('./services/vcs');
|
||||
|
||||
const executorRoutes = require('./routes/executor');
|
||||
const vcsRoutes = require('./routes/vcs');
|
||||
|
||||
// Function to initialize the Git repository
|
||||
function initRepo() {
|
||||
const projectId = '33934';
|
||||
return VCS.initRepo(projectId);
|
||||
}
|
||||
|
||||
// Start the Express app on APP_SHELL_PORT (4000)
|
||||
function startServer() {
|
||||
const PORT = 4000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Listening on port ${PORT}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Run Git check after the server is up
|
||||
function runGitCheck() {
|
||||
initRepo()
|
||||
.then(result => {
|
||||
console.log(result?.message ? result.message : result);
|
||||
// Here you can add additional logic if needed
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error during repo initialization:', err);
|
||||
// Optionally exit the process if Git check is critical:
|
||||
// process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
app.use(cors({ origin: true }));
|
||||
app.use(bodyParser.json());
|
||||
app.use(checkPermissions);
|
||||
app.use(modifyPath);
|
||||
|
||||
app.use('/executor', executorRoutes);
|
||||
app.use('/vcs', vcsRoutes);
|
||||
|
||||
// Start the app_shell server
|
||||
startServer();
|
||||
|
||||
// Now perform Git check
|
||||
runGitCheck();
|
||||
|
||||
module.exports = app;
|
||||
17
app-shell/src/middlewares/check-permissions.js
Normal file
17
app-shell/src/middlewares/check-permissions.js
Normal file
@ -0,0 +1,17 @@
|
||||
const config = require('../config');
|
||||
|
||||
function checkPermissions(req, res, next) {
|
||||
const project_uuid = config.project_uuid;
|
||||
const requiredHeader = 'X-Project-UUID';
|
||||
const headerValue = req.headers[requiredHeader.toLowerCase()];
|
||||
// Logging whatever request we're getting
|
||||
console.log('Request:', req.url, req.method, req.body, req.headers);
|
||||
|
||||
if (headerValue && headerValue === project_uuid) {
|
||||
next();
|
||||
} else {
|
||||
res.status(403).send({ error: 'Stop right there, criminal scum! Your project UUID is invalid or missing.' });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = checkPermissions;
|
||||
8
app-shell/src/middlewares/modify-path.js
Normal file
8
app-shell/src/middlewares/modify-path.js
Normal file
@ -0,0 +1,8 @@
|
||||
function modifyPath(req, res, next) {
|
||||
if (req.body && req.body.path) {
|
||||
req.body.path = '../../../' + req.body.path;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = modifyPath;
|
||||
346
app-shell/src/routes/executor.js
Normal file
346
app-shell/src/routes/executor.js
Normal file
@ -0,0 +1,346 @@
|
||||
const express = require('express');
|
||||
const multer = require('multer');
|
||||
const upload = multer({ dest: 'uploads/' });
|
||||
const fs = require('fs');
|
||||
|
||||
const ExecutorService = require('../services/executor');
|
||||
const { takeScreenshot } = require("../services/screenshot_service");
|
||||
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post(
|
||||
'/read_project_tree',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path } = req.body;
|
||||
const tree = await ExecutorService.readProjectTree(path);
|
||||
res.status(200).send(tree);
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/read_file',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path, showLines } = req.body;
|
||||
const content = await ExecutorService.readFileContents(path, showLines);
|
||||
res.status(200).send(content);
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/count_file_lines',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path } = req.body;
|
||||
const content = await ExecutorService.countFileLines(path);
|
||||
res.status(200).send(content);
|
||||
}),
|
||||
);
|
||||
|
||||
// router.post(
|
||||
// '/read_file_header',
|
||||
// wrapAsync(async (req, res) => {
|
||||
// const { path, N } = req.body;
|
||||
// try {
|
||||
// const header = await ExecutorService.readFileHeader(path, N);
|
||||
// res.status(200).send(header);
|
||||
// } catch (error) {
|
||||
// res.status(500).send({
|
||||
// error: true,
|
||||
// message: error.message,
|
||||
// details: error.details || error.stack,
|
||||
// validation: error.validation
|
||||
// });
|
||||
// }
|
||||
// }),
|
||||
// );
|
||||
|
||||
router.post(
|
||||
'/read_file_line_context',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path, lineNumber, windowSize, showLines } = req.body;
|
||||
try {
|
||||
const context = await ExecutorService.readFileLineContext(path, lineNumber, windowSize, showLines);
|
||||
res.status(200).send(context);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/write_file',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path, fileContents, comment } = req.body;
|
||||
try {
|
||||
await ExecutorService.writeFile(path, fileContents, comment);
|
||||
res.status(200).send({ message: 'File written successfully' });
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/insert_file_content',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path, lineNumber, newContent, message } = req.body;
|
||||
try {
|
||||
await ExecutorService.insertFileContent(path, lineNumber, newContent, message);
|
||||
res.status(200).send({ message: 'File written successfully' });
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/replace_file_line',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path, lineNumber, newText } = req.body;
|
||||
try {
|
||||
const result = await ExecutorService.replaceFileLine(path, lineNumber, newText);
|
||||
res.status(200).send(result);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
router.post(
|
||||
'/replace_file_chunk',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path, startLine, endLine, newCode } = req.body;
|
||||
try {
|
||||
const result = await ExecutorService.replaceFileChunk(path, startLine, endLine, newCode);
|
||||
res.status(200).send(result);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/delete_file_lines',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path, startLine, endLine, message } = req.body;
|
||||
try {
|
||||
const result = await ExecutorService.deleteFileLines(path, startLine, endLine, message);
|
||||
res.status(200).send(result);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/validate_file',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { path } = req.body;
|
||||
try {
|
||||
const validationResult = await ExecutorService.validateFile(path);
|
||||
res.status(200).send({ validationResult });
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
router.post(
|
||||
'/check_frontend_runtime_error',
|
||||
wrapAsync(async (req, res) => {
|
||||
try {
|
||||
const result = await ExecutorService.checkFrontendRuntimeLogs();
|
||||
res.status(200).send(result);
|
||||
} catch (error) {
|
||||
res.status(500).send({ error: error });
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
router.post(
|
||||
'/replace_code_block',
|
||||
wrapAsync(async (req, res) => {
|
||||
const {path, oldCode, newCode, message} = req.body;
|
||||
try {
|
||||
const response = await ExecutorService.replaceCodeBlock(path, oldCode, newCode, message);
|
||||
res.status(200).send(response);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: true,
|
||||
message: error.message,
|
||||
details: error.details || error.stack,
|
||||
validation: error.validation
|
||||
})
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
router.post('/update_project_files_from_scheme',
|
||||
upload.single('file'), // 'file' - name of the field in the form
|
||||
async (req, res) => {
|
||||
console.log('Request received');
|
||||
console.log('Headers:', req.headers);
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ error: 'No file uploaded' });
|
||||
}
|
||||
|
||||
console.log('File info:', {
|
||||
originalname: req.file.originalname,
|
||||
path: req.file.path,
|
||||
size: req.file.size,
|
||||
mimetype: req.file.mimetype
|
||||
});
|
||||
|
||||
try {
|
||||
console.log('Starting update process...');
|
||||
const result = await ExecutorService.updateProjectFilesFromScheme(req.file.path);
|
||||
console.log('Update completed, result:', result);
|
||||
|
||||
console.log('Removing temp file...');
|
||||
fs.unlinkSync(req.file.path);
|
||||
console.log('Temp file removed');
|
||||
|
||||
console.log('Sending response...');
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
console.error('Error in route handler:', error);
|
||||
if (req.file) {
|
||||
try {
|
||||
fs.unlinkSync(req.file.path);
|
||||
console.log('Temp file removed after error');
|
||||
} catch (unlinkError) {
|
||||
console.error('Error removing temp file:', unlinkError);
|
||||
}
|
||||
}
|
||||
console.error('Update project files error:', error);
|
||||
return res.status(500).json({
|
||||
error: error.message,
|
||||
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/get_db_schema',
|
||||
wrapAsync(async (req, res) => {
|
||||
try {
|
||||
|
||||
const jsonSchema = await ExecutorService.getDBSchema();
|
||||
res.status(200).send({ jsonSchema });
|
||||
} catch (error) {
|
||||
res.status(500).send({ error: error });
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/execute_sql',
|
||||
wrapAsync(async (req, res) => {
|
||||
try {
|
||||
const { query } = req.body;
|
||||
const result = await ExecutorService.executeSQL(query);
|
||||
res.status(200).send(result);
|
||||
} catch (error) {
|
||||
res.status(500).send({ error: error });
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/search_files',
|
||||
wrapAsync(async (req, res) => {
|
||||
try {
|
||||
const { searchStrings } = req.body;
|
||||
|
||||
if (
|
||||
typeof searchStrings !== 'string' &&
|
||||
!(
|
||||
Array.isArray(searchStrings) &&
|
||||
searchStrings.every(item => typeof item === 'string')
|
||||
)
|
||||
) {
|
||||
return res.status(400).send({ error: 'searchStrings must be a string or an array of strings' });
|
||||
}
|
||||
|
||||
const result = await ExecutorService.searchFiles(searchStrings);
|
||||
res.status(200).send(result);
|
||||
} catch (error) {
|
||||
res.status(500).send({ error: error.message });
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/screenshot',
|
||||
wrapAsync(async (req, res) => {
|
||||
const FRONT_PORT = process.env.FRONT_PORT || 3001;
|
||||
const targetUrl = `http://localhost:${FRONT_PORT}${req.body.url || '/'}`;
|
||||
|
||||
const filename = req.query.filename || `screenshot-${Date.now()}.png`;
|
||||
const fullPage = req.query.fullPage !== 'false';
|
||||
|
||||
try {
|
||||
console.log(`[App-Shell/Screenshot Route]: request to take screenshot of ${targetUrl} with filename ${filename} and fullPage=${fullPage}`);
|
||||
const outputPath = await takeScreenshot(targetUrl, filename, fullPage);
|
||||
|
||||
res.sendFile(outputPath, async (err) => {
|
||||
if (err) {
|
||||
console.error(`[App-Shell/Screenshot Route]: error sending screenshot ${outputPath}:`, err);
|
||||
if (err.code === 'ENOENT') {
|
||||
res.status(404).send('Screenshot not found.');
|
||||
} else {
|
||||
res.status(500).send('Error sending screenshot: ' + err.message);
|
||||
}
|
||||
} else {
|
||||
console.log(`[App-Shell/Screenshot Route]: Screenshot sent successfully: ${outputPath}`);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[App-Shell/Screenshot Route]: Could not take screenshot of ${targetUrl}:`, error);
|
||||
res.status(500).send(`Error taking screenshot: ${error.message}`);
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
40
app-shell/src/routes/vcs.js
Normal file
40
app-shell/src/routes/vcs.js
Normal file
@ -0,0 +1,40 @@
|
||||
const express = require('express');
|
||||
const wrapAsync = require('../helpers').wrapAsync; // Ваша обёртка для обработки асинхронных маршрутов
|
||||
const VSC = require('../services/vcs');
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/init', wrapAsync(async (req, res) => {
|
||||
const result = await VSC.initRepo();
|
||||
res.status(200).send(result);
|
||||
}));
|
||||
|
||||
router.post('/commit', wrapAsync(async (req, res) => {
|
||||
const { message, files, dev_schema } = req.body;
|
||||
const result = await VSC.commitChanges(message, files, dev_schema);
|
||||
res.status(200).send(result);
|
||||
}));
|
||||
|
||||
router.post('/log', wrapAsync(async (req, res) => {
|
||||
const result = await VSC.getLog();
|
||||
res.status(200).send(result);
|
||||
}));
|
||||
|
||||
router.post('/rollback', wrapAsync(async (req, res) => {
|
||||
const { ref } = req.body;
|
||||
// const result = await VSC.checkout(ref);
|
||||
const result = await VSC.revert(ref);
|
||||
res.status(200).send(result);
|
||||
}));
|
||||
|
||||
router.post('/sync-to-stable', wrapAsync(async (req, res) => {
|
||||
const result = await VSC.mergeDevIntoMaster();
|
||||
res.status(200).send(result);
|
||||
}));
|
||||
|
||||
router.post('/reset-dev', wrapAsync(async (req, res) => {
|
||||
const result = await VSC.resetDevBranch();
|
||||
res.status(200).send(result);
|
||||
}));
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
module.exports = router;
|
||||
88
app-shell/src/services/database.js
Normal file
88
app-shell/src/services/database.js
Normal file
@ -0,0 +1,88 @@
|
||||
// Database.js
|
||||
const { Client } = require('pg');
|
||||
const config = require('../../../backend/src/db/db.config');
|
||||
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
const dbConfig = config[env];
|
||||
|
||||
class Database {
|
||||
constructor() {
|
||||
this.client = new Client({
|
||||
user: dbConfig.username,
|
||||
password: dbConfig.password,
|
||||
database: dbConfig.database,
|
||||
host: dbConfig.host,
|
||||
port: dbConfig.port
|
||||
});
|
||||
|
||||
// Connect once, reuse the client
|
||||
this.client.connect().catch(err => {
|
||||
console.error('Error connecting to the database:', err);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
async executeSQL(query) {
|
||||
try {
|
||||
const result = await this.client.query(query);
|
||||
return {
|
||||
success: true,
|
||||
rows: result.rows
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Method to fetch simple table/column info from 'information_schema'
|
||||
// (You can expand this to handle constraints, indexes, etc.)
|
||||
async getDBSchema(schemaName = 'public') {
|
||||
try {
|
||||
const tableQuery = `
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = $1
|
||||
AND table_type = 'BASE TABLE'
|
||||
ORDER BY table_name
|
||||
`;
|
||||
|
||||
const columnQuery = `
|
||||
SELECT table_name, column_name, data_type, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = $1
|
||||
ORDER BY table_name, ordinal_position
|
||||
`;
|
||||
|
||||
const [tablesResult, columnsResult] = await Promise.all([
|
||||
this.client.query(tableQuery, [schemaName]),
|
||||
this.client.query(columnQuery, [schemaName]),
|
||||
]);
|
||||
|
||||
// Build a simple schema object:
|
||||
const tables = tablesResult.rows.map(row => row.table_name);
|
||||
const columnsByTable = {};
|
||||
|
||||
columnsResult.rows.forEach(row => {
|
||||
const { table_name, column_name, data_type, is_nullable } = row;
|
||||
if (!columnsByTable[table_name]) columnsByTable[table_name] = [];
|
||||
columnsByTable[table_name].push({ column_name, data_type, is_nullable });
|
||||
});
|
||||
|
||||
// Combine tables with their columns
|
||||
return tables.map(table => ({
|
||||
table,
|
||||
columns: columnsByTable[table] || [],
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error fetching schema:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async close() {
|
||||
await this.client.end();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Database();
|
||||
1206
app-shell/src/services/executor.js
Normal file
1206
app-shell/src/services/executor.js
Normal file
File diff suppressed because it is too large
Load Diff
16
app-shell/src/services/notifications/errors/forbidden.js
Normal file
16
app-shell/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
app-shell/src/services/notifications/errors/validation.js
Normal file
16
app-shell/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
app-shell/src/services/notifications/helpers.js
Normal file
30
app-shell/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
app-shell/src/services/notifications/list.js
Normal file
100
app-shell/src/services/notifications/list.js
Normal file
@ -0,0 +1,100 @@
|
||||
const errors = {
|
||||
app: {
|
||||
title: 'test',
|
||||
},
|
||||
|
||||
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;
|
||||
67
app-shell/src/services/project-events.js
Normal file
67
app-shell/src/services/project-events.js
Normal file
@ -0,0 +1,67 @@
|
||||
const axios = require('axios');
|
||||
const config = require('../config.js');
|
||||
|
||||
class ProjectEventsService {
|
||||
/**
|
||||
* Sends a project event to the Rails backend
|
||||
*
|
||||
* @param {string} eventType - Type of the event
|
||||
* @param {object} payload - Event payload data
|
||||
* @param {object} options - Additional options
|
||||
* @param {string} [options.conversationId] - Optional conversation ID
|
||||
* @param {boolean} [options.isError=false] - Whether this is an error event
|
||||
* @returns {Promise<object>} - Response from the webhook
|
||||
*/
|
||||
static async sendEvent(eventType, payload = {}, options = {}) {
|
||||
try {
|
||||
console.log(`[DEBUG] Sending project event: ${eventType}`);
|
||||
|
||||
const webhookUrl = `https://flatlogic.com/projects/events_webhook`;
|
||||
|
||||
// Prepare the event data
|
||||
const eventData = {
|
||||
project_uuid: config.project_uuid,
|
||||
event_type: eventType,
|
||||
payload: {
|
||||
...payload,
|
||||
message: `[APP] ${payload.message}`,
|
||||
is_error: options.isError || false,
|
||||
system_message: true,
|
||||
is_command_info: true
|
||||
}
|
||||
};
|
||||
|
||||
// Add conversation ID if provided
|
||||
if (options.conversationId) {
|
||||
eventData.conversation_id = options.conversationId;
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-project-uuid': config.project_uuid
|
||||
};
|
||||
|
||||
console.log(`[DEBUG] Event data: ${JSON.stringify(eventData)}`);
|
||||
|
||||
const response = await axios.post(webhookUrl, eventData, { headers });
|
||||
|
||||
console.log(`[DEBUG] Event sent successfully, status: ${response.status}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(`[ERROR] Failed to send project event: ${error.message}`);
|
||||
if (error.response) {
|
||||
console.error(`[ERROR] Response status: ${error.response.status}`);
|
||||
console.error(`[ERROR] Response data: ${JSON.stringify(error.response.data)}`);
|
||||
}
|
||||
|
||||
// Don't throw the error, just return a failed status
|
||||
// This prevents errors in the event service from breaking app functionality
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProjectEventsService;
|
||||
83
app-shell/src/services/screenshot_service.js
Normal file
83
app-shell/src/services/screenshot_service.js
Normal file
@ -0,0 +1,83 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const path = require('path');
|
||||
const fs = require('fs/promises');
|
||||
const config = require('../config');
|
||||
|
||||
const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3000';
|
||||
const SCREENSHOT_DIR = process.env.SCREENSHOT_DIR || '/tmp/screenshots';
|
||||
fs.mkdir(SCREENSHOT_DIR, { recursive: true }).catch(console.error);
|
||||
|
||||
async function takeScreenshot(url, filename = `screenshot-${Date.now()}.png`, fullPage = true) {
|
||||
let browser;
|
||||
|
||||
const response = await axios.post(
|
||||
`${BACKEND_URL}/api/auth/signin/local`,
|
||||
{ email: config.admin_email, password: config.admin_pass },
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const token = response.data;
|
||||
|
||||
const outputPath = path.join(SCREENSHOT_DIR, filename);
|
||||
|
||||
try {
|
||||
browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-accelerated-2d-canvas',
|
||||
'--disable-gpu',
|
||||
'window-size=1920,1080'
|
||||
]
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
|
||||
await page.setViewport({ width: 1920, height: 1080 });
|
||||
|
||||
if (token) {
|
||||
await page.setRequestInterception(true);
|
||||
|
||||
page.on('request', interceptedRequest => {
|
||||
if (interceptedRequest.isInterceptResolutionHandled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = interceptedRequest.headers();
|
||||
if (!headers['authorization'] && !headers['Authorization']) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
interceptedRequest.continue({ headers });
|
||||
});
|
||||
|
||||
page.on('requestfailed', request => {
|
||||
console.error(`[ScreenshotService]: Request failed: ${request.url()} - ${request.failure().errorText}`);
|
||||
});
|
||||
}
|
||||
|
||||
await page.goto(url, { waitUntil: 'load', timeout: 60000 });
|
||||
|
||||
await page.screenshot({
|
||||
path: outputPath,
|
||||
fullPage: true,
|
||||
});
|
||||
|
||||
console.log(`[ScreenshotService]: Screenshot saved to ${outputPath}`);
|
||||
return outputPath;
|
||||
} catch (error) {
|
||||
console.error(`[ScreenshotService]: Error taking screenshot: ${error.message}`);
|
||||
throw error;
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { takeScreenshot };
|
||||
1205
app-shell/src/services/vcs.js
Normal file
1205
app-shell/src/services/vcs.js
Normal file
File diff suppressed because it is too large
Load Diff
3044
app-shell/yarn.lock
Normal file
3044
app-shell/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
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 @@
|
||||
#Varmennappi - 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_varmennappi;`
|
||||
|
||||
- Then give that new user privileges to the new database then quit the `psql`.
|
||||
- `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_varmennappi 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`
|
||||
53
backend/package.json
Normal file
53
backend/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "varmennappi",
|
||||
"description": "Varmennappi - template backend",
|
||||
"scripts": {
|
||||
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
||||
"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",
|
||||
"watch": "node watcher.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google-cloud/storage": "^5.18.2",
|
||||
"axios": "^1.6.7",
|
||||
"bcrypt": "5.1.1",
|
||||
"chokidar": "^4.0.3",
|
||||
"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 });
|
||||
});
|
||||
}
|
||||
77
backend/src/config.js
Normal file
77
backend/src/config.js
Normal file
@ -0,0 +1,77 @@
|
||||
const os = require('os');
|
||||
|
||||
const config = {
|
||||
gcloud: {
|
||||
bucket: 'fldemo-files',
|
||||
hash: 'eff6a5d5b6eae3f5e185009c3494b29f',
|
||||
},
|
||||
bcrypt: {
|
||||
saltRounds: 12,
|
||||
},
|
||||
admin_pass: '5ba91a78',
|
||||
user_pass: '45f266a8bef8',
|
||||
admin_email: 'admin@flatlogic.com',
|
||||
providers: {
|
||||
LOCAL: 'local',
|
||||
GOOGLE: 'google',
|
||||
MICROSOFT: 'microsoft',
|
||||
},
|
||||
secret_key: process.env.SECRET_KEY || '',
|
||||
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: process.env.GOOGLE_CLIENT_ID || '',
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET || '',
|
||||
},
|
||||
microsoft: {
|
||||
clientId: process.env.MS_CLIENT_ID || '',
|
||||
clientSecret: process.env.MS_CLIENT_SECRET || '',
|
||||
},
|
||||
uploadDir: os.tmpdir(),
|
||||
email: {
|
||||
from: 'Varmennappi <app@flatlogic.app>',
|
||||
host: 'email-smtp.us-east-1.amazonaws.com',
|
||||
port: 587,
|
||||
auth: {
|
||||
user: process.env.EMAIL_USER || '',
|
||||
pass: process.env.EMAIL_PASS,
|
||||
},
|
||||
tls: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
},
|
||||
roles: {
|
||||
super_admin: 'Super Administrator',
|
||||
|
||||
admin: 'Administrator',
|
||||
|
||||
user: 'Viewer',
|
||||
},
|
||||
|
||||
project_uuid: '5ba91a78-3caa-44ed-8f25-45f266a8bef8',
|
||||
flHost:
|
||||
process.env.NODE_ENV === 'production' ||
|
||||
process.env.NODE_ENV === 'dev_stage'
|
||||
? 'https://flatlogic.com/projects'
|
||||
: 'http://localhost:3000/projects',
|
||||
|
||||
gpt_key: process.env.GPT_KEY || '',
|
||||
};
|
||||
|
||||
config.pexelsKey = process.env.PEXELS_KEY || '';
|
||||
config.pexelsQuery = 'Abstract data validation concept';
|
||||
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;
|
||||
433
backend/src/db/api/audit_logs.js
Normal file
433
backend/src/db/api/audit_logs.js
Normal file
@ -0,0 +1,433 @@
|
||||
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 Audit_logsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_logs = await db.audit_logs.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
action: data.action || null,
|
||||
entity_type: data.entity_type || null,
|
||||
entity_id: data.entity_id || null,
|
||||
diff_json: data.diff_json || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await audit_logs.setOrg(data.org || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await audit_logs.setActor_user(data.actor_user || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await audit_logs.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return audit_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 audit_logsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
action: item.action || null,
|
||||
entity_type: item.entity_type || null,
|
||||
entity_id: item.entity_id || null,
|
||||
diff_json: item.diff_json || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const audit_logs = await db.audit_logs.bulkCreate(audit_logsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return audit_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 audit_logs = await db.audit_logs.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.action !== undefined) updatePayload.action = data.action;
|
||||
|
||||
if (data.entity_type !== undefined)
|
||||
updatePayload.entity_type = data.entity_type;
|
||||
|
||||
if (data.entity_id !== undefined) updatePayload.entity_id = data.entity_id;
|
||||
|
||||
if (data.diff_json !== undefined) updatePayload.diff_json = data.diff_json;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await audit_logs.update(updatePayload, { transaction });
|
||||
|
||||
if (data.org !== undefined) {
|
||||
await audit_logs.setOrg(
|
||||
data.org,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.actor_user !== undefined) {
|
||||
await audit_logs.setActor_user(
|
||||
data.actor_user,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await audit_logs.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return audit_logs;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_logs = await db.audit_logs.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of audit_logs) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of audit_logs) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return audit_logs;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_logs = await db.audit_logs.findByPk(id, options);
|
||||
|
||||
await audit_logs.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await audit_logs.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return audit_logs;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_logs = await db.audit_logs.findOne({ where }, { transaction });
|
||||
|
||||
if (!audit_logs) {
|
||||
return audit_logs;
|
||||
}
|
||||
|
||||
const output = audit_logs.get({ plain: true });
|
||||
|
||||
output.org = await audit_logs.getOrg({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.actor_user = await audit_logs.getActor_user({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await audit_logs.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'org',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.users,
|
||||
as: 'actor_user',
|
||||
|
||||
where: filter.actor_user
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.actor_user
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
firstName: {
|
||||
[Op.or]: filter.actor_user
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.action) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('audit_logs', 'action', filter.action),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.entity_type) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'audit_logs',
|
||||
'entity_type',
|
||||
filter.entity_type,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.diff_json) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('audit_logs', 'diff_json', filter.diff_json),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.entity_idRange) {
|
||||
const [start, end] = filter.entity_idRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
entity_id: {
|
||||
...where.entity_id,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
entity_id: {
|
||||
...where.entity_id,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.org) {
|
||||
const listItems = filter.org.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
orgId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.audit_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('audit_logs', 'action', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.audit_logs.findAll({
|
||||
attributes: ['id', 'action'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['action', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.action,
|
||||
}));
|
||||
}
|
||||
};
|
||||
393
backend/src/db/api/audit_packs.js
Normal file
393
backend/src/db/api/audit_packs.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 Audit_packsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_packs = await db.audit_packs.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
zip_uri: data.zip_uri || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await audit_packs.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await audit_packs.setXbrl_instance(data.xbrl_instance || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await audit_packs.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return audit_packs;
|
||||
}
|
||||
|
||||
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 audit_packsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
zip_uri: item.zip_uri || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const audit_packs = await db.audit_packs.bulkCreate(audit_packsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return audit_packs;
|
||||
}
|
||||
|
||||
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 audit_packs = await db.audit_packs.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.zip_uri !== undefined) updatePayload.zip_uri = data.zip_uri;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await audit_packs.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await audit_packs.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.xbrl_instance !== undefined) {
|
||||
await audit_packs.setXbrl_instance(
|
||||
data.xbrl_instance,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await audit_packs.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return audit_packs;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_packs = await db.audit_packs.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of audit_packs) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of audit_packs) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return audit_packs;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_packs = await db.audit_packs.findByPk(id, options);
|
||||
|
||||
await audit_packs.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await audit_packs.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return audit_packs;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const audit_packs = await db.audit_packs.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!audit_packs) {
|
||||
return audit_packs;
|
||||
}
|
||||
|
||||
const output = audit_packs.get({ plain: true });
|
||||
|
||||
output.project = await audit_packs.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.xbrl_instance = await audit_packs.getXbrl_instance({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await audit_packs.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.xbrl_instances,
|
||||
as: 'xbrl_instance',
|
||||
|
||||
where: filter.xbrl_instance
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.xbrl_instance
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
build_status: {
|
||||
[Op.or]: filter.xbrl_instance
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.zip_uri) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('audit_packs', 'zip_uri', filter.zip_uri),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.audit_packs.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('audit_packs', 'zip_uri', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.audit_packs.findAll({
|
||||
attributes: ['id', 'zip_uri'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['zip_uri', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.zip_uri,
|
||||
}));
|
||||
}
|
||||
};
|
||||
478
backend/src/db/api/contexts.js
Normal file
478
backend/src/db/api/contexts.js
Normal file
@ -0,0 +1,478 @@
|
||||
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 ContextsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const contexts = await db.contexts.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
identifier_scheme: data.identifier_scheme || null,
|
||||
identifier_value: data.identifier_value || null,
|
||||
period_start: data.period_start || null,
|
||||
period_end: data.period_end || null,
|
||||
scenario: data.scenario || null,
|
||||
segment: data.segment || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await contexts.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await contexts.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
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 contextsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
identifier_scheme: item.identifier_scheme || null,
|
||||
identifier_value: item.identifier_value || null,
|
||||
period_start: item.period_start || null,
|
||||
period_end: item.period_end || null,
|
||||
scenario: item.scenario || null,
|
||||
segment: item.segment || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const contexts = await db.contexts.bulkCreate(contextsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
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 contexts = await db.contexts.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.identifier_scheme !== undefined)
|
||||
updatePayload.identifier_scheme = data.identifier_scheme;
|
||||
|
||||
if (data.identifier_value !== undefined)
|
||||
updatePayload.identifier_value = data.identifier_value;
|
||||
|
||||
if (data.period_start !== undefined)
|
||||
updatePayload.period_start = data.period_start;
|
||||
|
||||
if (data.period_end !== undefined)
|
||||
updatePayload.period_end = data.period_end;
|
||||
|
||||
if (data.scenario !== undefined) updatePayload.scenario = data.scenario;
|
||||
|
||||
if (data.segment !== undefined) updatePayload.segment = data.segment;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await contexts.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await contexts.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await contexts.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const contexts = await db.contexts.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of contexts) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of contexts) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const contexts = await db.contexts.findByPk(id, options);
|
||||
|
||||
await contexts.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await contexts.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const contexts = await db.contexts.findOne({ where }, { transaction });
|
||||
|
||||
if (!contexts) {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
const output = contexts.get({ plain: true });
|
||||
|
||||
output.mappings_default_context =
|
||||
await contexts.getMappings_default_context({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.metric_values_context = await contexts.getMetric_values_context({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.narratives_context = await contexts.getNarratives_context({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.project = await contexts.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await contexts.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.identifier_scheme) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'contexts',
|
||||
'identifier_scheme',
|
||||
filter.identifier_scheme,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.identifier_value) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'contexts',
|
||||
'identifier_value',
|
||||
filter.identifier_value,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.scenario) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('contexts', 'scenario', filter.scenario),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.segment) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('contexts', 'segment', filter.segment),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{
|
||||
period_start: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
{
|
||||
period_end: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.period_startRange) {
|
||||
const [start, end] = filter.period_startRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_start: {
|
||||
...where.period_start,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_start: {
|
||||
...where.period_start,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.period_endRange) {
|
||||
const [start, end] = filter.period_endRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_end: {
|
||||
...where.period_end,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_end: {
|
||||
...where.period_end,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.contexts.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('contexts', 'identifier_value', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.contexts.findAll({
|
||||
attributes: ['id', 'identifier_value'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['identifier_value', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.identifier_value,
|
||||
}));
|
||||
}
|
||||
};
|
||||
478
backend/src/db/api/evidence_files.js
Normal file
478
backend/src/db/api/evidence_files.js
Normal file
@ -0,0 +1,478 @@
|
||||
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 Evidence_filesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const evidence_files = await db.evidence_files.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
file_name: data.file_name || null,
|
||||
mime_type: data.mime_type || null,
|
||||
gcs_uri: data.gcs_uri || null,
|
||||
checksum: data.checksum || null,
|
||||
uploaded_at: data.uploaded_at || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await evidence_files.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await evidence_files.setUploaded_by(data.uploaded_by || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await evidence_files.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return evidence_files;
|
||||
}
|
||||
|
||||
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 evidence_filesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
file_name: item.file_name || null,
|
||||
mime_type: item.mime_type || null,
|
||||
gcs_uri: item.gcs_uri || null,
|
||||
checksum: item.checksum || null,
|
||||
uploaded_at: item.uploaded_at || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const evidence_files = await db.evidence_files.bulkCreate(
|
||||
evidence_filesData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return evidence_files;
|
||||
}
|
||||
|
||||
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 evidence_files = await db.evidence_files.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.file_name !== undefined) updatePayload.file_name = data.file_name;
|
||||
|
||||
if (data.mime_type !== undefined) updatePayload.mime_type = data.mime_type;
|
||||
|
||||
if (data.gcs_uri !== undefined) updatePayload.gcs_uri = data.gcs_uri;
|
||||
|
||||
if (data.checksum !== undefined) updatePayload.checksum = data.checksum;
|
||||
|
||||
if (data.uploaded_at !== undefined)
|
||||
updatePayload.uploaded_at = data.uploaded_at;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await evidence_files.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await evidence_files.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.uploaded_by !== undefined) {
|
||||
await evidence_files.setUploaded_by(
|
||||
data.uploaded_by,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await evidence_files.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return evidence_files;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const evidence_files = await db.evidence_files.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of evidence_files) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of evidence_files) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return evidence_files;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const evidence_files = await db.evidence_files.findByPk(id, options);
|
||||
|
||||
await evidence_files.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await evidence_files.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return evidence_files;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const evidence_files = await db.evidence_files.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!evidence_files) {
|
||||
return evidence_files;
|
||||
}
|
||||
|
||||
const output = evidence_files.get({ plain: true });
|
||||
|
||||
output.metric_values_source_ref =
|
||||
await evidence_files.getMetric_values_source_ref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.narratives_source_ref =
|
||||
await evidence_files.getNarratives_source_ref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.project = await evidence_files.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.uploaded_by = await evidence_files.getUploaded_by({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await evidence_files.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.users,
|
||||
as: 'uploaded_by',
|
||||
|
||||
where: filter.uploaded_by
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.uploaded_by
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
firstName: {
|
||||
[Op.or]: filter.uploaded_by
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.file_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'evidence_files',
|
||||
'file_name',
|
||||
filter.file_name,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.mime_type) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'evidence_files',
|
||||
'mime_type',
|
||||
filter.mime_type,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.gcs_uri) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('evidence_files', 'gcs_uri', filter.gcs_uri),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.checksum) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('evidence_files', 'checksum', filter.checksum),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.uploaded_atRange) {
|
||||
const [start, end] = filter.uploaded_atRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
uploaded_at: {
|
||||
...where.uploaded_at,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
uploaded_at: {
|
||||
...where.uploaded_at,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.evidence_files.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('evidence_files', 'file_name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.evidence_files.findAll({
|
||||
attributes: ['id', 'file_name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['file_name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.file_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,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
447
backend/src/db/api/jobs.js
Normal file
447
backend/src/db/api/jobs.js
Normal file
@ -0,0 +1,447 @@
|
||||
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 JobsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const jobs = await db.jobs.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
type: data.type || null,
|
||||
status: data.status || null,
|
||||
payload: data.payload || null,
|
||||
result: data.result || null,
|
||||
started_at: data.started_at || null,
|
||||
finished_at: data.finished_at || null,
|
||||
error_text: data.error_text || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await jobs.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await jobs.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
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 jobsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
type: item.type || null,
|
||||
status: item.status || null,
|
||||
payload: item.payload || null,
|
||||
result: item.result || null,
|
||||
started_at: item.started_at || null,
|
||||
finished_at: item.finished_at || null,
|
||||
error_text: item.error_text || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const jobs = await db.jobs.bulkCreate(jobsData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
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 jobs = await db.jobs.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.type !== undefined) updatePayload.type = data.type;
|
||||
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
if (data.payload !== undefined) updatePayload.payload = data.payload;
|
||||
|
||||
if (data.result !== undefined) updatePayload.result = data.result;
|
||||
|
||||
if (data.started_at !== undefined)
|
||||
updatePayload.started_at = data.started_at;
|
||||
|
||||
if (data.finished_at !== undefined)
|
||||
updatePayload.finished_at = data.finished_at;
|
||||
|
||||
if (data.error_text !== undefined)
|
||||
updatePayload.error_text = data.error_text;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await jobs.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await jobs.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await jobs.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const jobs = await db.jobs.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of jobs) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of jobs) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const jobs = await db.jobs.findByPk(id, options);
|
||||
|
||||
await jobs.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await jobs.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const jobs = await db.jobs.findOne({ where }, { transaction });
|
||||
|
||||
if (!jobs) {
|
||||
return jobs;
|
||||
}
|
||||
|
||||
const output = jobs.get({ plain: true });
|
||||
|
||||
output.project = await jobs.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await jobs.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.status) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('jobs', 'status', filter.status),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.payload) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('jobs', 'payload', filter.payload),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.result) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('jobs', 'result', filter.result),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.error_text) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('jobs', 'error_text', filter.error_text),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.started_atRange) {
|
||||
const [start, end] = filter.started_atRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
started_at: {
|
||||
...where.started_at,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
started_at: {
|
||||
...where.started_at,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.finished_atRange) {
|
||||
const [start, end] = filter.finished_atRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
finished_at: {
|
||||
...where.finished_at,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
finished_at: {
|
||||
...where.finished_at,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.type) {
|
||||
where = {
|
||||
...where,
|
||||
type: filter.type,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.jobs.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('jobs', 'type', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.jobs.findAll({
|
||||
attributes: ['id', 'type'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['type', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.type,
|
||||
}));
|
||||
}
|
||||
};
|
||||
501
backend/src/db/api/mappings.js
Normal file
501
backend/src/db/api/mappings.js
Normal file
@ -0,0 +1,501 @@
|
||||
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 MappingsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const mappings = await db.mappings.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
source_type: data.source_type || null,
|
||||
source_path: data.source_path || null,
|
||||
target_concept_qname: data.target_concept_qname || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await mappings.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await mappings.setDefault_unit(data.default_unit || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await mappings.setDefault_context(data.default_context || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await mappings.setLast_mapped_by(data.last_mapped_by || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await mappings.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
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 mappingsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
source_type: item.source_type || null,
|
||||
source_path: item.source_path || null,
|
||||
target_concept_qname: item.target_concept_qname || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const mappings = await db.mappings.bulkCreate(mappingsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
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 mappings = await db.mappings.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.source_type !== undefined)
|
||||
updatePayload.source_type = data.source_type;
|
||||
|
||||
if (data.source_path !== undefined)
|
||||
updatePayload.source_path = data.source_path;
|
||||
|
||||
if (data.target_concept_qname !== undefined)
|
||||
updatePayload.target_concept_qname = data.target_concept_qname;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await mappings.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await mappings.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.default_unit !== undefined) {
|
||||
await mappings.setDefault_unit(
|
||||
data.default_unit,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.default_context !== undefined) {
|
||||
await mappings.setDefault_context(
|
||||
data.default_context,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.last_mapped_by !== undefined) {
|
||||
await mappings.setLast_mapped_by(
|
||||
data.last_mapped_by,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await mappings.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const mappings = await db.mappings.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of mappings) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of mappings) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const mappings = await db.mappings.findByPk(id, options);
|
||||
|
||||
await mappings.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await mappings.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const mappings = await db.mappings.findOne({ where }, { transaction });
|
||||
|
||||
if (!mappings) {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
const output = mappings.get({ plain: true });
|
||||
|
||||
output.project = await mappings.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.default_unit = await mappings.getDefault_unit({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.default_context = await mappings.getDefault_context({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.last_mapped_by = await mappings.getLast_mapped_by({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await mappings.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.units,
|
||||
as: 'default_unit',
|
||||
|
||||
where: filter.default_unit
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.default_unit
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
unit_id: {
|
||||
[Op.or]: filter.default_unit
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.contexts,
|
||||
as: 'default_context',
|
||||
|
||||
where: filter.default_context
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.default_context
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
identifier_value: {
|
||||
[Op.or]: filter.default_context
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.users,
|
||||
as: 'last_mapped_by',
|
||||
|
||||
where: filter.last_mapped_by
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.last_mapped_by
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
firstName: {
|
||||
[Op.or]: filter.last_mapped_by
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.source_path) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('mappings', 'source_path', filter.source_path),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.target_concept_qname) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'mappings',
|
||||
'target_concept_qname',
|
||||
filter.target_concept_qname,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.source_type) {
|
||||
where = {
|
||||
...where,
|
||||
source_type: filter.source_type,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.mappings.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('mappings', 'source_path', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.mappings.findAll({
|
||||
attributes: ['id', 'source_path'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['source_path', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.source_path,
|
||||
}));
|
||||
}
|
||||
};
|
||||
570
backend/src/db/api/metric_values.js
Normal file
570
backend/src/db/api/metric_values.js
Normal file
@ -0,0 +1,570 @@
|
||||
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 Metric_valuesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const metric_values = await db.metric_values.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
concept_qname: data.concept_qname || null,
|
||||
value_numeric: data.value_numeric || null,
|
||||
value_text: data.value_text || null,
|
||||
decimals: data.decimals || null,
|
||||
note: data.note || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await metric_values.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await metric_values.setContext(data.context || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await metric_values.setUnit(data.unit || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await metric_values.setSource_ref(data.source_ref || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await metric_values.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return metric_values;
|
||||
}
|
||||
|
||||
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 metric_valuesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
concept_qname: item.concept_qname || null,
|
||||
value_numeric: item.value_numeric || null,
|
||||
value_text: item.value_text || null,
|
||||
decimals: item.decimals || null,
|
||||
note: item.note || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const metric_values = await db.metric_values.bulkCreate(metric_valuesData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return metric_values;
|
||||
}
|
||||
|
||||
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 metric_values = await db.metric_values.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.concept_qname !== undefined)
|
||||
updatePayload.concept_qname = data.concept_qname;
|
||||
|
||||
if (data.value_numeric !== undefined)
|
||||
updatePayload.value_numeric = data.value_numeric;
|
||||
|
||||
if (data.value_text !== undefined)
|
||||
updatePayload.value_text = data.value_text;
|
||||
|
||||
if (data.decimals !== undefined) updatePayload.decimals = data.decimals;
|
||||
|
||||
if (data.note !== undefined) updatePayload.note = data.note;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await metric_values.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await metric_values.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.context !== undefined) {
|
||||
await metric_values.setContext(
|
||||
data.context,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.unit !== undefined) {
|
||||
await metric_values.setUnit(
|
||||
data.unit,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.source_ref !== undefined) {
|
||||
await metric_values.setSource_ref(
|
||||
data.source_ref,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await metric_values.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return metric_values;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const metric_values = await db.metric_values.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of metric_values) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of metric_values) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return metric_values;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const metric_values = await db.metric_values.findByPk(id, options);
|
||||
|
||||
await metric_values.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await metric_values.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return metric_values;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const metric_values = await db.metric_values.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!metric_values) {
|
||||
return metric_values;
|
||||
}
|
||||
|
||||
const output = metric_values.get({ plain: true });
|
||||
|
||||
output.project = await metric_values.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.context = await metric_values.getContext({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.unit = await metric_values.getUnit({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.source_ref = await metric_values.getSource_ref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await metric_values.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.contexts,
|
||||
as: 'context',
|
||||
|
||||
where: filter.context
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.context
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
identifier_value: {
|
||||
[Op.or]: filter.context
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.units,
|
||||
as: 'unit',
|
||||
|
||||
where: filter.unit
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.unit
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
unit_id: {
|
||||
[Op.or]: filter.unit
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.evidence_files,
|
||||
as: 'source_ref',
|
||||
|
||||
where: filter.source_ref
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.source_ref
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
file_name: {
|
||||
[Op.or]: filter.source_ref
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.concept_qname) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'metric_values',
|
||||
'concept_qname',
|
||||
filter.concept_qname,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.value_text) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'metric_values',
|
||||
'value_text',
|
||||
filter.value_text,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.note) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('metric_values', 'note', filter.note),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.value_numericRange) {
|
||||
const [start, end] = filter.value_numericRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
value_numeric: {
|
||||
...where.value_numeric,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
value_numeric: {
|
||||
...where.value_numeric,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.decimalsRange) {
|
||||
const [start, end] = filter.decimalsRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
decimals: {
|
||||
...where.decimals,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
decimals: {
|
||||
...where.decimals,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.metric_values.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('metric_values', 'concept_qname', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.metric_values.findAll({
|
||||
attributes: ['id', 'concept_qname'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['concept_qname', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.concept_qname,
|
||||
}));
|
||||
}
|
||||
};
|
||||
458
backend/src/db/api/narratives.js
Normal file
458
backend/src/db/api/narratives.js
Normal file
@ -0,0 +1,458 @@
|
||||
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 NarrativesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const narratives = await db.narratives.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
concept_qname: data.concept_qname || null,
|
||||
value_text: data.value_text || null,
|
||||
note: data.note || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await narratives.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await narratives.setContext(data.context || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await narratives.setSource_ref(data.source_ref || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await narratives.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return narratives;
|
||||
}
|
||||
|
||||
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 narrativesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
concept_qname: item.concept_qname || null,
|
||||
value_text: item.value_text || null,
|
||||
note: item.note || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const narratives = await db.narratives.bulkCreate(narrativesData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return narratives;
|
||||
}
|
||||
|
||||
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 narratives = await db.narratives.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.concept_qname !== undefined)
|
||||
updatePayload.concept_qname = data.concept_qname;
|
||||
|
||||
if (data.value_text !== undefined)
|
||||
updatePayload.value_text = data.value_text;
|
||||
|
||||
if (data.note !== undefined) updatePayload.note = data.note;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await narratives.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await narratives.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.context !== undefined) {
|
||||
await narratives.setContext(
|
||||
data.context,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.source_ref !== undefined) {
|
||||
await narratives.setSource_ref(
|
||||
data.source_ref,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await narratives.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return narratives;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const narratives = await db.narratives.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of narratives) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of narratives) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return narratives;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const narratives = await db.narratives.findByPk(id, options);
|
||||
|
||||
await narratives.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await narratives.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return narratives;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const narratives = await db.narratives.findOne({ where }, { transaction });
|
||||
|
||||
if (!narratives) {
|
||||
return narratives;
|
||||
}
|
||||
|
||||
const output = narratives.get({ plain: true });
|
||||
|
||||
output.project = await narratives.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.context = await narratives.getContext({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.source_ref = await narratives.getSource_ref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await narratives.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.contexts,
|
||||
as: 'context',
|
||||
|
||||
where: filter.context
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.context
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
identifier_value: {
|
||||
[Op.or]: filter.context
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.evidence_files,
|
||||
as: 'source_ref',
|
||||
|
||||
where: filter.source_ref
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.source_ref
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
file_name: {
|
||||
[Op.or]: filter.source_ref
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.concept_qname) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'narratives',
|
||||
'concept_qname',
|
||||
filter.concept_qname,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.value_text) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('narratives', 'value_text', filter.value_text),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.note) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('narratives', 'note', filter.note),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.narratives.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('narratives', 'concept_qname', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.narratives.findAll({
|
||||
attributes: ['id', 'concept_qname'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['concept_qname', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.concept_qname,
|
||||
}));
|
||||
}
|
||||
};
|
||||
389
backend/src/db/api/org_users.js
Normal file
389
backend/src/db/api/org_users.js
Normal file
@ -0,0 +1,389 @@
|
||||
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 Org_usersDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const org_users = await db.org_users.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
role: data.role || null,
|
||||
status: data.status || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await org_users.setOrg(data.org || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await org_users.setUser(data.user || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await org_users.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return org_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 org_usersData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
role: item.role || null,
|
||||
status: item.status || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const org_users = await db.org_users.bulkCreate(org_usersData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return org_users;
|
||||
}
|
||||
|
||||
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 org_users = await db.org_users.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.role !== undefined) updatePayload.role = data.role;
|
||||
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await org_users.update(updatePayload, { transaction });
|
||||
|
||||
if (data.org !== undefined) {
|
||||
await org_users.setOrg(
|
||||
data.org,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.user !== undefined) {
|
||||
await org_users.setUser(
|
||||
data.user,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await org_users.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return org_users;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const org_users = await db.org_users.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of org_users) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of org_users) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return org_users;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const org_users = await db.org_users.findByPk(id, options);
|
||||
|
||||
await org_users.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await org_users.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return org_users;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const org_users = await db.org_users.findOne({ where }, { transaction });
|
||||
|
||||
if (!org_users) {
|
||||
return org_users;
|
||||
}
|
||||
|
||||
const output = org_users.get({ plain: true });
|
||||
|
||||
output.org = await org_users.getOrg({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.user = await org_users.getUser({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await org_users.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'org',
|
||||
},
|
||||
|
||||
{
|
||||
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.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.status) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('org_users', 'status', filter.status),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.role) {
|
||||
where = {
|
||||
...where,
|
||||
role: filter.role,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.org) {
|
||||
const listItems = filter.org.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
orgId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.org_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('org_users', 'role', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.org_users.findAll({
|
||||
attributes: ['id', 'role'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['role', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.role,
|
||||
}));
|
||||
}
|
||||
};
|
||||
365
backend/src/db/api/organizations.js
Normal file
365
backend/src/db/api/organizations.js
Normal file
@ -0,0 +1,365 @@
|
||||
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 OrganizationsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const organizations = await db.organizations.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
return organizations;
|
||||
}
|
||||
|
||||
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 organizationsData = 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 organizations = await db.organizations.bulkCreate(organizationsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return organizations;
|
||||
}
|
||||
|
||||
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 organizations = await db.organizations.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await organizations.update(updatePayload, { transaction });
|
||||
|
||||
return organizations;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const organizations = await db.organizations.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of organizations) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of organizations) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return organizations;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const organizations = await db.organizations.findByPk(id, options);
|
||||
|
||||
await organizations.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await organizations.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return organizations;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const organizations = await db.organizations.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!organizations) {
|
||||
return organizations;
|
||||
}
|
||||
|
||||
const output = organizations.get({ plain: true });
|
||||
|
||||
output.users_organizations = await organizations.getUsers_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.audit_logs_org = await organizations.getAudit_logs_org({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.audit_logs_organizations =
|
||||
await organizations.getAudit_logs_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.audit_packs_organizations =
|
||||
await organizations.getAudit_packs_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.contexts_organizations =
|
||||
await organizations.getContexts_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.evidence_files_organizations =
|
||||
await organizations.getEvidence_files_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.jobs_organizations = await organizations.getJobs_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.mappings_organizations =
|
||||
await organizations.getMappings_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.metric_values_organizations =
|
||||
await organizations.getMetric_values_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.narratives_organizations =
|
||||
await organizations.getNarratives_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.org_users_org = await organizations.getOrg_users_org({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.org_users_organizations =
|
||||
await organizations.getOrg_users_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.projects_org = await organizations.getProjects_org({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.projects_organizations =
|
||||
await organizations.getProjects_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.taxonomies_organizations =
|
||||
await organizations.getTaxonomies_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.units_organizations = await organizations.getUnits_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.validation_runs_organizations =
|
||||
await organizations.getValidation_runs_organizations({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.xbrl_instances_organizations =
|
||||
await organizations.getXbrl_instances_organizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
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('organizations', '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.organizationsId;
|
||||
}
|
||||
|
||||
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.organizations.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('organizations', 'name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.organizations.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,
|
||||
}));
|
||||
}
|
||||
};
|
||||
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 userOrganizations = (user && user.organizations?.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,
|
||||
}));
|
||||
}
|
||||
};
|
||||
471
backend/src/db/api/projects.js
Normal file
471
backend/src/db/api/projects.js
Normal file
@ -0,0 +1,471 @@
|
||||
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 ProjectsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const projects = await db.projects.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
fiscal_year_start: data.fiscal_year_start || null,
|
||||
fiscal_year_end: data.fiscal_year_end || null,
|
||||
reporting_scope: data.reporting_scope || null,
|
||||
status: data.status || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await projects.setOrg(data.org || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await projects.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
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 projectsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name || null,
|
||||
fiscal_year_start: item.fiscal_year_start || null,
|
||||
fiscal_year_end: item.fiscal_year_end || null,
|
||||
reporting_scope: item.reporting_scope || null,
|
||||
status: item.status || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const projects = await db.projects.bulkCreate(projectsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
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 projects = await db.projects.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
if (data.fiscal_year_start !== undefined)
|
||||
updatePayload.fiscal_year_start = data.fiscal_year_start;
|
||||
|
||||
if (data.fiscal_year_end !== undefined)
|
||||
updatePayload.fiscal_year_end = data.fiscal_year_end;
|
||||
|
||||
if (data.reporting_scope !== undefined)
|
||||
updatePayload.reporting_scope = data.reporting_scope;
|
||||
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await projects.update(updatePayload, { transaction });
|
||||
|
||||
if (data.org !== undefined) {
|
||||
await projects.setOrg(
|
||||
data.org,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await projects.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const projects = await db.projects.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of projects) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of projects) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const projects = await db.projects.findByPk(id, options);
|
||||
|
||||
await projects.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await projects.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const projects = await db.projects.findOne({ where }, { transaction });
|
||||
|
||||
if (!projects) {
|
||||
return projects;
|
||||
}
|
||||
|
||||
const output = projects.get({ plain: true });
|
||||
|
||||
output.audit_packs_project = await projects.getAudit_packs_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.contexts_project = await projects.getContexts_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.evidence_files_project = await projects.getEvidence_files_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.jobs_project = await projects.getJobs_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.mappings_project = await projects.getMappings_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.metric_values_project = await projects.getMetric_values_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.narratives_project = await projects.getNarratives_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.units_project = await projects.getUnits_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.xbrl_instances_project = await projects.getXbrl_instances_project({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.org = await projects.getOrg({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await projects.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'org',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('projects', 'name', filter.name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.status) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('projects', 'status', filter.status),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{
|
||||
fiscal_year_start: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
{
|
||||
fiscal_year_end: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.fiscal_year_startRange) {
|
||||
const [start, end] = filter.fiscal_year_startRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
fiscal_year_start: {
|
||||
...where.fiscal_year_start,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
fiscal_year_start: {
|
||||
...where.fiscal_year_start,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.fiscal_year_endRange) {
|
||||
const [start, end] = filter.fiscal_year_endRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
fiscal_year_end: {
|
||||
...where.fiscal_year_end,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
fiscal_year_end: {
|
||||
...where.fiscal_year_end,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.reporting_scope) {
|
||||
where = {
|
||||
...where,
|
||||
reporting_scope: filter.reporting_scope,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.org) {
|
||||
const listItems = filter.org.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
orgId: { [Op.or]: listItems },
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.projects.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('projects', 'name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.projects.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,
|
||||
}));
|
||||
}
|
||||
};
|
||||
344
backend/src/db/api/roles.js
Normal file
344
backend/src/db/api/roles.js
Normal file
@ -0,0 +1,344 @@
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.permissions,
|
||||
as: 'permissions',
|
||||
required: false,
|
||||
},
|
||||
];
|
||||
|
||||
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,
|
||||
}));
|
||||
}
|
||||
};
|
||||
347
backend/src/db/api/taxonomies.js
Normal file
347
backend/src/db/api/taxonomies.js
Normal file
@ -0,0 +1,347 @@
|
||||
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 TaxonomiesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const taxonomies = await db.taxonomies.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
code: data.code || null,
|
||||
label: data.label || null,
|
||||
version: data.version || null,
|
||||
entry_point_url: data.entry_point_url || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await taxonomies.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return taxonomies;
|
||||
}
|
||||
|
||||
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 taxonomiesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
code: item.code || null,
|
||||
label: item.label || null,
|
||||
version: item.version || null,
|
||||
entry_point_url: item.entry_point_url || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const taxonomies = await db.taxonomies.bulkCreate(taxonomiesData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return taxonomies;
|
||||
}
|
||||
|
||||
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 taxonomies = await db.taxonomies.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.code !== undefined) updatePayload.code = data.code;
|
||||
|
||||
if (data.label !== undefined) updatePayload.label = data.label;
|
||||
|
||||
if (data.version !== undefined) updatePayload.version = data.version;
|
||||
|
||||
if (data.entry_point_url !== undefined)
|
||||
updatePayload.entry_point_url = data.entry_point_url;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await taxonomies.update(updatePayload, { transaction });
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await taxonomies.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return taxonomies;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const taxonomies = await db.taxonomies.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of taxonomies) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of taxonomies) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return taxonomies;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const taxonomies = await db.taxonomies.findByPk(id, options);
|
||||
|
||||
await taxonomies.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await taxonomies.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return taxonomies;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const taxonomies = await db.taxonomies.findOne({ where }, { transaction });
|
||||
|
||||
if (!taxonomies) {
|
||||
return taxonomies;
|
||||
}
|
||||
|
||||
const output = taxonomies.get({ plain: true });
|
||||
|
||||
output.xbrl_instances_taxonomy =
|
||||
await taxonomies.getXbrl_instances_taxonomy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await taxonomies.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.code) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('taxonomies', 'code', filter.code),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.label) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('taxonomies', 'label', filter.label),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.version) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('taxonomies', 'version', filter.version),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.entry_point_url) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'taxonomies',
|
||||
'entry_point_url',
|
||||
filter.entry_point_url,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.taxonomies.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('taxonomies', 'label', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.taxonomies.findAll({
|
||||
attributes: ['id', 'label'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['label', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.label,
|
||||
}));
|
||||
}
|
||||
};
|
||||
375
backend/src/db/api/units.js
Normal file
375
backend/src/db/api/units.js
Normal file
@ -0,0 +1,375 @@
|
||||
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 UnitsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const units = await db.units.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
unit_id: data.unit_id || null,
|
||||
numerator: data.numerator || null,
|
||||
denominator: data.denominator || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await units.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await units.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
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 unitsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
unit_id: item.unit_id || null,
|
||||
numerator: item.numerator || null,
|
||||
denominator: item.denominator || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const units = await db.units.bulkCreate(unitsData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
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 units = await db.units.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.unit_id !== undefined) updatePayload.unit_id = data.unit_id;
|
||||
|
||||
if (data.numerator !== undefined) updatePayload.numerator = data.numerator;
|
||||
|
||||
if (data.denominator !== undefined)
|
||||
updatePayload.denominator = data.denominator;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await units.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await units.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await units.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const units = await db.units.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of units) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of units) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const units = await db.units.findByPk(id, options);
|
||||
|
||||
await units.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await units.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const units = await db.units.findOne({ where }, { transaction });
|
||||
|
||||
if (!units) {
|
||||
return units;
|
||||
}
|
||||
|
||||
const output = units.get({ plain: true });
|
||||
|
||||
output.mappings_default_unit = await units.getMappings_default_unit({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.metric_values_unit = await units.getMetric_values_unit({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.project = await units.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await units.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.unit_id) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('units', 'unit_id', filter.unit_id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.numerator) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('units', 'numerator', filter.numerator),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.denominator) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('units', 'denominator', filter.denominator),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.units.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('units', 'unit_id', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.units.findAll({
|
||||
attributes: ['id', 'unit_id'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['unit_id', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.unit_id,
|
||||
}));
|
||||
}
|
||||
};
|
||||
817
backend/src/db/api/users.js
Normal file
817
backend/src/db/api/users.js
Normal file
@ -0,0 +1,817 @@
|
||||
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.setOrganizations(data.data.organizations || 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.organizations !== undefined) {
|
||||
await users.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ 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.audit_logs_actor_user = await users.getAudit_logs_actor_user({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.evidence_files_uploaded_by =
|
||||
await users.getEvidence_files_uploaded_by({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.mappings_last_mapped_by = await users.getMappings_last_mapped_by({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.org_users_user = await users.getOrg_users_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.organizations = await users.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
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.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
|
||||
{
|
||||
model: db.permissions,
|
||||
as: 'custom_permissions',
|
||||
required: false,
|
||||
},
|
||||
|
||||
{
|
||||
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.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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: config.roles?.user || '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;
|
||||
}
|
||||
};
|
||||
517
backend/src/db/api/validation_runs.js
Normal file
517
backend/src/db/api/validation_runs.js
Normal file
@ -0,0 +1,517 @@
|
||||
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 Validation_runsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const validation_runs = await db.validation_runs.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
status: data.status || null,
|
||||
started_at: data.started_at || null,
|
||||
finished_at: data.finished_at || null,
|
||||
engine: data.engine || null,
|
||||
error_count: data.error_count || null,
|
||||
warning_count: data.warning_count || null,
|
||||
report_json: data.report_json || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await validation_runs.setXbrl_instance(data.xbrl_instance || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await validation_runs.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return validation_runs;
|
||||
}
|
||||
|
||||
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 validation_runsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
status: item.status || null,
|
||||
started_at: item.started_at || null,
|
||||
finished_at: item.finished_at || null,
|
||||
engine: item.engine || null,
|
||||
error_count: item.error_count || null,
|
||||
warning_count: item.warning_count || null,
|
||||
report_json: item.report_json || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const validation_runs = await db.validation_runs.bulkCreate(
|
||||
validation_runsData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return validation_runs;
|
||||
}
|
||||
|
||||
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 validation_runs = await db.validation_runs.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
if (data.started_at !== undefined)
|
||||
updatePayload.started_at = data.started_at;
|
||||
|
||||
if (data.finished_at !== undefined)
|
||||
updatePayload.finished_at = data.finished_at;
|
||||
|
||||
if (data.engine !== undefined) updatePayload.engine = data.engine;
|
||||
|
||||
if (data.error_count !== undefined)
|
||||
updatePayload.error_count = data.error_count;
|
||||
|
||||
if (data.warning_count !== undefined)
|
||||
updatePayload.warning_count = data.warning_count;
|
||||
|
||||
if (data.report_json !== undefined)
|
||||
updatePayload.report_json = data.report_json;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await validation_runs.update(updatePayload, { transaction });
|
||||
|
||||
if (data.xbrl_instance !== undefined) {
|
||||
await validation_runs.setXbrl_instance(
|
||||
data.xbrl_instance,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await validation_runs.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return validation_runs;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const validation_runs = await db.validation_runs.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of validation_runs) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of validation_runs) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return validation_runs;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const validation_runs = await db.validation_runs.findByPk(id, options);
|
||||
|
||||
await validation_runs.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await validation_runs.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return validation_runs;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const validation_runs = await db.validation_runs.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!validation_runs) {
|
||||
return validation_runs;
|
||||
}
|
||||
|
||||
const output = validation_runs.get({ plain: true });
|
||||
|
||||
output.xbrl_instance = await validation_runs.getXbrl_instance({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await validation_runs.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.xbrl_instances,
|
||||
as: 'xbrl_instance',
|
||||
|
||||
where: filter.xbrl_instance
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.xbrl_instance
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
build_status: {
|
||||
[Op.or]: filter.xbrl_instance
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.status) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('validation_runs', 'status', filter.status),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.engine) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('validation_runs', 'engine', filter.engine),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.report_json) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'validation_runs',
|
||||
'report_json',
|
||||
filter.report_json,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{
|
||||
started_at: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
{
|
||||
finished_at: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.started_atRange) {
|
||||
const [start, end] = filter.started_atRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
started_at: {
|
||||
...where.started_at,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
started_at: {
|
||||
...where.started_at,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.finished_atRange) {
|
||||
const [start, end] = filter.finished_atRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
finished_at: {
|
||||
...where.finished_at,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
finished_at: {
|
||||
...where.finished_at,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.error_countRange) {
|
||||
const [start, end] = filter.error_countRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
error_count: {
|
||||
...where.error_count,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
error_count: {
|
||||
...where.error_count,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.warning_countRange) {
|
||||
const [start, end] = filter.warning_countRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
warning_count: {
|
||||
...where.warning_count,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
warning_count: {
|
||||
...where.warning_count,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.validation_runs.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('validation_runs', 'status', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.validation_runs.findAll({
|
||||
attributes: ['id', 'status'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['status', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.status,
|
||||
}));
|
||||
}
|
||||
};
|
||||
455
backend/src/db/api/xbrl_instances.js
Normal file
455
backend/src/db/api/xbrl_instances.js
Normal file
@ -0,0 +1,455 @@
|
||||
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 Xbrl_instancesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const xbrl_instances = await db.xbrl_instances.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
build_status: data.build_status || null,
|
||||
build_log: data.build_log || null,
|
||||
instance_uri: data.instance_uri || null,
|
||||
ixbrl_uri: data.ixbrl_uri || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await xbrl_instances.setProject(data.project || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await xbrl_instances.setTaxonomy(data.taxonomy || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await xbrl_instances.setOrganizations(data.organizations || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return xbrl_instances;
|
||||
}
|
||||
|
||||
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 xbrl_instancesData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
build_status: item.build_status || null,
|
||||
build_log: item.build_log || null,
|
||||
instance_uri: item.instance_uri || null,
|
||||
ixbrl_uri: item.ixbrl_uri || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const xbrl_instances = await db.xbrl_instances.bulkCreate(
|
||||
xbrl_instancesData,
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return xbrl_instances;
|
||||
}
|
||||
|
||||
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 xbrl_instances = await db.xbrl_instances.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.build_status !== undefined)
|
||||
updatePayload.build_status = data.build_status;
|
||||
|
||||
if (data.build_log !== undefined) updatePayload.build_log = data.build_log;
|
||||
|
||||
if (data.instance_uri !== undefined)
|
||||
updatePayload.instance_uri = data.instance_uri;
|
||||
|
||||
if (data.ixbrl_uri !== undefined) updatePayload.ixbrl_uri = data.ixbrl_uri;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await xbrl_instances.update(updatePayload, { transaction });
|
||||
|
||||
if (data.project !== undefined) {
|
||||
await xbrl_instances.setProject(
|
||||
data.project,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.taxonomy !== undefined) {
|
||||
await xbrl_instances.setTaxonomy(
|
||||
data.taxonomy,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.organizations !== undefined) {
|
||||
await xbrl_instances.setOrganizations(
|
||||
data.organizations,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return xbrl_instances;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const xbrl_instances = await db.xbrl_instances.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of xbrl_instances) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of xbrl_instances) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return xbrl_instances;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const xbrl_instances = await db.xbrl_instances.findByPk(id, options);
|
||||
|
||||
await xbrl_instances.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await xbrl_instances.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return xbrl_instances;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const xbrl_instances = await db.xbrl_instances.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!xbrl_instances) {
|
||||
return xbrl_instances;
|
||||
}
|
||||
|
||||
const output = xbrl_instances.get({ plain: true });
|
||||
|
||||
output.audit_packs_xbrl_instance =
|
||||
await xbrl_instances.getAudit_packs_xbrl_instance({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.validation_runs_xbrl_instance =
|
||||
await xbrl_instances.getValidation_runs_xbrl_instance({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.project = await xbrl_instances.getProject({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.taxonomy = await xbrl_instances.getTaxonomy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.organizations = await xbrl_instances.getOrganizations({
|
||||
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 userOrganizations = (user && user.organizations?.id) || null;
|
||||
|
||||
if (userOrganizations) {
|
||||
if (options?.currentUser?.organizationsId) {
|
||||
where.organizationsId = options.currentUser.organizationsId;
|
||||
}
|
||||
}
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.projects,
|
||||
as: 'project',
|
||||
|
||||
where: filter.project
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.project
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.project
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.taxonomies,
|
||||
as: 'taxonomy',
|
||||
|
||||
where: filter.taxonomy
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.taxonomy
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
[Op.or]: filter.taxonomy
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.organizations,
|
||||
as: 'organizations',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.build_log) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'xbrl_instances',
|
||||
'build_log',
|
||||
filter.build_log,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.instance_uri) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'xbrl_instances',
|
||||
'instance_uri',
|
||||
filter.instance_uri,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.ixbrl_uri) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'xbrl_instances',
|
||||
'ixbrl_uri',
|
||||
filter.ixbrl_uri,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.build_status) {
|
||||
where = {
|
||||
...where,
|
||||
build_status: filter.build_status,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.organizations) {
|
||||
const listItems = filter.organizations.split('|').map((item) => {
|
||||
return Utils.uuid(item);
|
||||
});
|
||||
|
||||
where = {
|
||||
...where,
|
||||
organizationsId: { [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.organizationsId;
|
||||
}
|
||||
|
||||
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.xbrl_instances.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('xbrl_instances', 'build_status', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.xbrl_instances.findAll({
|
||||
attributes: ['id', 'build_status'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['build_status', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.build_status,
|
||||
}));
|
||||
}
|
||||
};
|
||||
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_varmennappi',
|
||||
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',
|
||||
},
|
||||
};
|
||||
2393
backend/src/db/migrations/1757280210408.js
Normal file
2393
backend/src/db/migrations/1757280210408.js
Normal file
File diff suppressed because it is too large
Load Diff
85
backend/src/db/models/audit_logs.js
Normal file
85
backend/src/db/models/audit_logs.js
Normal file
@ -0,0 +1,85 @@
|
||||
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 audit_logs = sequelize.define(
|
||||
'audit_logs',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
action: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
entity_type: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
entity_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
},
|
||||
|
||||
diff_json: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
audit_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.audit_logs.belongsTo(db.organizations, {
|
||||
as: 'org',
|
||||
foreignKey: {
|
||||
name: 'orgId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.audit_logs.belongsTo(db.users, {
|
||||
as: 'actor_user',
|
||||
foreignKey: {
|
||||
name: 'actor_userId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.audit_logs.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.audit_logs.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.audit_logs.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return audit_logs;
|
||||
};
|
||||
73
backend/src/db/models/audit_packs.js
Normal file
73
backend/src/db/models/audit_packs.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 audit_packs = sequelize.define(
|
||||
'audit_packs',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
zip_uri: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
audit_packs.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.audit_packs.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.audit_packs.belongsTo(db.xbrl_instances, {
|
||||
as: 'xbrl_instance',
|
||||
foreignKey: {
|
||||
name: 'xbrl_instanceId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.audit_packs.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.audit_packs.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.audit_packs.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return audit_packs;
|
||||
};
|
||||
109
backend/src/db/models/contexts.js
Normal file
109
backend/src/db/models/contexts.js
Normal file
@ -0,0 +1,109 @@
|
||||
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 contexts = sequelize.define(
|
||||
'contexts',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
identifier_scheme: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
identifier_value: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
period_start: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
period_end: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
scenario: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
segment: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
contexts.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.contexts.hasMany(db.mappings, {
|
||||
as: 'mappings_default_context',
|
||||
foreignKey: {
|
||||
name: 'default_contextId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.contexts.hasMany(db.metric_values, {
|
||||
as: 'metric_values_context',
|
||||
foreignKey: {
|
||||
name: 'contextId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.contexts.hasMany(db.narratives, {
|
||||
as: 'narratives_context',
|
||||
foreignKey: {
|
||||
name: 'contextId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.contexts.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.contexts.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.contexts.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.contexts.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return contexts;
|
||||
};
|
||||
105
backend/src/db/models/evidence_files.js
Normal file
105
backend/src/db/models/evidence_files.js
Normal file
@ -0,0 +1,105 @@
|
||||
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 evidence_files = sequelize.define(
|
||||
'evidence_files',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
file_name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
mime_type: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
gcs_uri: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
checksum: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
uploaded_at: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
evidence_files.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.evidence_files.hasMany(db.metric_values, {
|
||||
as: 'metric_values_source_ref',
|
||||
foreignKey: {
|
||||
name: 'source_refId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.evidence_files.hasMany(db.narratives, {
|
||||
as: 'narratives_source_ref',
|
||||
foreignKey: {
|
||||
name: 'source_refId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.evidence_files.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.evidence_files.belongsTo(db.users, {
|
||||
as: 'uploaded_by',
|
||||
foreignKey: {
|
||||
name: 'uploaded_byId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.evidence_files.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.evidence_files.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.evidence_files.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return evidence_files;
|
||||
};
|
||||
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;
|
||||
91
backend/src/db/models/jobs.js
Normal file
91
backend/src/db/models/jobs.js
Normal file
@ -0,0 +1,91 @@
|
||||
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 jobs = sequelize.define(
|
||||
'jobs',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
type: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
values: ['GENERATE_XBRL', 'VALIDATE_XBRL', 'BUILD_AUDIT_PACK'],
|
||||
},
|
||||
|
||||
status: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
payload: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
result: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
started_at: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
finished_at: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
error_text: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
jobs.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.jobs.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.jobs.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.jobs.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.jobs.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return jobs;
|
||||
};
|
||||
99
backend/src/db/models/mappings.js
Normal file
99
backend/src/db/models/mappings.js
Normal file
@ -0,0 +1,99 @@
|
||||
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 mappings = sequelize.define(
|
||||
'mappings',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
source_type: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
values: ['CSV_FIELD', 'MANUAL'],
|
||||
},
|
||||
|
||||
source_path: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
target_concept_qname: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
mappings.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.mappings.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.mappings.belongsTo(db.units, {
|
||||
as: 'default_unit',
|
||||
foreignKey: {
|
||||
name: 'default_unitId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.mappings.belongsTo(db.contexts, {
|
||||
as: 'default_context',
|
||||
foreignKey: {
|
||||
name: 'default_contextId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.mappings.belongsTo(db.users, {
|
||||
as: 'last_mapped_by',
|
||||
foreignKey: {
|
||||
name: 'last_mapped_byId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.mappings.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.mappings.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.mappings.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return mappings;
|
||||
};
|
||||
105
backend/src/db/models/metric_values.js
Normal file
105
backend/src/db/models/metric_values.js
Normal file
@ -0,0 +1,105 @@
|
||||
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 metric_values = sequelize.define(
|
||||
'metric_values',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
concept_qname: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
value_numeric: {
|
||||
type: DataTypes.DECIMAL,
|
||||
},
|
||||
|
||||
value_text: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
decimals: {
|
||||
type: DataTypes.INTEGER,
|
||||
},
|
||||
|
||||
note: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
metric_values.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.metric_values.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.metric_values.belongsTo(db.contexts, {
|
||||
as: 'context',
|
||||
foreignKey: {
|
||||
name: 'contextId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.metric_values.belongsTo(db.units, {
|
||||
as: 'unit',
|
||||
foreignKey: {
|
||||
name: 'unitId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.metric_values.belongsTo(db.evidence_files, {
|
||||
as: 'source_ref',
|
||||
foreignKey: {
|
||||
name: 'source_refId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.metric_values.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.metric_values.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.metric_values.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return metric_values;
|
||||
};
|
||||
89
backend/src/db/models/narratives.js
Normal file
89
backend/src/db/models/narratives.js
Normal file
@ -0,0 +1,89 @@
|
||||
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 narratives = sequelize.define(
|
||||
'narratives',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
concept_qname: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
value_text: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
note: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
narratives.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.narratives.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.narratives.belongsTo(db.contexts, {
|
||||
as: 'context',
|
||||
foreignKey: {
|
||||
name: 'contextId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.narratives.belongsTo(db.evidence_files, {
|
||||
as: 'source_ref',
|
||||
foreignKey: {
|
||||
name: 'source_refId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.narratives.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.narratives.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.narratives.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return narratives;
|
||||
};
|
||||
89
backend/src/db/models/org_users.js
Normal file
89
backend/src/db/models/org_users.js
Normal file
@ -0,0 +1,89 @@
|
||||
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 org_users = sequelize.define(
|
||||
'org_users',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
role: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
values: [
|
||||
'OrgOwner',
|
||||
|
||||
'Controller',
|
||||
|
||||
'SustainabilityMgr',
|
||||
|
||||
'Auditor',
|
||||
|
||||
'Viewer',
|
||||
],
|
||||
},
|
||||
|
||||
status: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
org_users.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.org_users.belongsTo(db.organizations, {
|
||||
as: 'org',
|
||||
foreignKey: {
|
||||
name: 'orgId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.org_users.belongsTo(db.users, {
|
||||
as: 'user',
|
||||
foreignKey: {
|
||||
name: 'userId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.org_users.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.org_users.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.org_users.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return org_users;
|
||||
};
|
||||
193
backend/src/db/models/organizations.js
Normal file
193
backend/src/db/models/organizations.js
Normal file
@ -0,0 +1,193 @@
|
||||
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 organizations = sequelize.define(
|
||||
'organizations',
|
||||
{
|
||||
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,
|
||||
},
|
||||
);
|
||||
|
||||
organizations.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.organizations.hasMany(db.users, {
|
||||
as: 'users_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.audit_logs, {
|
||||
as: 'audit_logs_org',
|
||||
foreignKey: {
|
||||
name: 'orgId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.audit_logs, {
|
||||
as: 'audit_logs_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.audit_packs, {
|
||||
as: 'audit_packs_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.contexts, {
|
||||
as: 'contexts_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.evidence_files, {
|
||||
as: 'evidence_files_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.jobs, {
|
||||
as: 'jobs_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.mappings, {
|
||||
as: 'mappings_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.metric_values, {
|
||||
as: 'metric_values_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.narratives, {
|
||||
as: 'narratives_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.org_users, {
|
||||
as: 'org_users_org',
|
||||
foreignKey: {
|
||||
name: 'orgId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.org_users, {
|
||||
as: 'org_users_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.projects, {
|
||||
as: 'projects_org',
|
||||
foreignKey: {
|
||||
name: 'orgId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.projects, {
|
||||
as: 'projects_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.taxonomies, {
|
||||
as: 'taxonomies_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.units, {
|
||||
as: 'units_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.validation_runs, {
|
||||
as: 'validation_runs_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.organizations.hasMany(db.xbrl_instances, {
|
||||
as: 'xbrl_instances_organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.organizations.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.organizations.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return organizations;
|
||||
};
|
||||
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;
|
||||
};
|
||||
155
backend/src/db/models/projects.js
Normal file
155
backend/src/db/models/projects.js
Normal file
@ -0,0 +1,155 @@
|
||||
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 projects = sequelize.define(
|
||||
'projects',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
fiscal_year_start: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
fiscal_year_end: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
reporting_scope: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
values: ['ESRS_SET1', 'VSME'],
|
||||
},
|
||||
|
||||
status: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
projects.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.projects.hasMany(db.audit_packs, {
|
||||
as: 'audit_packs_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.contexts, {
|
||||
as: 'contexts_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.evidence_files, {
|
||||
as: 'evidence_files_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.jobs, {
|
||||
as: 'jobs_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.mappings, {
|
||||
as: 'mappings_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.metric_values, {
|
||||
as: 'metric_values_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.narratives, {
|
||||
as: 'narratives_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.units, {
|
||||
as: 'units_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.hasMany(db.xbrl_instances, {
|
||||
as: 'xbrl_instances_project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.projects.belongsTo(db.organizations, {
|
||||
as: 'org',
|
||||
foreignKey: {
|
||||
name: 'orgId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.projects.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.projects.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return projects;
|
||||
};
|
||||
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;
|
||||
};
|
||||
77
backend/src/db/models/taxonomies.js
Normal file
77
backend/src/db/models/taxonomies.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 taxonomies = sequelize.define(
|
||||
'taxonomies',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
code: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
label: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
version: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
entry_point_url: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
taxonomies.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.taxonomies.hasMany(db.xbrl_instances, {
|
||||
as: 'xbrl_instances_taxonomy',
|
||||
foreignKey: {
|
||||
name: 'taxonomyId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.taxonomies.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.taxonomies.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.taxonomies.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return taxonomies;
|
||||
};
|
||||
89
backend/src/db/models/units.js
Normal file
89
backend/src/db/models/units.js
Normal file
@ -0,0 +1,89 @@
|
||||
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 units = sequelize.define(
|
||||
'units',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
unit_id: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
numerator: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
denominator: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
units.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.units.hasMany(db.mappings, {
|
||||
as: 'mappings_default_unit',
|
||||
foreignKey: {
|
||||
name: 'default_unitId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.units.hasMany(db.metric_values, {
|
||||
as: 'metric_values_unit',
|
||||
foreignKey: {
|
||||
name: 'unitId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.units.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.units.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.units.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.units.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return units;
|
||||
};
|
||||
211
backend/src/db/models/users.js
Normal file
211
backend/src/db/models/users.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 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.audit_logs, {
|
||||
as: 'audit_logs_actor_user',
|
||||
foreignKey: {
|
||||
name: 'actor_userId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.users.hasMany(db.evidence_files, {
|
||||
as: 'evidence_files_uploaded_by',
|
||||
foreignKey: {
|
||||
name: 'uploaded_byId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.users.hasMany(db.mappings, {
|
||||
as: 'mappings_last_mapped_by',
|
||||
foreignKey: {
|
||||
name: 'last_mapped_byId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.users.hasMany(db.org_users, {
|
||||
as: 'org_users_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.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
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;
|
||||
}
|
||||
89
backend/src/db/models/validation_runs.js
Normal file
89
backend/src/db/models/validation_runs.js
Normal file
@ -0,0 +1,89 @@
|
||||
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 validation_runs = sequelize.define(
|
||||
'validation_runs',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
status: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
started_at: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
finished_at: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
engine: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
error_count: {
|
||||
type: DataTypes.INTEGER,
|
||||
},
|
||||
|
||||
warning_count: {
|
||||
type: DataTypes.INTEGER,
|
||||
},
|
||||
|
||||
report_json: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
validation_runs.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.validation_runs.belongsTo(db.xbrl_instances, {
|
||||
as: 'xbrl_instance',
|
||||
foreignKey: {
|
||||
name: 'xbrl_instanceId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.validation_runs.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.validation_runs.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.validation_runs.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return validation_runs;
|
||||
};
|
||||
103
backend/src/db/models/xbrl_instances.js
Normal file
103
backend/src/db/models/xbrl_instances.js
Normal file
@ -0,0 +1,103 @@
|
||||
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 xbrl_instances = sequelize.define(
|
||||
'xbrl_instances',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
build_status: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
values: ['QUEUED', 'RUNNING', 'SUCCEEDED', 'FAILED'],
|
||||
},
|
||||
|
||||
build_log: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
instance_uri: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
ixbrl_uri: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
xbrl_instances.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.xbrl_instances.hasMany(db.audit_packs, {
|
||||
as: 'audit_packs_xbrl_instance',
|
||||
foreignKey: {
|
||||
name: 'xbrl_instanceId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.xbrl_instances.hasMany(db.validation_runs, {
|
||||
as: 'validation_runs_xbrl_instance',
|
||||
foreignKey: {
|
||||
name: 'xbrl_instanceId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.xbrl_instances.belongsTo(db.projects, {
|
||||
as: 'project',
|
||||
foreignKey: {
|
||||
name: 'projectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.xbrl_instances.belongsTo(db.taxonomies, {
|
||||
as: 'taxonomy',
|
||||
foreignKey: {
|
||||
name: 'taxonomyId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.xbrl_instances.belongsTo(db.organizations, {
|
||||
as: 'organizations',
|
||||
foreignKey: {
|
||||
name: 'organizationsId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.xbrl_instances.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.xbrl_instances.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return xbrl_instances;
|
||||
};
|
||||
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);
|
||||
});
|
||||
84
backend/src/db/seeders/20200430130759-admin-user.js
Normal file
84
backend/src/db/seeders/20200430130759-admin-user.js
Normal file
@ -0,0 +1,84 @@
|
||||
'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 admin_hash = bcrypt.hashSync(
|
||||
config.admin_pass,
|
||||
config.bcrypt.saltRounds,
|
||||
);
|
||||
let user_hash = bcrypt.hashSync(config.user_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: admin_hash,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: ids[1],
|
||||
firstName: 'John',
|
||||
email: 'john@doe.com',
|
||||
emailVerified: true,
|
||||
provider: config.providers.LOCAL,
|
||||
password: user_hash,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: ids[2],
|
||||
firstName: 'Client',
|
||||
email: 'client@hello.com',
|
||||
emailVerified: true,
|
||||
provider: config.providers.LOCAL,
|
||||
password: user_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: admin_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;
|
||||
}
|
||||
},
|
||||
};
|
||||
2279
backend/src/db/seeders/20200430130760-user-roles.js
Normal file
2279
backend/src/db/seeders/20200430130760-user-roles.js
Normal file
File diff suppressed because it is too large
Load Diff
2346
backend/src/db/seeders/20231127130745-sample-data.js
Normal file
2346
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' });
|
||||
}
|
||||
};
|
||||
267
backend/src/index.js
Normal file
267
backend/src/index.js
Normal file
@ -0,0 +1,267 @@
|
||||
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 audit_logsRoutes = require('./routes/audit_logs');
|
||||
|
||||
const audit_packsRoutes = require('./routes/audit_packs');
|
||||
|
||||
const contextsRoutes = require('./routes/contexts');
|
||||
|
||||
const evidence_filesRoutes = require('./routes/evidence_files');
|
||||
|
||||
const jobsRoutes = require('./routes/jobs');
|
||||
|
||||
const mappingsRoutes = require('./routes/mappings');
|
||||
|
||||
const metric_valuesRoutes = require('./routes/metric_values');
|
||||
|
||||
const narrativesRoutes = require('./routes/narratives');
|
||||
|
||||
const org_usersRoutes = require('./routes/org_users');
|
||||
|
||||
const projectsRoutes = require('./routes/projects');
|
||||
|
||||
const taxonomiesRoutes = require('./routes/taxonomies');
|
||||
|
||||
const unitsRoutes = require('./routes/units');
|
||||
|
||||
const validation_runsRoutes = require('./routes/validation_runs');
|
||||
|
||||
const xbrl_instancesRoutes = require('./routes/xbrl_instances');
|
||||
|
||||
const rolesRoutes = require('./routes/roles');
|
||||
|
||||
const permissionsRoutes = require('./routes/permissions');
|
||||
|
||||
const organizationsRoutes = require('./routes/organizations');
|
||||
|
||||
const getBaseUrl = (url) => {
|
||||
if (!url) return '';
|
||||
return url.endsWith('/api') ? url.slice(0, -4) : url;
|
||||
};
|
||||
|
||||
const options = {
|
||||
definition: {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
version: '1.0.0',
|
||||
title: 'Varmennappi',
|
||||
description:
|
||||
'Varmennappi Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.',
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || 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 =
|
||||
getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || 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/audit_logs',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
audit_logsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/audit_packs',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
audit_packsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/contexts',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
contextsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/evidence_files',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
evidence_filesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/jobs',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
jobsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/mappings',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
mappingsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/metric_values',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
metric_valuesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/narratives',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
narrativesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/org_users',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
org_usersRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/projects',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
projectsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/taxonomies',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
taxonomiesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/units',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
unitsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/validation_runs',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
validation_runsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/xbrl_instances',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
xbrl_instancesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/roles',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
rolesRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/permissions',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
permissionsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/organizations',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
organizationsRoutes,
|
||||
);
|
||||
|
||||
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;
|
||||
176
backend/src/middlewares/check-permissions.js
Normal file
176
backend/src/middlewares/check-permissions.js
Normal file
@ -0,0 +1,176 @@
|
||||
const ValidationError = require('../services/notifications/errors/validation');
|
||||
const RolesDBApi = require('../db/api/roles');
|
||||
|
||||
// Cache for the 'Public' role object
|
||||
let publicRoleCache = null;
|
||||
|
||||
// Function to asynchronously fetch and cache the 'Public' role
|
||||
async function fetchAndCachePublicRole() {
|
||||
try {
|
||||
// Use RolesDBApi to find the role by name 'Public'
|
||||
publicRoleCache = await RolesDBApi.findBy({ name: 'Public' });
|
||||
|
||||
if (!publicRoleCache) {
|
||||
console.error(
|
||||
"WARNING: Role 'Public' not found in database during middleware startup. Check your migrations.",
|
||||
);
|
||||
// The system might not function correctly without this role. May need to throw an error or use a fallback stub.
|
||||
} else {
|
||||
console.log("'Public' role successfully loaded and cached.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error fetching 'Public' role during middleware startup:",
|
||||
error,
|
||||
);
|
||||
// Handle the error during startup fetch
|
||||
throw error; // Important to know if the app can proceed without the Public role
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger the role fetching when the check-permissions.js module is imported/loaded
|
||||
// This should happen during application startup when routes are being configured.
|
||||
fetchAndCachePublicRole().catch((error) => {
|
||||
// Handle the case where the fetchAndCachePublicRole promise is rejected
|
||||
console.error(
|
||||
'Critical error during permissions middleware initialization:',
|
||||
error,
|
||||
);
|
||||
// Decide here if the process should exit if the Public role is essential.
|
||||
// process.exit(1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Middleware creator to check if the current user (or Public role) has a specific permission.
|
||||
* @param {string} permission - The name of the required permission.
|
||||
* @return {import("express").RequestHandler} Express middleware function.
|
||||
*/
|
||||
function checkPermissions(permission) {
|
||||
return async (req, res, next) => {
|
||||
const { currentUser } = req;
|
||||
|
||||
// 1. Check self-access bypass (only if the user is authenticated)
|
||||
if (
|
||||
currentUser &&
|
||||
(currentUser.id === req.params.id || currentUser.id === req.body.id)
|
||||
) {
|
||||
return next(); // User has access to their own resource
|
||||
}
|
||||
|
||||
// 2. Check Custom Permissions (only if the user is authenticated)
|
||||
if (currentUser) {
|
||||
// Ensure custom_permissions is an array before using find
|
||||
const customPermissions = Array.isArray(currentUser.custom_permissions)
|
||||
? currentUser.custom_permissions
|
||||
: [];
|
||||
const userPermission = customPermissions.find(
|
||||
(cp) => cp.name === permission,
|
||||
);
|
||||
if (userPermission) {
|
||||
return next(); // User has a custom permission
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Determine the "effective" role for permission check
|
||||
let effectiveRole = null;
|
||||
try {
|
||||
if (currentUser && currentUser.app_role) {
|
||||
// User is authenticated and has an assigned role
|
||||
effectiveRole = currentUser.app_role;
|
||||
} else {
|
||||
// User is NOT authenticated OR is authenticated but has no role
|
||||
// Use the cached 'Public' role
|
||||
if (!publicRoleCache) {
|
||||
// If the cache is unexpectedly empty (e.g., startup error caught),
|
||||
// we can try fetching the role again synchronously (less ideal) or just deny access.
|
||||
console.error(
|
||||
'Public role cache is empty. Attempting synchronous fetch...',
|
||||
);
|
||||
// Less efficient fallback option:
|
||||
effectiveRole = await RolesDBApi.findBy({ name: 'Public' }); // Could be slow
|
||||
if (!effectiveRole) {
|
||||
// If even the synchronous attempt failed
|
||||
return next(
|
||||
new Error(
|
||||
'Internal Server Error: Public role missing and cannot be fetched.',
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
effectiveRole = publicRoleCache; // Use the cached object
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we got a valid role object
|
||||
if (!effectiveRole) {
|
||||
return next(
|
||||
new Error(
|
||||
'Internal Server Error: Could not determine effective role.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Check Permissions on the "effective" role
|
||||
// Assume the effectiveRole object (from app_role or RolesDBApi) has a getPermissions() method
|
||||
// or a 'permissions' property (if permissions are eagerly loaded).
|
||||
let rolePermissions = [];
|
||||
if (typeof effectiveRole.getPermissions === 'function') {
|
||||
rolePermissions = await effectiveRole.getPermissions(); // Get permissions asynchronously if the method exists
|
||||
} else if (Array.isArray(effectiveRole.permissions)) {
|
||||
rolePermissions = effectiveRole.permissions; // Or take from property if permissions are pre-loaded
|
||||
} else {
|
||||
console.error(
|
||||
'Role object lacks getPermissions() method or permissions property:',
|
||||
effectiveRole,
|
||||
);
|
||||
return next(
|
||||
new Error('Internal Server Error: Invalid role object format.'),
|
||||
);
|
||||
}
|
||||
|
||||
if (rolePermissions.find((p) => p.name === permission)) {
|
||||
next(); // The "effective" role has the required permission
|
||||
} else {
|
||||
// The "effective" role does not have the required permission
|
||||
const roleName = effectiveRole.name || 'unknown role';
|
||||
next(
|
||||
new ValidationError(
|
||||
'auth.forbidden',
|
||||
`Role '${roleName}' denied access to '${permission}'.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// Handle errors during role or permission fetching
|
||||
console.error('Error during permission check:', e);
|
||||
next(e); // Pass the error to the next middleware
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const METHOD_MAP = {
|
||||
POST: 'CREATE',
|
||||
GET: 'READ',
|
||||
PUT: 'UPDATE',
|
||||
PATCH: 'UPDATE',
|
||||
DELETE: 'DELETE',
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware creator to check standard CRUD permissions based on HTTP method and entity name.
|
||||
* @param {string} name - The name of the entity.
|
||||
* @return {import("express").RequestHandler} Express middleware function.
|
||||
*/
|
||||
function checkCrudPermissions(name) {
|
||||
return (req, res, next) => {
|
||||
// Dynamically determine the permission name (e.g., 'READ_USERS')
|
||||
const permissionName = `${METHOD_MAP[req.method]}_${name.toUpperCase()}`;
|
||||
// Call the checkPermissions middleware with the determined permission
|
||||
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;
|
||||
462
backend/src/routes/audit_logs.js
Normal file
462
backend/src/routes/audit_logs.js
Normal file
@ -0,0 +1,462 @@
|
||||
const express = require('express');
|
||||
|
||||
const Audit_logsService = require('../services/audit_logs');
|
||||
const Audit_logsDBApi = require('../db/api/audit_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('audit_logs'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Audit_logs:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* action:
|
||||
* type: string
|
||||
* default: action
|
||||
* entity_type:
|
||||
* type: string
|
||||
* default: entity_type
|
||||
* diff_json:
|
||||
* type: string
|
||||
* default: diff_json
|
||||
|
||||
* entity_id:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Audit_logs
|
||||
* description: The Audit_logs managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_logs:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_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/Audit_logs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Audit_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 Audit_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: [Audit_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/Audit_logs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Audit_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 Audit_logsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_logs/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_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/Audit_logs"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Audit_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 Audit_logsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_logs/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_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/Audit_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 Audit_logsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_logs/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_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/Audit_logs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Audit_logsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_logs:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_logs]
|
||||
* summary: Get all audit_logs
|
||||
* description: Get all audit_logs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Audit_logs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Audit_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 Audit_logsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'action', 'entity_type', 'diff_json', 'entity_id'];
|
||||
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/audit_logs/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_logs]
|
||||
* summary: Count all audit_logs
|
||||
* description: Count all audit_logs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Audit_logs count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Audit_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 Audit_logsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_logs/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_logs]
|
||||
* summary: Find all audit_logs that match search criteria
|
||||
* description: Find all audit_logs that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Audit_logs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Audit_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 Audit_logsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_logs/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_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/Audit_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 Audit_logsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
456
backend/src/routes/audit_packs.js
Normal file
456
backend/src/routes/audit_packs.js
Normal file
@ -0,0 +1,456 @@
|
||||
const express = require('express');
|
||||
|
||||
const Audit_packsService = require('../services/audit_packs');
|
||||
const Audit_packsDBApi = require('../db/api/audit_packs');
|
||||
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('audit_packs'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Audit_packs:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* zip_uri:
|
||||
* type: string
|
||||
* default: zip_uri
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Audit_packs
|
||||
* description: The Audit_packs managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_packs:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* 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/Audit_packs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Audit_packs"
|
||||
* 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 Audit_packsService.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: [Audit_packs]
|
||||
* 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/Audit_packs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Audit_packs"
|
||||
* 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 Audit_packsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_packs/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* 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/Audit_packs"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Audit_packs"
|
||||
* 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 Audit_packsService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_packs/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* 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/Audit_packs"
|
||||
* 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 Audit_packsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_packs/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* 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/Audit_packs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Audit_packsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_packs:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* summary: Get all audit_packs
|
||||
* description: Get all audit_packs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Audit_packs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Audit_packs"
|
||||
* 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 Audit_packsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'zip_uri'];
|
||||
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/audit_packs/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* summary: Count all audit_packs
|
||||
* description: Count all audit_packs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Audit_packs count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Audit_packs"
|
||||
* 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 Audit_packsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_packs/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* summary: Find all audit_packs that match search criteria
|
||||
* description: Find all audit_packs that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Audit_packs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Audit_packs"
|
||||
* 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 Audit_packsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/audit_packs/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Audit_packs]
|
||||
* 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/Audit_packs"
|
||||
* 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 Audit_packsDBApi.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;
|
||||
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: 'liz@labtechanalytics.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;
|
||||
470
backend/src/routes/contexts.js
Normal file
470
backend/src/routes/contexts.js
Normal file
@ -0,0 +1,470 @@
|
||||
const express = require('express');
|
||||
|
||||
const ContextsService = require('../services/contexts');
|
||||
const ContextsDBApi = require('../db/api/contexts');
|
||||
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('contexts'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Contexts:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* identifier_scheme:
|
||||
* type: string
|
||||
* default: identifier_scheme
|
||||
* identifier_value:
|
||||
* type: string
|
||||
* default: identifier_value
|
||||
* scenario:
|
||||
* type: string
|
||||
* default: scenario
|
||||
* segment:
|
||||
* type: string
|
||||
* default: segment
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Contexts
|
||||
* description: The Contexts managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/contexts:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* 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/Contexts"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Contexts"
|
||||
* 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 ContextsService.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: [Contexts]
|
||||
* 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/Contexts"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Contexts"
|
||||
* 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 ContextsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/contexts/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* 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/Contexts"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Contexts"
|
||||
* 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 ContextsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/contexts/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* 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/Contexts"
|
||||
* 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 ContextsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/contexts/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* 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/Contexts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await ContextsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/contexts:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* summary: Get all contexts
|
||||
* description: Get all contexts
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Contexts list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Contexts"
|
||||
* 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 ContextsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = [
|
||||
'id',
|
||||
'identifier_scheme',
|
||||
'identifier_value',
|
||||
'scenario',
|
||||
'segment',
|
||||
|
||||
'period_start',
|
||||
'period_end',
|
||||
];
|
||||
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/contexts/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* summary: Count all contexts
|
||||
* description: Count all contexts
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Contexts count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Contexts"
|
||||
* 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 ContextsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/contexts/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* summary: Find all contexts that match search criteria
|
||||
* description: Find all contexts that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Contexts list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Contexts"
|
||||
* 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 ContextsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/contexts/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Contexts]
|
||||
* 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/Contexts"
|
||||
* 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 ContextsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
473
backend/src/routes/evidence_files.js
Normal file
473
backend/src/routes/evidence_files.js
Normal file
@ -0,0 +1,473 @@
|
||||
const express = require('express');
|
||||
|
||||
const Evidence_filesService = require('../services/evidence_files');
|
||||
const Evidence_filesDBApi = require('../db/api/evidence_files');
|
||||
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('evidence_files'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Evidence_files:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* file_name:
|
||||
* type: string
|
||||
* default: file_name
|
||||
* mime_type:
|
||||
* type: string
|
||||
* default: mime_type
|
||||
* gcs_uri:
|
||||
* type: string
|
||||
* default: gcs_uri
|
||||
* checksum:
|
||||
* type: string
|
||||
* default: checksum
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Evidence_files
|
||||
* description: The Evidence_files managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/evidence_files:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* 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/Evidence_files"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Evidence_files"
|
||||
* 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 Evidence_filesService.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: [Evidence_files]
|
||||
* 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/Evidence_files"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Evidence_files"
|
||||
* 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 Evidence_filesService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/evidence_files/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* 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/Evidence_files"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Evidence_files"
|
||||
* 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 Evidence_filesService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/evidence_files/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* 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/Evidence_files"
|
||||
* 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 Evidence_filesService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/evidence_files/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* 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/Evidence_files"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Evidence_filesService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/evidence_files:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* summary: Get all evidence_files
|
||||
* description: Get all evidence_files
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Evidence_files list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Evidence_files"
|
||||
* 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 Evidence_filesDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = [
|
||||
'id',
|
||||
'file_name',
|
||||
'mime_type',
|
||||
'gcs_uri',
|
||||
'checksum',
|
||||
|
||||
'uploaded_at',
|
||||
];
|
||||
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/evidence_files/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* summary: Count all evidence_files
|
||||
* description: Count all evidence_files
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Evidence_files count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Evidence_files"
|
||||
* 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 Evidence_filesDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/evidence_files/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* summary: Find all evidence_files that match search criteria
|
||||
* description: Find all evidence_files that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Evidence_files list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Evidence_files"
|
||||
* 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 Evidence_filesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/evidence_files/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Evidence_files]
|
||||
* 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/Evidence_files"
|
||||
* 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 Evidence_filesDBApi.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;
|
||||
466
backend/src/routes/jobs.js
Normal file
466
backend/src/routes/jobs.js
Normal file
@ -0,0 +1,466 @@
|
||||
const express = require('express');
|
||||
|
||||
const JobsService = require('../services/jobs');
|
||||
const JobsDBApi = require('../db/api/jobs');
|
||||
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('jobs'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Jobs:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* status:
|
||||
* type: string
|
||||
* default: status
|
||||
* payload:
|
||||
* type: string
|
||||
* default: payload
|
||||
* result:
|
||||
* type: string
|
||||
* default: result
|
||||
* error_text:
|
||||
* type: string
|
||||
* default: error_text
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Jobs
|
||||
* description: The Jobs managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/jobs:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* 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/Jobs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Jobs"
|
||||
* 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 JobsService.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: [Jobs]
|
||||
* 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/Jobs"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Jobs"
|
||||
* 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 JobsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/jobs/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* 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/Jobs"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Jobs"
|
||||
* 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 JobsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/jobs/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* 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/Jobs"
|
||||
* 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 JobsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/jobs/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* 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/Jobs"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await JobsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/jobs:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* summary: Get all jobs
|
||||
* description: Get all jobs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Jobs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Jobs"
|
||||
* 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 JobsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = [
|
||||
'id',
|
||||
'status',
|
||||
'payload',
|
||||
'result',
|
||||
'error_text',
|
||||
|
||||
'started_at',
|
||||
'finished_at',
|
||||
];
|
||||
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/jobs/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* summary: Count all jobs
|
||||
* description: Count all jobs
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Jobs count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Jobs"
|
||||
* 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 JobsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/jobs/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* summary: Find all jobs that match search criteria
|
||||
* description: Find all jobs that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Jobs list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Jobs"
|
||||
* 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 JobsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/jobs/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Jobs]
|
||||
* 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/Jobs"
|
||||
* 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 JobsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
456
backend/src/routes/mappings.js
Normal file
456
backend/src/routes/mappings.js
Normal file
@ -0,0 +1,456 @@
|
||||
const express = require('express');
|
||||
|
||||
const MappingsService = require('../services/mappings');
|
||||
const MappingsDBApi = require('../db/api/mappings');
|
||||
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('mappings'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Mappings:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* source_path:
|
||||
* type: string
|
||||
* default: source_path
|
||||
* target_concept_qname:
|
||||
* type: string
|
||||
* default: target_concept_qname
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Mappings
|
||||
* description: The Mappings managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/mappings:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* 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/Mappings"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Mappings"
|
||||
* 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 MappingsService.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: [Mappings]
|
||||
* 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/Mappings"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Mappings"
|
||||
* 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 MappingsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/mappings/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* 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/Mappings"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Mappings"
|
||||
* 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 MappingsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/mappings/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* 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/Mappings"
|
||||
* 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 MappingsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/mappings/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* 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/Mappings"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await MappingsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/mappings:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* summary: Get all mappings
|
||||
* description: Get all mappings
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Mappings list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Mappings"
|
||||
* 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 MappingsDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'source_path', 'target_concept_qname'];
|
||||
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/mappings/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* summary: Count all mappings
|
||||
* description: Count all mappings
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Mappings count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Mappings"
|
||||
* 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 MappingsDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/mappings/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* summary: Find all mappings that match search criteria
|
||||
* description: Find all mappings that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Mappings list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Mappings"
|
||||
* 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 MappingsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/mappings/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Mappings]
|
||||
* 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/Mappings"
|
||||
* 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 MappingsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
477
backend/src/routes/metric_values.js
Normal file
477
backend/src/routes/metric_values.js
Normal file
@ -0,0 +1,477 @@
|
||||
const express = require('express');
|
||||
|
||||
const Metric_valuesService = require('../services/metric_values');
|
||||
const Metric_valuesDBApi = require('../db/api/metric_values');
|
||||
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('metric_values'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Metric_values:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* concept_qname:
|
||||
* type: string
|
||||
* default: concept_qname
|
||||
* value_text:
|
||||
* type: string
|
||||
* default: value_text
|
||||
* note:
|
||||
* type: string
|
||||
* default: note
|
||||
|
||||
* decimals:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
* value_numeric:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Metric_values
|
||||
* description: The Metric_values managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/metric_values:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* 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/Metric_values"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Metric_values"
|
||||
* 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 Metric_valuesService.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: [Metric_values]
|
||||
* 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/Metric_values"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Metric_values"
|
||||
* 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 Metric_valuesService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/metric_values/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* 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/Metric_values"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Metric_values"
|
||||
* 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 Metric_valuesService.update(
|
||||
req.body.data,
|
||||
req.body.id,
|
||||
req.currentUser,
|
||||
);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/metric_values/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* 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/Metric_values"
|
||||
* 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 Metric_valuesService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/metric_values/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* 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/Metric_values"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Metric_valuesService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/metric_values:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* summary: Get all metric_values
|
||||
* description: Get all metric_values
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Metric_values list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Metric_values"
|
||||
* 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 Metric_valuesDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = [
|
||||
'id',
|
||||
'concept_qname',
|
||||
'value_text',
|
||||
'note',
|
||||
'decimals',
|
||||
'value_numeric',
|
||||
];
|
||||
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/metric_values/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* summary: Count all metric_values
|
||||
* description: Count all metric_values
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Metric_values count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Metric_values"
|
||||
* 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 Metric_valuesDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/metric_values/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* summary: Find all metric_values that match search criteria
|
||||
* description: Find all metric_values that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Metric_values list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Metric_values"
|
||||
* 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 Metric_valuesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/metric_values/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Metric_values]
|
||||
* 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/Metric_values"
|
||||
* 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 Metric_valuesDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
458
backend/src/routes/narratives.js
Normal file
458
backend/src/routes/narratives.js
Normal file
@ -0,0 +1,458 @@
|
||||
const express = require('express');
|
||||
|
||||
const NarrativesService = require('../services/narratives');
|
||||
const NarrativesDBApi = require('../db/api/narratives');
|
||||
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('narratives'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Narratives:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* concept_qname:
|
||||
* type: string
|
||||
* default: concept_qname
|
||||
* value_text:
|
||||
* type: string
|
||||
* default: value_text
|
||||
* note:
|
||||
* type: string
|
||||
* default: note
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Narratives
|
||||
* description: The Narratives managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/narratives:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* 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/Narratives"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Narratives"
|
||||
* 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 NarrativesService.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: [Narratives]
|
||||
* 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/Narratives"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Narratives"
|
||||
* 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 NarrativesService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/narratives/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* 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/Narratives"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Narratives"
|
||||
* 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 NarrativesService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/narratives/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* 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/Narratives"
|
||||
* 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 NarrativesService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/narratives/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* 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/Narratives"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await NarrativesService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/narratives:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* summary: Get all narratives
|
||||
* description: Get all narratives
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Narratives list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Narratives"
|
||||
* 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 NarrativesDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'concept_qname', 'value_text', 'note'];
|
||||
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/narratives/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* summary: Count all narratives
|
||||
* description: Count all narratives
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Narratives count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Narratives"
|
||||
* 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 NarrativesDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/narratives/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* summary: Find all narratives that match search criteria
|
||||
* description: Find all narratives that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Narratives list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Narratives"
|
||||
* 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 NarrativesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/narratives/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Narratives]
|
||||
* 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/Narratives"
|
||||
* 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 NarrativesDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
244
backend/src/routes/openai.js
Normal file
244
backend/src/routes/openai.js
Normal file
@ -0,0 +1,244 @@
|
||||
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, askGpt } = 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);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/openai/ask:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [OpenAI]
|
||||
* summary: Ask a question to ChatGPT
|
||||
* description: Send a question to OpenAI's ChatGPT and get a response
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* question:
|
||||
* type: string
|
||||
* description: The question to ask ChatGPT
|
||||
* apiKey:
|
||||
* type: string
|
||||
* description: OpenAI API key
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Question successfully answered
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* description: Whether the request was successful
|
||||
* data:
|
||||
* type: string
|
||||
* description: The answer from ChatGPT
|
||||
* 400:
|
||||
* description: Invalid request
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/ask-gpt',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { prompt } = req.body;
|
||||
if (!prompt) {
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
error: 'Question and API key are required',
|
||||
});
|
||||
}
|
||||
|
||||
const response = await askGpt(prompt);
|
||||
|
||||
if (response.success) {
|
||||
return res.status(200).send(response);
|
||||
} else {
|
||||
return res.status(500).send(response);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
453
backend/src/routes/org_users.js
Normal file
453
backend/src/routes/org_users.js
Normal file
@ -0,0 +1,453 @@
|
||||
const express = require('express');
|
||||
|
||||
const Org_usersService = require('../services/org_users');
|
||||
const Org_usersDBApi = require('../db/api/org_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('org_users'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Org_users:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* status:
|
||||
* type: string
|
||||
* default: status
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Org_users
|
||||
* description: The Org_users managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/org_users:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_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/Org_users"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Org_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 Org_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: [Org_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/Org_users"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Org_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 Org_usersService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/org_users/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_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/Org_users"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Org_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 Org_usersService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/org_users/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_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/Org_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 Org_usersService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/org_users/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_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/Org_users"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await Org_usersService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/org_users:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_users]
|
||||
* summary: Get all org_users
|
||||
* description: Get all org_users
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Org_users list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Org_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 Org_usersDBApi.findAll(req.query, globalAccess, {
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'status'];
|
||||
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/org_users/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_users]
|
||||
* summary: Count all org_users
|
||||
* description: Count all org_users
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Org_users count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Org_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 Org_usersDBApi.findAll(req.query, globalAccess, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/org_users/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_users]
|
||||
* summary: Find all org_users that match search criteria
|
||||
* description: Find all org_users that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Org_users list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Org_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 Org_usersDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess,
|
||||
organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/org_users/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Org_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/Org_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 Org_usersDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
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