Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
backend/node_modules
|
||||||
|
frontend/node_modules
|
||||||
|
frontend/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>Gerador de Loterias Admin</h2>
|
||||||
|
<p>Admin-only dashboard to manage lottery draws, import histories, create future placeholders, and generate statistical predictions.</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>
|
||||||
21
Dockerfile
Normal file
21
Dockerfile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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"]
|
||||||
14
backend/.env
Normal file
14
backend/.env
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
DB_NAME=app_39019
|
||||||
|
DB_USER=app_39019
|
||||||
|
DB_PASS=7c4e8fc3-6a8f-413a-96be-8298508480ad
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=5432
|
||||||
|
PORT=3000
|
||||||
|
GOOGLE_CLIENT_ID=671001533244-kf1k1gmp6mnl0r030qmvdu6v36ghmim6.apps.googleusercontent.com
|
||||||
|
GOOGLE_CLIENT_SECRET=Yo4qbKZniqvojzUQ60iKlxqR
|
||||||
|
MS_CLIENT_ID=4696f457-31af-40de-897c-e00d7d4cff73
|
||||||
|
MS_CLIENT_SECRET=m8jzZ.5UpHF3=-dXzyxiZ4e[F8OF54@p
|
||||||
|
EMAIL_USER=AKIAVEW7G4PQUBGM52OF
|
||||||
|
EMAIL_PASS=BLnD4hKGb6YkSz3gaQrf8fnyLi3C3/EdjOOsLEDTDPTz
|
||||||
|
SECRET_KEY=HUEyqESqgQ1yTwzVlO6wprC9Kf1J1xuA
|
||||||
|
PEXELS_KEY=Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18
|
||||||
4
backend/.eslintignore
Normal file
4
backend/.eslintignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore generated and runtime files
|
||||||
|
node_modules/
|
||||||
|
tmp/
|
||||||
|
logs/
|
||||||
15
backend/.eslintrc.cjs
Normal file
15
backend/.eslintrc.cjs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
es2021: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended'
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'import'
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'import/no-unresolved': 'error'
|
||||||
|
}
|
||||||
|
};
|
||||||
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" ]
|
||||||
56
backend/README.md
Normal file
56
backend/README.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
#Gerador de Loterias Admin - 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_gerador_de_loterias_admin;`
|
||||||
|
|
||||||
|
- Then give that new user privileges to the new database then quit the `psql`.
|
||||||
|
- `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_gerador_de_loterias_admin 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`
|
||||||
@ -1,12 +1,56 @@
|
|||||||
{
|
{
|
||||||
"name": "backend",
|
"name": "geradordeloteriasadmin",
|
||||||
"version": "1.0.0",
|
"description": "Gerador de Loterias Admin - template backend",
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
||||||
|
"lint": "eslint . --ext .js",
|
||||||
|
"db:migrate": "sequelize-cli db:migrate",
|
||||||
|
"db:seed": "sequelize-cli db:seed:all",
|
||||||
|
"db:drop": "sequelize-cli db:drop",
|
||||||
|
"db:create": "sequelize-cli db:create",
|
||||||
|
"watch": "node watcher.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"dependencies": {
|
||||||
"author": "",
|
"@google-cloud/storage": "^5.18.2",
|
||||||
"license": "ISC"
|
"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",
|
||||||
|
"eslint": "^8.23.1",
|
||||||
|
"eslint-plugin-import": "^2.29.1",
|
||||||
|
"mocha": "8.1.3",
|
||||||
|
"node-mocks-http": "1.9.0",
|
||||||
|
"nodemon": "2.0.5",
|
||||||
|
"sequelize-cli": "6.6.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
484
backend/src/ai/LocalAIApi.js
Normal file
484
backend/src/ai/LocalAIApi.js
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const http = require("http");
|
||||||
|
const https = require("https");
|
||||||
|
const { URL } = require("url");
|
||||||
|
|
||||||
|
let CONFIG_CACHE = null;
|
||||||
|
|
||||||
|
class LocalAIApi {
|
||||||
|
static createResponse(params, options) {
|
||||||
|
return createResponse(params, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static request(pathValue, payload, options) {
|
||||||
|
return request(pathValue, payload, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fetchStatus(aiRequestId, options) {
|
||||||
|
return fetchStatus(aiRequestId, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static awaitResponse(aiRequestId, options) {
|
||||||
|
return awaitResponse(aiRequestId, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static extractText(response) {
|
||||||
|
return extractText(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
static decodeJsonFromResponse(response) {
|
||||||
|
return decodeJsonFromResponse(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createResponse(params, options = {}) {
|
||||||
|
const payload = { ...(params || {}) };
|
||||||
|
|
||||||
|
if (!Array.isArray(payload.input) || payload.input.length === 0) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "input_missing",
|
||||||
|
message: 'Parameter "input" is required and must be a non-empty array.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const cfg = config();
|
||||||
|
if (!payload.model) {
|
||||||
|
payload.model = cfg.defaultModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initial = await request(options.path, payload, options);
|
||||||
|
if (!initial.success) {
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = initial.data;
|
||||||
|
if (data && typeof data === "object" && data.ai_request_id) {
|
||||||
|
const pollTimeout = Number(options.poll_timeout ?? 300);
|
||||||
|
const pollInterval = Number(options.poll_interval ?? 5);
|
||||||
|
return await awaitResponse(data.ai_request_id, {
|
||||||
|
interval: pollInterval,
|
||||||
|
timeout: pollTimeout,
|
||||||
|
headers: options.headers,
|
||||||
|
timeout_per_call: options.timeout,
|
||||||
|
verify_tls: options.verify_tls,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request(pathValue, payload = {}, options = {}) {
|
||||||
|
const cfg = config();
|
||||||
|
const resolvedPath = pathValue || options.path || cfg.responsesPath;
|
||||||
|
|
||||||
|
if (!resolvedPath) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "project_id_missing",
|
||||||
|
message: "PROJECT_ID is not defined; cannot resolve AI proxy endpoint.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cfg.projectUuid) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "project_uuid_missing",
|
||||||
|
message: "PROJECT_UUID is not defined; aborting AI request.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const bodyPayload = { ...(payload || {}) };
|
||||||
|
if (!bodyPayload.project_uuid) {
|
||||||
|
bodyPayload.project_uuid = cfg.projectUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = buildUrl(resolvedPath, cfg.baseUrl);
|
||||||
|
const timeout = resolveTimeout(options.timeout, cfg.timeout);
|
||||||
|
const verifyTls = resolveVerifyTls(options.verify_tls, cfg.verifyTls);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
[cfg.projectHeader]: cfg.projectUuid,
|
||||||
|
};
|
||||||
|
if (Array.isArray(options.headers)) {
|
||||||
|
for (const header of options.headers) {
|
||||||
|
if (typeof header === "string" && header.includes(":")) {
|
||||||
|
const [name, value] = header.split(":", 2);
|
||||||
|
headers[name.trim()] = value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = JSON.stringify(bodyPayload);
|
||||||
|
return sendRequest(url, "POST", body, headers, timeout, verifyTls);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchStatus(aiRequestId, options = {}) {
|
||||||
|
const cfg = config();
|
||||||
|
if (!cfg.projectUuid) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "project_uuid_missing",
|
||||||
|
message: "PROJECT_UUID is not defined; aborting status check.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusPath = resolveStatusPath(aiRequestId, cfg);
|
||||||
|
const url = buildUrl(statusPath, cfg.baseUrl);
|
||||||
|
const timeout = resolveTimeout(options.timeout, cfg.timeout);
|
||||||
|
const verifyTls = resolveVerifyTls(options.verify_tls, cfg.verifyTls);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: "application/json",
|
||||||
|
[cfg.projectHeader]: cfg.projectUuid,
|
||||||
|
};
|
||||||
|
if (Array.isArray(options.headers)) {
|
||||||
|
for (const header of options.headers) {
|
||||||
|
if (typeof header === "string" && header.includes(":")) {
|
||||||
|
const [name, value] = header.split(":", 2);
|
||||||
|
headers[name.trim()] = value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendRequest(url, "GET", null, headers, timeout, verifyTls);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function awaitResponse(aiRequestId, options = {}) {
|
||||||
|
const timeout = Number(options.timeout ?? 300);
|
||||||
|
const interval = Math.max(Number(options.interval ?? 5), 1);
|
||||||
|
const deadline = Date.now() + Math.max(timeout, interval) * 1000;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const statusResp = await fetchStatus(aiRequestId, {
|
||||||
|
headers: options.headers,
|
||||||
|
timeout: options.timeout_per_call,
|
||||||
|
verify_tls: options.verify_tls,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (statusResp.success) {
|
||||||
|
const data = statusResp.data || {};
|
||||||
|
if (data && typeof data === "object") {
|
||||||
|
if (data.status === "success") {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: 200,
|
||||||
|
data: data.response || data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (data.status === "failed") {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: 500,
|
||||||
|
error: String(data.error || "AI request failed"),
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return statusResp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Date.now() >= deadline) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "timeout",
|
||||||
|
message: "Timed out waiting for AI response.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(interval * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractText(response) {
|
||||||
|
const payload = response && typeof response === "object" ? response.data || response : null;
|
||||||
|
if (!payload || typeof payload !== "object") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(payload.output)) {
|
||||||
|
let combined = "";
|
||||||
|
for (const item of payload.output) {
|
||||||
|
if (!item || !Array.isArray(item.content)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const block of item.content) {
|
||||||
|
if (
|
||||||
|
block &&
|
||||||
|
typeof block === "object" &&
|
||||||
|
block.type === "output_text" &&
|
||||||
|
typeof block.text === "string" &&
|
||||||
|
block.text.length > 0
|
||||||
|
) {
|
||||||
|
combined += block.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (combined) {
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
payload.choices &&
|
||||||
|
payload.choices[0] &&
|
||||||
|
payload.choices[0].message &&
|
||||||
|
typeof payload.choices[0].message.content === "string"
|
||||||
|
) {
|
||||||
|
return payload.choices[0].message.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeJsonFromResponse(response) {
|
||||||
|
const text = extractText(response);
|
||||||
|
if (!text) {
|
||||||
|
throw new Error("No text found in AI response.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = parseJson(text);
|
||||||
|
if (parsed.ok && parsed.value && typeof parsed.value === "object") {
|
||||||
|
return parsed.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripped = stripJsonFence(text);
|
||||||
|
if (stripped !== text) {
|
||||||
|
const parsedStripped = parseJson(stripped);
|
||||||
|
if (parsedStripped.ok && parsedStripped.value && typeof parsedStripped.value === "object") {
|
||||||
|
return parsedStripped.value;
|
||||||
|
}
|
||||||
|
throw new Error(`JSON parse failed after stripping fences: ${parsedStripped.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`JSON parse failed: ${parsed.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function config() {
|
||||||
|
if (CONFIG_CACHE) {
|
||||||
|
return CONFIG_CACHE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureEnvLoaded();
|
||||||
|
|
||||||
|
const baseUrl = process.env.AI_PROXY_BASE_URL || "https://flatlogic.com";
|
||||||
|
const projectId = process.env.PROJECT_ID || null;
|
||||||
|
let responsesPath = process.env.AI_RESPONSES_PATH || null;
|
||||||
|
if (!responsesPath && projectId) {
|
||||||
|
responsesPath = `/projects/${projectId}/ai-request`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeout = resolveTimeout(process.env.AI_TIMEOUT, 30);
|
||||||
|
const verifyTls = resolveVerifyTls(process.env.AI_VERIFY_TLS, true);
|
||||||
|
|
||||||
|
CONFIG_CACHE = {
|
||||||
|
baseUrl,
|
||||||
|
responsesPath,
|
||||||
|
projectId,
|
||||||
|
projectUuid: process.env.PROJECT_UUID || null,
|
||||||
|
projectHeader: process.env.AI_PROJECT_HEADER || "project-uuid",
|
||||||
|
defaultModel: process.env.AI_DEFAULT_MODEL || "gpt-5-mini",
|
||||||
|
timeout,
|
||||||
|
verifyTls,
|
||||||
|
};
|
||||||
|
|
||||||
|
return CONFIG_CACHE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildUrl(pathValue, baseUrl) {
|
||||||
|
const trimmed = String(pathValue || "").trim();
|
||||||
|
if (trimmed === "") {
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith("/")) {
|
||||||
|
return `${baseUrl}${trimmed}`;
|
||||||
|
}
|
||||||
|
return `${baseUrl}/${trimmed}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveStatusPath(aiRequestId, cfg) {
|
||||||
|
const basePath = (cfg.responsesPath || "").replace(/\/+$/, "");
|
||||||
|
if (!basePath) {
|
||||||
|
return `/ai-request/${encodeURIComponent(String(aiRequestId))}/status`;
|
||||||
|
}
|
||||||
|
const normalized = basePath.endsWith("/ai-request") ? basePath : `${basePath}/ai-request`;
|
||||||
|
return `${normalized}/${encodeURIComponent(String(aiRequestId))}/status`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendRequest(urlString, method, body, headers, timeoutSeconds, verifyTls) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let targetUrl;
|
||||||
|
try {
|
||||||
|
targetUrl = new URL(urlString);
|
||||||
|
} catch (err) {
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
error: "invalid_url",
|
||||||
|
message: err.message,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isHttps = targetUrl.protocol === "https:";
|
||||||
|
const requestFn = isHttps ? https.request : http.request;
|
||||||
|
const options = {
|
||||||
|
protocol: targetUrl.protocol,
|
||||||
|
hostname: targetUrl.hostname,
|
||||||
|
port: targetUrl.port || (isHttps ? 443 : 80),
|
||||||
|
path: `${targetUrl.pathname}${targetUrl.search}`,
|
||||||
|
method: method.toUpperCase(),
|
||||||
|
headers,
|
||||||
|
timeout: Math.max(Number(timeoutSeconds || 30), 1) * 1000,
|
||||||
|
};
|
||||||
|
if (isHttps) {
|
||||||
|
options.rejectUnauthorized = Boolean(verifyTls);
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = requestFn(options, (res) => {
|
||||||
|
let responseBody = "";
|
||||||
|
res.setEncoding("utf8");
|
||||||
|
res.on("data", (chunk) => {
|
||||||
|
responseBody += chunk;
|
||||||
|
});
|
||||||
|
res.on("end", () => {
|
||||||
|
const status = res.statusCode || 0;
|
||||||
|
const parsed = parseJson(responseBody);
|
||||||
|
const payload = parsed.ok ? parsed.value : responseBody;
|
||||||
|
|
||||||
|
if (status >= 200 && status < 300) {
|
||||||
|
const result = {
|
||||||
|
success: true,
|
||||||
|
status,
|
||||||
|
data: payload,
|
||||||
|
};
|
||||||
|
if (!parsed.ok) {
|
||||||
|
result.json_error = parsed.error;
|
||||||
|
}
|
||||||
|
resolve(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMessage =
|
||||||
|
parsed.ok && payload && typeof payload === "object"
|
||||||
|
? String(payload.error || payload.message || "AI proxy request failed")
|
||||||
|
: String(responseBody || "AI proxy request failed");
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
status,
|
||||||
|
error: errorMessage,
|
||||||
|
response: payload,
|
||||||
|
json_error: parsed.ok ? undefined : parsed.error,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on("timeout", () => {
|
||||||
|
req.destroy(new Error("request_timeout"));
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on("error", (err) => {
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
error: "request_failed",
|
||||||
|
message: err.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
req.write(body);
|
||||||
|
}
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJson(value) {
|
||||||
|
if (typeof value !== "string" || value.trim() === "") {
|
||||||
|
return { ok: false, error: "empty_response" };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return { ok: true, value: JSON.parse(value) };
|
||||||
|
} catch (err) {
|
||||||
|
return { ok: false, error: err.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripJsonFence(text) {
|
||||||
|
const trimmed = text.trim();
|
||||||
|
if (trimmed.startsWith("```json")) {
|
||||||
|
return trimmed.replace(/^```json/, "").replace(/```$/, "").trim();
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith("```")) {
|
||||||
|
return trimmed.replace(/^```/, "").replace(/```$/, "").trim();
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTimeout(value, fallback) {
|
||||||
|
const parsed = Number.parseInt(String(value ?? fallback), 10);
|
||||||
|
return Number.isNaN(parsed) ? Number(fallback) : parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveVerifyTls(value, fallback) {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return Boolean(fallback);
|
||||||
|
}
|
||||||
|
return String(value).toLowerCase() !== "false" && String(value) !== "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureEnvLoaded() {
|
||||||
|
if (process.env.PROJECT_UUID && process.env.PROJECT_ID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const envPath = path.resolve(__dirname, "../../../../.env");
|
||||||
|
if (!fs.existsSync(envPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content;
|
||||||
|
try {
|
||||||
|
content = fs.readFileSync(envPath, "utf8");
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`Failed to read executor .env: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const line of content.split(/\r?\n/)) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const [rawKey, ...rest] = trimmed.split("=");
|
||||||
|
const key = rawKey.trim();
|
||||||
|
if (!key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const value = rest.join("=").trim().replace(/^['"]|['"]$/g, "");
|
||||||
|
if (!process.env[key]) {
|
||||||
|
process.env[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
LocalAIApi,
|
||||||
|
createResponse,
|
||||||
|
request,
|
||||||
|
fetchStatus,
|
||||||
|
awaitResponse,
|
||||||
|
extractText,
|
||||||
|
decodeJsonFromResponse,
|
||||||
|
};
|
||||||
68
backend/src/auth/auth.js
Normal file
68
backend/src/auth/auth.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
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});
|
||||||
|
});
|
||||||
|
}
|
||||||
79
backend/src/config.js
Normal file
79
backend/src/config.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
gcloud: {
|
||||||
|
bucket: "fldemo-files",
|
||||||
|
hash: "afeefb9d49f5b7977577876b99532ac7"
|
||||||
|
},
|
||||||
|
bcrypt: {
|
||||||
|
saltRounds: 12
|
||||||
|
},
|
||||||
|
admin_pass: "7c4e8fc3",
|
||||||
|
user_pass: "8298508480ad",
|
||||||
|
admin_email: "admin@flatlogic.com",
|
||||||
|
providers: {
|
||||||
|
LOCAL: 'local',
|
||||||
|
GOOGLE: 'google',
|
||||||
|
MICROSOFT: 'microsoft'
|
||||||
|
},
|
||||||
|
secret_key: process.env.SECRET_KEY || '7c4e8fc3-6a8f-413a-96be-8298508480ad',
|
||||||
|
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: 'Gerador de Loterias Admin <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: {
|
||||||
|
|
||||||
|
admin: 'Administrator',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
user: 'ReadOnly Auditor',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
project_uuid: '7c4e8fc3-6a8f-413a-96be-8298508480ad',
|
||||||
|
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 = 'Starry sky over mountain ridge';
|
||||||
|
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;
|
||||||
590
backend/src/db/api/admin_keys.js
Normal file
590
backend/src/db/api/admin_keys.js
Normal file
@ -0,0 +1,590 @@
|
|||||||
|
|
||||||
|
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 Admin_keysDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const admin_keys = await db.admin_keys.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
key_name: data.key_name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
key_fingerprint: data.key_fingerprint
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
issued_at: data.issued_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
expires_at: data.expires_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
single_use: data.single_use
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
max_uses: data.max_uses
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
uses_count: data.uses_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await admin_keys.setIssued_for_user( data.issued_for_user || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return admin_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 admin_keysData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
key_name: item.key_name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
key_fingerprint: item.key_fingerprint
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
issued_at: item.issued_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
expires_at: item.expires_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
single_use: item.single_use
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
max_uses: item.max_uses
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
uses_count: item.uses_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const admin_keys = await db.admin_keys.bulkCreate(admin_keysData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
|
return admin_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const admin_keys = await db.admin_keys.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.key_name !== undefined) updatePayload.key_name = data.key_name;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.key_fingerprint !== undefined) updatePayload.key_fingerprint = data.key_fingerprint;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.issued_at !== undefined) updatePayload.issued_at = data.issued_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.expires_at !== undefined) updatePayload.expires_at = data.expires_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.single_use !== undefined) updatePayload.single_use = data.single_use;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.max_uses !== undefined) updatePayload.max_uses = data.max_uses;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.uses_count !== undefined) updatePayload.uses_count = data.uses_count;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await admin_keys.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.issued_for_user !== undefined) {
|
||||||
|
await admin_keys.setIssued_for_user(
|
||||||
|
|
||||||
|
data.issued_for_user,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return admin_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const admin_keys = await db.admin_keys.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of admin_keys) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of admin_keys) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return admin_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const admin_keys = await db.admin_keys.findByPk(id, options);
|
||||||
|
|
||||||
|
await admin_keys.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await admin_keys.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return admin_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const admin_keys = await db.admin_keys.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!admin_keys) {
|
||||||
|
return admin_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = admin_keys.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.issued_for_user = await admin_keys.getIssued_for_user({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.users,
|
||||||
|
as: 'issued_for_user',
|
||||||
|
|
||||||
|
where: filter.issued_for_user ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.issued_for_user.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
firstName: {
|
||||||
|
[Op.or]: filter.issued_for_user.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.key_name) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'admin_keys',
|
||||||
|
'key_name',
|
||||||
|
filter.key_name,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.key_fingerprint) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'admin_keys',
|
||||||
|
'key_fingerprint',
|
||||||
|
filter.key_fingerprint,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.issued_atRange) {
|
||||||
|
const [start, end] = filter.issued_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
issued_at: {
|
||||||
|
...where.issued_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
issued_at: {
|
||||||
|
...where.issued_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.expires_atRange) {
|
||||||
|
const [start, end] = filter.expires_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
expires_at: {
|
||||||
|
...where.expires_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
expires_at: {
|
||||||
|
...where.expires_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.max_usesRange) {
|
||||||
|
const [start, end] = filter.max_usesRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
max_uses: {
|
||||||
|
...where.max_uses,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
max_uses: {
|
||||||
|
...where.max_uses,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.uses_countRange) {
|
||||||
|
const [start, end] = filter.uses_countRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
uses_count: {
|
||||||
|
...where.uses_count,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
uses_count: {
|
||||||
|
...where.uses_count,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.single_use) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
single_use: filter.single_use,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.admin_keys.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(
|
||||||
|
'admin_keys',
|
||||||
|
'key_name',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.admin_keys.findAll({
|
||||||
|
attributes: [ 'id', 'key_name' ],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
orderBy: [['key_name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({
|
||||||
|
id: record.id,
|
||||||
|
label: record.key_name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
621
backend/src/db/api/analysis_models.js
Normal file
621
backend/src/db/api/analysis_models.js
Normal file
@ -0,0 +1,621 @@
|
|||||||
|
|
||||||
|
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 Analysis_modelsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const analysis_models = await db.analysis_models.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
name: data.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
model_type: data.model_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
description: data.description
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
hyperparameters_json: data.hyperparameters_json
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
trained_at: data.trained_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
last_evaluated_at: data.last_evaluated_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
quality_score: data.quality_score
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await analysis_models.setGame( data.game || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.analysis_models.getTableName(),
|
||||||
|
belongsToColumn: 'artifact_file',
|
||||||
|
belongsToId: analysis_models.id,
|
||||||
|
},
|
||||||
|
data.artifact_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return analysis_models;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 analysis_modelsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
name: item.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
model_type: item.model_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
description: item.description
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
hyperparameters_json: item.hyperparameters_json
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
trained_at: item.trained_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
last_evaluated_at: item.last_evaluated_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
quality_score: item.quality_score
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const analysis_models = await db.analysis_models.bulkCreate(analysis_modelsData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
for (let i = 0; i < analysis_models.length; i++) {
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.analysis_models.getTableName(),
|
||||||
|
belongsToColumn: 'artifact_file',
|
||||||
|
belongsToId: analysis_models[i].id,
|
||||||
|
},
|
||||||
|
data[i].artifact_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return analysis_models;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const analysis_models = await db.analysis_models.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.model_type !== undefined) updatePayload.model_type = data.model_type;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.description !== undefined) updatePayload.description = data.description;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.hyperparameters_json !== undefined) updatePayload.hyperparameters_json = data.hyperparameters_json;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.trained_at !== undefined) updatePayload.trained_at = data.trained_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.last_evaluated_at !== undefined) updatePayload.last_evaluated_at = data.last_evaluated_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.quality_score !== undefined) updatePayload.quality_score = data.quality_score;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await analysis_models.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.game !== undefined) {
|
||||||
|
await analysis_models.setGame(
|
||||||
|
|
||||||
|
data.game,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.analysis_models.getTableName(),
|
||||||
|
belongsToColumn: 'artifact_file',
|
||||||
|
belongsToId: analysis_models.id,
|
||||||
|
},
|
||||||
|
data.artifact_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return analysis_models;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const analysis_models = await db.analysis_models.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of analysis_models) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of analysis_models) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return analysis_models;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const analysis_models = await db.analysis_models.findByPk(id, options);
|
||||||
|
|
||||||
|
await analysis_models.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await analysis_models.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return analysis_models;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const analysis_models = await db.analysis_models.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!analysis_models) {
|
||||||
|
return analysis_models;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = analysis_models.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.model_runs_model = await analysis_models.getModel_runs_model({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.game = await analysis_models.getGame({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.artifact_file = await analysis_models.getArtifact_file({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.lottery_games,
|
||||||
|
as: 'game',
|
||||||
|
|
||||||
|
where: filter.game ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.game.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.game.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.file,
|
||||||
|
as: 'artifact_file',
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.name) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'analysis_models',
|
||||||
|
'name',
|
||||||
|
filter.name,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.description) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'analysis_models',
|
||||||
|
'description',
|
||||||
|
filter.description,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.hyperparameters_json) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'analysis_models',
|
||||||
|
'hyperparameters_json',
|
||||||
|
filter.hyperparameters_json,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.trained_atRange) {
|
||||||
|
const [start, end] = filter.trained_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
trained_at: {
|
||||||
|
...where.trained_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
trained_at: {
|
||||||
|
...where.trained_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.last_evaluated_atRange) {
|
||||||
|
const [start, end] = filter.last_evaluated_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
last_evaluated_at: {
|
||||||
|
...where.last_evaluated_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
last_evaluated_at: {
|
||||||
|
...where.last_evaluated_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.quality_scoreRange) {
|
||||||
|
const [start, end] = filter.quality_scoreRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
quality_score: {
|
||||||
|
...where.quality_score,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
quality_score: {
|
||||||
|
...where.quality_score,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.model_type) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
model_type: filter.model_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.analysis_models.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(
|
||||||
|
'analysis_models',
|
||||||
|
'name',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.analysis_models.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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
413
backend/src/db/api/countries.js
Normal file
413
backend/src/db/api/countries.js
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
|
||||||
|
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 CountriesDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const countries = await db.countries.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
name: data.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
iso_code: data.iso_code
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
currency: data.currency
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
is_active: data.is_active
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 countriesData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
name: item.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
iso_code: item.iso_code
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
currency: item.currency
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
is_active: item.is_active
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const countries = await db.countries.bulkCreate(countriesData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const countries = await db.countries.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.iso_code !== undefined) updatePayload.iso_code = data.iso_code;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.currency !== undefined) updatePayload.currency = data.currency;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.is_active !== undefined) updatePayload.is_active = data.is_active;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await countries.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const countries = await db.countries.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of countries) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of countries) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const countries = await db.countries.findByPk(id, options);
|
||||||
|
|
||||||
|
await countries.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await countries.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const countries = await db.countries.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!countries) {
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = countries.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.lottery_games_country = await countries.getLottery_games_country({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
'countries',
|
||||||
|
'name',
|
||||||
|
filter.name,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.iso_code) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'countries',
|
||||||
|
'iso_code',
|
||||||
|
filter.iso_code,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.currency) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'countries',
|
||||||
|
'currency',
|
||||||
|
filter.currency,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.is_active) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
is_active: filter.is_active,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.countries.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(
|
||||||
|
'countries',
|
||||||
|
'name',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.countries.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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
771
backend/src/db/api/draws.js
Normal file
771
backend/src/db/api/draws.js
Normal file
@ -0,0 +1,771 @@
|
|||||||
|
|
||||||
|
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 DrawsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const draws = await db.draws.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
draw_number: data.draw_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
scheduled_at: data.scheduled_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
result_published_at: data.result_published_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
winning_numbers: data.winning_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_numbers: data.bonus_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
jackpot_amount: data.jackpot_amount
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
prize_pool_amount: data.prize_pool_amount
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
source_url: data.source_url
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
import_batch_tag: data.import_batch_tag
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
is_verified: data.is_verified
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
admin_notes: data.admin_notes
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await draws.setGame( data.game || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.draws.getTableName(),
|
||||||
|
belongsToColumn: 'source_file',
|
||||||
|
belongsToId: draws.id,
|
||||||
|
},
|
||||||
|
data.source_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return draws;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 drawsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
draw_number: item.draw_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
scheduled_at: item.scheduled_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
result_published_at: item.result_published_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
winning_numbers: item.winning_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_numbers: item.bonus_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
jackpot_amount: item.jackpot_amount
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
prize_pool_amount: item.prize_pool_amount
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
source_url: item.source_url
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
import_batch_tag: item.import_batch_tag
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
is_verified: item.is_verified
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
admin_notes: item.admin_notes
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const draws = await db.draws.bulkCreate(drawsData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
for (let i = 0; i < draws.length; i++) {
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.draws.getTableName(),
|
||||||
|
belongsToColumn: 'source_file',
|
||||||
|
belongsToId: draws[i].id,
|
||||||
|
},
|
||||||
|
data[i].source_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return draws;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const draws = await db.draws.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.draw_number !== undefined) updatePayload.draw_number = data.draw_number;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.scheduled_at !== undefined) updatePayload.scheduled_at = data.scheduled_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.result_published_at !== undefined) updatePayload.result_published_at = data.result_published_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.winning_numbers !== undefined) updatePayload.winning_numbers = data.winning_numbers;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.bonus_numbers !== undefined) updatePayload.bonus_numbers = data.bonus_numbers;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.jackpot_amount !== undefined) updatePayload.jackpot_amount = data.jackpot_amount;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.prize_pool_amount !== undefined) updatePayload.prize_pool_amount = data.prize_pool_amount;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.source_url !== undefined) updatePayload.source_url = data.source_url;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.import_batch_tag !== undefined) updatePayload.import_batch_tag = data.import_batch_tag;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.is_verified !== undefined) updatePayload.is_verified = data.is_verified;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.admin_notes !== undefined) updatePayload.admin_notes = data.admin_notes;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await draws.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.game !== undefined) {
|
||||||
|
await draws.setGame(
|
||||||
|
|
||||||
|
data.game,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.draws.getTableName(),
|
||||||
|
belongsToColumn: 'source_file',
|
||||||
|
belongsToId: draws.id,
|
||||||
|
},
|
||||||
|
data.source_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return draws;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const draws = await db.draws.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of draws) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of draws) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return draws;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const draws = await db.draws.findByPk(id, options);
|
||||||
|
|
||||||
|
await draws.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await draws.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return draws;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const draws = await db.draws.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!draws) {
|
||||||
|
return draws;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = draws.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.number_sets_target_draw = await draws.getNumber_sets_target_draw({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.generation_jobs_target_draw = await draws.getGeneration_jobs_target_draw({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.model_runs_target_draw = await draws.getModel_runs_target_draw({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.game = await draws.getGame({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.source_file = await draws.getSource_file({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.lottery_games,
|
||||||
|
as: 'game',
|
||||||
|
|
||||||
|
where: filter.game ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.game.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.game.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.file,
|
||||||
|
as: 'source_file',
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.winning_numbers) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'draws',
|
||||||
|
'winning_numbers',
|
||||||
|
filter.winning_numbers,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.bonus_numbers) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'draws',
|
||||||
|
'bonus_numbers',
|
||||||
|
filter.bonus_numbers,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.source_url) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'draws',
|
||||||
|
'source_url',
|
||||||
|
filter.source_url,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.import_batch_tag) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'draws',
|
||||||
|
'import_batch_tag',
|
||||||
|
filter.import_batch_tag,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.admin_notes) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'draws',
|
||||||
|
'admin_notes',
|
||||||
|
filter.admin_notes,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.calendarStart && filter.calendarEnd) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.or]: [
|
||||||
|
{
|
||||||
|
scheduled_at: {
|
||||||
|
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
result_published_at: {
|
||||||
|
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.draw_numberRange) {
|
||||||
|
const [start, end] = filter.draw_numberRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
draw_number: {
|
||||||
|
...where.draw_number,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
draw_number: {
|
||||||
|
...where.draw_number,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.scheduled_atRange) {
|
||||||
|
const [start, end] = filter.scheduled_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
scheduled_at: {
|
||||||
|
...where.scheduled_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
scheduled_at: {
|
||||||
|
...where.scheduled_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.result_published_atRange) {
|
||||||
|
const [start, end] = filter.result_published_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
result_published_at: {
|
||||||
|
...where.result_published_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
result_published_at: {
|
||||||
|
...where.result_published_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.jackpot_amountRange) {
|
||||||
|
const [start, end] = filter.jackpot_amountRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
jackpot_amount: {
|
||||||
|
...where.jackpot_amount,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
jackpot_amount: {
|
||||||
|
...where.jackpot_amount,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.prize_pool_amountRange) {
|
||||||
|
const [start, end] = filter.prize_pool_amountRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
prize_pool_amount: {
|
||||||
|
...where.prize_pool_amount,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
prize_pool_amount: {
|
||||||
|
...where.prize_pool_amount,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.is_verified) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
is_verified: filter.is_verified,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.draws.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(
|
||||||
|
'draws',
|
||||||
|
'winning_numbers',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.draws.findAll({
|
||||||
|
attributes: [ 'id', 'winning_numbers' ],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
orderBy: [['winning_numbers', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({
|
||||||
|
id: record.id,
|
||||||
|
label: record.winning_numbers,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
87
backend/src/db/api/file.js
Normal file
87
backend/src/db/api/file.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
667
backend/src/db/api/generation_jobs.js
Normal file
667
backend/src/db/api/generation_jobs.js
Normal file
@ -0,0 +1,667 @@
|
|||||||
|
|
||||||
|
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 Generation_jobsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const generation_jobs = await db.generation_jobs.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
job_type: data.job_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
started_at: data.started_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
finished_at: data.finished_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
requested_count: data.requested_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
produced_count: data.produced_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
parameters_json: data.parameters_json
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
error_message: data.error_message
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await generation_jobs.setGame( data.game || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await generation_jobs.setTarget_draw( data.target_draw || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await generation_jobs.setProduced_number_sets(data.produced_number_sets || [], {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return generation_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 generation_jobsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
job_type: item.job_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
started_at: item.started_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
finished_at: item.finished_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
requested_count: item.requested_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
produced_count: item.produced_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
parameters_json: item.parameters_json
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
error_message: item.error_message
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const generation_jobs = await db.generation_jobs.bulkCreate(generation_jobsData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
|
return generation_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const generation_jobs = await db.generation_jobs.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.job_type !== undefined) updatePayload.job_type = data.job_type;
|
||||||
|
|
||||||
|
|
||||||
|
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.requested_count !== undefined) updatePayload.requested_count = data.requested_count;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.produced_count !== undefined) updatePayload.produced_count = data.produced_count;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.parameters_json !== undefined) updatePayload.parameters_json = data.parameters_json;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.error_message !== undefined) updatePayload.error_message = data.error_message;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await generation_jobs.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.game !== undefined) {
|
||||||
|
await generation_jobs.setGame(
|
||||||
|
|
||||||
|
data.game,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.target_draw !== undefined) {
|
||||||
|
await generation_jobs.setTarget_draw(
|
||||||
|
|
||||||
|
data.target_draw,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.produced_number_sets !== undefined) {
|
||||||
|
await generation_jobs.setProduced_number_sets(data.produced_number_sets, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return generation_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const generation_jobs = await db.generation_jobs.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of generation_jobs) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of generation_jobs) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return generation_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const generation_jobs = await db.generation_jobs.findByPk(id, options);
|
||||||
|
|
||||||
|
await generation_jobs.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await generation_jobs.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return generation_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const generation_jobs = await db.generation_jobs.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!generation_jobs) {
|
||||||
|
return generation_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = generation_jobs.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.game = await generation_jobs.getGame({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.target_draw = await generation_jobs.getTarget_draw({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.produced_number_sets = await generation_jobs.getProduced_number_sets({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.lottery_games,
|
||||||
|
as: 'game',
|
||||||
|
|
||||||
|
where: filter.game ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.game.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.game.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.draws,
|
||||||
|
as: 'target_draw',
|
||||||
|
|
||||||
|
where: filter.target_draw ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.target_draw.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
winning_numbers: {
|
||||||
|
[Op.or]: filter.target_draw.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.number_sets,
|
||||||
|
as: 'produced_number_sets',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.parameters_json) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'generation_jobs',
|
||||||
|
'parameters_json',
|
||||||
|
filter.parameters_json,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.error_message) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'generation_jobs',
|
||||||
|
'error_message',
|
||||||
|
filter.error_message,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.requested_countRange) {
|
||||||
|
const [start, end] = filter.requested_countRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
requested_count: {
|
||||||
|
...where.requested_count,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
requested_count: {
|
||||||
|
...where.requested_count,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.produced_countRange) {
|
||||||
|
const [start, end] = filter.produced_countRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
produced_count: {
|
||||||
|
...where.produced_count,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
produced_count: {
|
||||||
|
...where.produced_count,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.job_type) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
job_type: filter.job_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.produced_number_sets) {
|
||||||
|
const searchTerms = filter.produced_number_sets.split('|');
|
||||||
|
|
||||||
|
include = [
|
||||||
|
{
|
||||||
|
model: db.number_sets,
|
||||||
|
as: 'produced_number_sets_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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.generation_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, ) {
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where = {
|
||||||
|
[Op.or]: [
|
||||||
|
{ ['id']: Utils.uuid(query) },
|
||||||
|
Utils.ilike(
|
||||||
|
'generation_jobs',
|
||||||
|
'job_type',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.generation_jobs.findAll({
|
||||||
|
attributes: [ 'id', 'job_type' ],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
orderBy: [['job_type', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({
|
||||||
|
id: record.id,
|
||||||
|
label: record.job_type,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
704
backend/src/db/api/imports.js
Normal file
704
backend/src/db/api/imports.js
Normal file
@ -0,0 +1,704 @@
|
|||||||
|
|
||||||
|
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 ImportsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const imports = await db.imports.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
import_type: data.import_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
started_at: data.started_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
finished_at: data.finished_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
source_url: data.source_url
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_read: data.records_read
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_created: data.records_created
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_updated: data.records_updated
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_failed: data.records_failed
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
log: data.log
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await imports.setGame( data.game || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.imports.getTableName(),
|
||||||
|
belongsToColumn: 'input_file',
|
||||||
|
belongsToId: imports.id,
|
||||||
|
},
|
||||||
|
data.input_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 importsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
import_type: item.import_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
started_at: item.started_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
finished_at: item.finished_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
source_url: item.source_url
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_read: item.records_read
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_created: item.records_created
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_updated: item.records_updated
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
records_failed: item.records_failed
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
log: item.log
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const imports = await db.imports.bulkCreate(importsData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
for (let i = 0; i < imports.length; i++) {
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.imports.getTableName(),
|
||||||
|
belongsToColumn: 'input_file',
|
||||||
|
belongsToId: imports[i].id,
|
||||||
|
},
|
||||||
|
data[i].input_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const imports = await db.imports.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.import_type !== undefined) updatePayload.import_type = data.import_type;
|
||||||
|
|
||||||
|
|
||||||
|
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.source_url !== undefined) updatePayload.source_url = data.source_url;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.records_read !== undefined) updatePayload.records_read = data.records_read;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.records_created !== undefined) updatePayload.records_created = data.records_created;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.records_updated !== undefined) updatePayload.records_updated = data.records_updated;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.records_failed !== undefined) updatePayload.records_failed = data.records_failed;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.log !== undefined) updatePayload.log = data.log;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await imports.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.game !== undefined) {
|
||||||
|
await imports.setGame(
|
||||||
|
|
||||||
|
data.game,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.imports.getTableName(),
|
||||||
|
belongsToColumn: 'input_file',
|
||||||
|
belongsToId: imports.id,
|
||||||
|
},
|
||||||
|
data.input_file,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const imports = await db.imports.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of imports) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of imports) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const imports = await db.imports.findByPk(id, options);
|
||||||
|
|
||||||
|
await imports.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await imports.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const imports = await db.imports.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!imports) {
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = imports.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.game = await imports.getGame({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.input_file = await imports.getInput_file({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.lottery_games,
|
||||||
|
as: 'game',
|
||||||
|
|
||||||
|
where: filter.game ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.game.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.game.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.file,
|
||||||
|
as: 'input_file',
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.source_url) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'imports',
|
||||||
|
'source_url',
|
||||||
|
filter.source_url,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.log) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'imports',
|
||||||
|
'log',
|
||||||
|
filter.log,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.records_readRange) {
|
||||||
|
const [start, end] = filter.records_readRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_read: {
|
||||||
|
...where.records_read,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_read: {
|
||||||
|
...where.records_read,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.records_createdRange) {
|
||||||
|
const [start, end] = filter.records_createdRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_created: {
|
||||||
|
...where.records_created,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_created: {
|
||||||
|
...where.records_created,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.records_updatedRange) {
|
||||||
|
const [start, end] = filter.records_updatedRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_updated: {
|
||||||
|
...where.records_updated,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_updated: {
|
||||||
|
...where.records_updated,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.records_failedRange) {
|
||||||
|
const [start, end] = filter.records_failedRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_failed: {
|
||||||
|
...where.records_failed,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
records_failed: {
|
||||||
|
...where.records_failed,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.import_type) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
import_type: filter.import_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.imports.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(
|
||||||
|
'imports',
|
||||||
|
'import_type',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.imports.findAll({
|
||||||
|
attributes: [ 'id', 'import_type' ],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
orderBy: [['import_type', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({
|
||||||
|
id: record.id,
|
||||||
|
label: record.import_type,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
874
backend/src/db/api/lottery_games.js
Normal file
874
backend/src/db/api/lottery_games.js
Normal file
@ -0,0 +1,874 @@
|
|||||||
|
|
||||||
|
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 Lottery_gamesDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const lottery_games = await db.lottery_games.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
name: data.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
slug: data.slug
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
draw_schedule_type: data.draw_schedule_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
draw_days: data.draw_days
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
timezone: data.timezone
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
numbers_per_ticket: data.numbers_per_ticket
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
min_number: data.min_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
max_number: data.max_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_numbers_count: data.bonus_numbers_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_min_number: data.bonus_min_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_max_number: data.bonus_max_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
future_draws_target: data.future_draws_target
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
first_draw_at: data.first_draw_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
notes: data.notes
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await lottery_games.setCountry( data.country || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.lottery_games.getTableName(),
|
||||||
|
belongsToColumn: 'rules_document',
|
||||||
|
belongsToId: lottery_games.id,
|
||||||
|
},
|
||||||
|
data.rules_document,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return lottery_games;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 lottery_gamesData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
name: item.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
slug: item.slug
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
draw_schedule_type: item.draw_schedule_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
draw_days: item.draw_days
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
timezone: item.timezone
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
numbers_per_ticket: item.numbers_per_ticket
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
min_number: item.min_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
max_number: item.max_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_numbers_count: item.bonus_numbers_count
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_min_number: item.bonus_min_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_max_number: item.bonus_max_number
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
future_draws_target: item.future_draws_target
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
first_draw_at: item.first_draw_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
notes: item.notes
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const lottery_games = await db.lottery_games.bulkCreate(lottery_gamesData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
for (let i = 0; i < lottery_games.length; i++) {
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.lottery_games.getTableName(),
|
||||||
|
belongsToColumn: 'rules_document',
|
||||||
|
belongsToId: lottery_games[i].id,
|
||||||
|
},
|
||||||
|
data[i].rules_document,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return lottery_games;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const lottery_games = await db.lottery_games.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.slug !== undefined) updatePayload.slug = data.slug;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.draw_schedule_type !== undefined) updatePayload.draw_schedule_type = data.draw_schedule_type;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.draw_days !== undefined) updatePayload.draw_days = data.draw_days;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.timezone !== undefined) updatePayload.timezone = data.timezone;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.numbers_per_ticket !== undefined) updatePayload.numbers_per_ticket = data.numbers_per_ticket;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.min_number !== undefined) updatePayload.min_number = data.min_number;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.max_number !== undefined) updatePayload.max_number = data.max_number;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.bonus_numbers_count !== undefined) updatePayload.bonus_numbers_count = data.bonus_numbers_count;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.bonus_min_number !== undefined) updatePayload.bonus_min_number = data.bonus_min_number;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.bonus_max_number !== undefined) updatePayload.bonus_max_number = data.bonus_max_number;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.future_draws_target !== undefined) updatePayload.future_draws_target = data.future_draws_target;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.first_draw_at !== undefined) updatePayload.first_draw_at = data.first_draw_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.notes !== undefined) updatePayload.notes = data.notes;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await lottery_games.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.country !== undefined) {
|
||||||
|
await lottery_games.setCountry(
|
||||||
|
|
||||||
|
data.country,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await FileDBApi.replaceRelationFiles(
|
||||||
|
{
|
||||||
|
belongsTo: db.lottery_games.getTableName(),
|
||||||
|
belongsToColumn: 'rules_document',
|
||||||
|
belongsToId: lottery_games.id,
|
||||||
|
},
|
||||||
|
data.rules_document,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return lottery_games;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const lottery_games = await db.lottery_games.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of lottery_games) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of lottery_games) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return lottery_games;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const lottery_games = await db.lottery_games.findByPk(id, options);
|
||||||
|
|
||||||
|
await lottery_games.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await lottery_games.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return lottery_games;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const lottery_games = await db.lottery_games.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!lottery_games) {
|
||||||
|
return lottery_games;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = lottery_games.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.draws_game = await lottery_games.getDraws_game({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.number_sets_game = await lottery_games.getNumber_sets_game({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.generation_jobs_game = await lottery_games.getGeneration_jobs_game({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.analysis_models_game = await lottery_games.getAnalysis_models_game({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.model_runs_game = await lottery_games.getModel_runs_game({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.imports_game = await lottery_games.getImports_game({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.country = await lottery_games.getCountry({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.rules_document = await lottery_games.getRules_document({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.countries,
|
||||||
|
as: 'country',
|
||||||
|
|
||||||
|
where: filter.country ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.country.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.country.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.file,
|
||||||
|
as: 'rules_document',
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.name) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'lottery_games',
|
||||||
|
'name',
|
||||||
|
filter.name,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.slug) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'lottery_games',
|
||||||
|
'slug',
|
||||||
|
filter.slug,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.draw_days) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'lottery_games',
|
||||||
|
'draw_days',
|
||||||
|
filter.draw_days,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.timezone) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'lottery_games',
|
||||||
|
'timezone',
|
||||||
|
filter.timezone,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.notes) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'lottery_games',
|
||||||
|
'notes',
|
||||||
|
filter.notes,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.numbers_per_ticketRange) {
|
||||||
|
const [start, end] = filter.numbers_per_ticketRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
numbers_per_ticket: {
|
||||||
|
...where.numbers_per_ticket,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
numbers_per_ticket: {
|
||||||
|
...where.numbers_per_ticket,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.min_numberRange) {
|
||||||
|
const [start, end] = filter.min_numberRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
min_number: {
|
||||||
|
...where.min_number,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
min_number: {
|
||||||
|
...where.min_number,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.max_numberRange) {
|
||||||
|
const [start, end] = filter.max_numberRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
max_number: {
|
||||||
|
...where.max_number,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
max_number: {
|
||||||
|
...where.max_number,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.bonus_numbers_countRange) {
|
||||||
|
const [start, end] = filter.bonus_numbers_countRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
bonus_numbers_count: {
|
||||||
|
...where.bonus_numbers_count,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
bonus_numbers_count: {
|
||||||
|
...where.bonus_numbers_count,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.bonus_min_numberRange) {
|
||||||
|
const [start, end] = filter.bonus_min_numberRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
bonus_min_number: {
|
||||||
|
...where.bonus_min_number,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
bonus_min_number: {
|
||||||
|
...where.bonus_min_number,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.bonus_max_numberRange) {
|
||||||
|
const [start, end] = filter.bonus_max_numberRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
bonus_max_number: {
|
||||||
|
...where.bonus_max_number,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
bonus_max_number: {
|
||||||
|
...where.bonus_max_number,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.future_draws_targetRange) {
|
||||||
|
const [start, end] = filter.future_draws_targetRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
future_draws_target: {
|
||||||
|
...where.future_draws_target,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
future_draws_target: {
|
||||||
|
...where.future_draws_target,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.first_draw_atRange) {
|
||||||
|
const [start, end] = filter.first_draw_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
first_draw_at: {
|
||||||
|
...where.first_draw_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
first_draw_at: {
|
||||||
|
...where.first_draw_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.draw_schedule_type) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
draw_schedule_type: filter.draw_schedule_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.lottery_games.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(
|
||||||
|
'lottery_games',
|
||||||
|
'name',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.lottery_games.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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
654
backend/src/db/api/model_runs.js
Normal file
654
backend/src/db/api/model_runs.js
Normal file
@ -0,0 +1,654 @@
|
|||||||
|
|
||||||
|
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 Model_runsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const model_runs = await db.model_runs.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
run_type: data.run_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
started_at: data.started_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
finished_at: data.finished_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
input_window: data.input_window
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
metrics_json: data.metrics_json
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
output_summary: data.output_summary
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await model_runs.setModel( data.model || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await model_runs.setGame( data.game || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await model_runs.setTarget_draw( data.target_draw || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await model_runs.setSuggested_number_sets(data.suggested_number_sets || [], {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return model_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 model_runsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
run_type: item.run_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
started_at: item.started_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
finished_at: item.finished_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
input_window: item.input_window
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
metrics_json: item.metrics_json
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
output_summary: item.output_summary
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const model_runs = await db.model_runs.bulkCreate(model_runsData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
|
return model_runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const model_runs = await db.model_runs.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.run_type !== undefined) updatePayload.run_type = data.run_type;
|
||||||
|
|
||||||
|
|
||||||
|
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.input_window !== undefined) updatePayload.input_window = data.input_window;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.metrics_json !== undefined) updatePayload.metrics_json = data.metrics_json;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.output_summary !== undefined) updatePayload.output_summary = data.output_summary;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await model_runs.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.model !== undefined) {
|
||||||
|
await model_runs.setModel(
|
||||||
|
|
||||||
|
data.model,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.game !== undefined) {
|
||||||
|
await model_runs.setGame(
|
||||||
|
|
||||||
|
data.game,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.target_draw !== undefined) {
|
||||||
|
await model_runs.setTarget_draw(
|
||||||
|
|
||||||
|
data.target_draw,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.suggested_number_sets !== undefined) {
|
||||||
|
await model_runs.setSuggested_number_sets(data.suggested_number_sets, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return model_runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const model_runs = await db.model_runs.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of model_runs) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of model_runs) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return model_runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const model_runs = await db.model_runs.findByPk(id, options);
|
||||||
|
|
||||||
|
await model_runs.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await model_runs.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return model_runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const model_runs = await db.model_runs.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!model_runs) {
|
||||||
|
return model_runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = model_runs.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.model = await model_runs.getModel({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.game = await model_runs.getGame({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.target_draw = await model_runs.getTarget_draw({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.suggested_number_sets = await model_runs.getSuggested_number_sets({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.analysis_models,
|
||||||
|
as: 'model',
|
||||||
|
|
||||||
|
where: filter.model ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.model.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.model.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.lottery_games,
|
||||||
|
as: 'game',
|
||||||
|
|
||||||
|
where: filter.game ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.game.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.game.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.draws,
|
||||||
|
as: 'target_draw',
|
||||||
|
|
||||||
|
where: filter.target_draw ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.target_draw.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
winning_numbers: {
|
||||||
|
[Op.or]: filter.target_draw.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.number_sets,
|
||||||
|
as: 'suggested_number_sets',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.input_window) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'model_runs',
|
||||||
|
'input_window',
|
||||||
|
filter.input_window,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.metrics_json) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'model_runs',
|
||||||
|
'metrics_json',
|
||||||
|
filter.metrics_json,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.output_summary) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'model_runs',
|
||||||
|
'output_summary',
|
||||||
|
filter.output_summary,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.run_type) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
run_type: filter.run_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.suggested_number_sets) {
|
||||||
|
const searchTerms = filter.suggested_number_sets.split('|');
|
||||||
|
|
||||||
|
include = [
|
||||||
|
{
|
||||||
|
model: db.number_sets,
|
||||||
|
as: 'suggested_number_sets_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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.model_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, ) {
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where = {
|
||||||
|
[Op.or]: [
|
||||||
|
{ ['id']: Utils.uuid(query) },
|
||||||
|
Utils.ilike(
|
||||||
|
'model_runs',
|
||||||
|
'run_type',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.model_runs.findAll({
|
||||||
|
attributes: [ 'id', 'run_type' ],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
orderBy: [['run_type', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({
|
||||||
|
id: record.id,
|
||||||
|
label: record.run_type,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
660
backend/src/db/api/number_sets.js
Normal file
660
backend/src/db/api/number_sets.js
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
|
||||||
|
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 Number_setsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const number_sets = await db.number_sets.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
name: data.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
set_type: data.set_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
main_numbers: data.main_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_numbers: data.bonus_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
size: data.size
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
generated_at: data.generated_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
generation_method: data.generation_method
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
constraints_summary: data.constraints_summary
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
score: data.score
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await number_sets.setGame( data.game || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await number_sets.setTarget_draw( data.target_draw || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return number_sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 number_setsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
name: item.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
set_type: item.set_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
main_numbers: item.main_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
bonus_numbers: item.bonus_numbers
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
size: item.size
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
generated_at: item.generated_at
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
generation_method: item.generation_method
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
constraints_summary: item.constraints_summary
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
score: item.score
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const number_sets = await db.number_sets.bulkCreate(number_setsData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
|
return number_sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const number_sets = await db.number_sets.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.set_type !== undefined) updatePayload.set_type = data.set_type;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.main_numbers !== undefined) updatePayload.main_numbers = data.main_numbers;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.bonus_numbers !== undefined) updatePayload.bonus_numbers = data.bonus_numbers;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.size !== undefined) updatePayload.size = data.size;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.generated_at !== undefined) updatePayload.generated_at = data.generated_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.generation_method !== undefined) updatePayload.generation_method = data.generation_method;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.constraints_summary !== undefined) updatePayload.constraints_summary = data.constraints_summary;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.score !== undefined) updatePayload.score = data.score;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await number_sets.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.game !== undefined) {
|
||||||
|
await number_sets.setGame(
|
||||||
|
|
||||||
|
data.game,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.target_draw !== undefined) {
|
||||||
|
await number_sets.setTarget_draw(
|
||||||
|
|
||||||
|
data.target_draw,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return number_sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const number_sets = await db.number_sets.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of number_sets) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of number_sets) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return number_sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const number_sets = await db.number_sets.findByPk(id, options);
|
||||||
|
|
||||||
|
await number_sets.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await number_sets.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return number_sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const number_sets = await db.number_sets.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!number_sets) {
|
||||||
|
return number_sets;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = number_sets.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.game = await number_sets.getGame({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.target_draw = await number_sets.getTarget_draw({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
offset = currentPage * limit;
|
||||||
|
|
||||||
|
const orderBy = null;
|
||||||
|
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.lottery_games,
|
||||||
|
as: 'game',
|
||||||
|
|
||||||
|
where: filter.game ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.game.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.game.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.draws,
|
||||||
|
as: 'target_draw',
|
||||||
|
|
||||||
|
where: filter.target_draw ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.target_draw.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
winning_numbers: {
|
||||||
|
[Op.or]: filter.target_draw.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
['id']: Utils.uuid(filter.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.name) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'number_sets',
|
||||||
|
'name',
|
||||||
|
filter.name,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.main_numbers) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'number_sets',
|
||||||
|
'main_numbers',
|
||||||
|
filter.main_numbers,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.bonus_numbers) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'number_sets',
|
||||||
|
'bonus_numbers',
|
||||||
|
filter.bonus_numbers,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.generation_method) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'number_sets',
|
||||||
|
'generation_method',
|
||||||
|
filter.generation_method,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.constraints_summary) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'number_sets',
|
||||||
|
'constraints_summary',
|
||||||
|
filter.constraints_summary,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.sizeRange) {
|
||||||
|
const [start, end] = filter.sizeRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
size: {
|
||||||
|
...where.size,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
size: {
|
||||||
|
...where.size,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.generated_atRange) {
|
||||||
|
const [start, end] = filter.generated_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
generated_at: {
|
||||||
|
...where.generated_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
generated_at: {
|
||||||
|
...where.generated_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.scoreRange) {
|
||||||
|
const [start, end] = filter.scoreRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
score: {
|
||||||
|
...where.score,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
score: {
|
||||||
|
...where.score,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.set_type) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
set_type: filter.set_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.number_sets.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(
|
||||||
|
'number_sets',
|
||||||
|
'name',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.number_sets.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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
339
backend/src/db/api/permissions.js
Normal file
339
backend/src/db/api/permissions.js
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
409
backend/src/db/api/roles.js
Normal file
409
backend/src/db/api/roles.js
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
|
||||||
|
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 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
|
||||||
|
,
|
||||||
|
|
||||||
|
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
|
||||||
|
,
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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, ) {
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
429
backend/src/db/api/system_settings.js
Normal file
429
backend/src/db/api/system_settings.js
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
|
||||||
|
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 System_settingsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const system_settings = await db.system_settings.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
setting_key: data.setting_key
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
setting_value: data.setting_value
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
value_type: data.value_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
description: data.description
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
is_secret: data.is_secret
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return system_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 system_settingsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
setting_key: item.setting_key
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
setting_value: item.setting_value
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
value_type: item.value_type
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
description: item.description
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
is_secret: item.is_secret
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bulk create items
|
||||||
|
const system_settings = await db.system_settings.bulkCreate(system_settingsData, { transaction });
|
||||||
|
|
||||||
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
|
return system_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
|
const system_settings = await db.system_settings.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.setting_key !== undefined) updatePayload.setting_key = data.setting_key;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.setting_value !== undefined) updatePayload.setting_value = data.setting_value;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.value_type !== undefined) updatePayload.value_type = data.value_type;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.description !== undefined) updatePayload.description = data.description;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.is_secret !== undefined) updatePayload.is_secret = data.is_secret;
|
||||||
|
|
||||||
|
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await system_settings.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return system_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const system_settings = await db.system_settings.findAll({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
[Op.in]: ids,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
|
for (const record of system_settings) {
|
||||||
|
await record.update(
|
||||||
|
{deletedBy: currentUser.id},
|
||||||
|
{transaction}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const record of system_settings) {
|
||||||
|
await record.destroy({transaction});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return system_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const system_settings = await db.system_settings.findByPk(id, options);
|
||||||
|
|
||||||
|
await system_settings.update({
|
||||||
|
deletedBy: currentUser.id
|
||||||
|
}, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await system_settings.destroy({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
return system_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const system_settings = await db.system_settings.findOne(
|
||||||
|
{ where },
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!system_settings) {
|
||||||
|
return system_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = system_settings.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.setting_key) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'system_settings',
|
||||||
|
'setting_key',
|
||||||
|
filter.setting_key,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.setting_value) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'system_settings',
|
||||||
|
'setting_value',
|
||||||
|
filter.setting_value,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.description) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'system_settings',
|
||||||
|
'description',
|
||||||
|
filter.description,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
active: filter.active === true || filter.active === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.value_type) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
value_type: filter.value_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.is_secret) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
is_secret: filter.is_secret,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.system_settings.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(
|
||||||
|
'system_settings',
|
||||||
|
'setting_key',
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.system_settings.findAll({
|
||||||
|
attributes: [ 'id', 'setting_key' ],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
orderBy: [['setting_key', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({
|
||||||
|
id: record.id,
|
||||||
|
label: record.setting_key,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
941
backend/src/db/api/users.js
Normal file
941
backend/src/db/api/users.js
Normal file
@ -0,0 +1,941 @@
|
|||||||
|
|
||||||
|
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, 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.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, 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.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.admin_keys_issued_for_user = await users.getAdmin_keys_issued_for_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
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(
|
||||||
|
filter,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
let offset = 0;
|
||||||
|
let where = {};
|
||||||
|
const currentPage = +filter.page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.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.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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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, ) {
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
},
|
||||||
|
{ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
33
backend/src/db/db.config.js
Normal file
33
backend/src/db/db.config.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
|
||||||
|
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_gerador_de_loterias_admin',
|
||||||
|
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',
|
||||||
|
}
|
||||||
|
};
|
||||||
3302
backend/src/db/migrations/1772749336617.js
Normal file
3302
backend/src/db/migrations/1772749336617.js
Normal file
File diff suppressed because it is too large
Load Diff
147
backend/src/db/models/admin_keys.js
Normal file
147
backend/src/db/models/admin_keys.js
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
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 admin_keys = sequelize.define(
|
||||||
|
'admin_keys',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
key_name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
key_fingerprint: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"active",
|
||||||
|
|
||||||
|
|
||||||
|
"revoked"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
issued_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
expires_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
single_use: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
max_uses: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
uses_count: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
admin_keys.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.admin_keys.belongsTo(db.users, {
|
||||||
|
as: 'issued_for_user',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'issued_for_userId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.admin_keys.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.admin_keys.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return admin_keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
183
backend/src/db/models/analysis_models.js
Normal file
183
backend/src/db/models/analysis_models.js
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
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 analysis_models = sequelize.define(
|
||||||
|
'analysis_models',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
model_type: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"frequency",
|
||||||
|
|
||||||
|
|
||||||
|
"delay",
|
||||||
|
|
||||||
|
|
||||||
|
"markov_chain",
|
||||||
|
|
||||||
|
|
||||||
|
"monte_carlo",
|
||||||
|
|
||||||
|
|
||||||
|
"hybrid"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"draft",
|
||||||
|
|
||||||
|
|
||||||
|
"active",
|
||||||
|
|
||||||
|
|
||||||
|
"retired"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
description: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
hyperparameters_json: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
trained_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
last_evaluated_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
quality_score: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
analysis_models.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.analysis_models.hasMany(db.model_runs, {
|
||||||
|
as: 'model_runs_model',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'modelId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.analysis_models.belongsTo(db.lottery_games, {
|
||||||
|
as: 'game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.analysis_models.hasMany(db.file, {
|
||||||
|
as: 'artifact_file',
|
||||||
|
foreignKey: 'belongsToId',
|
||||||
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
belongsTo: db.analysis_models.getTableName(),
|
||||||
|
belongsToColumn: 'artifact_file',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.analysis_models.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.analysis_models.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return analysis_models;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
110
backend/src/db/models/countries.js
Normal file
110
backend/src/db/models/countries.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
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 countries = sequelize.define(
|
||||||
|
'countries',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
iso_code: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
currency: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
is_active: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
countries.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.countries.hasMany(db.lottery_games, {
|
||||||
|
as: 'lottery_games_country',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'countryId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.countries.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.countries.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return countries;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
215
backend/src/db/models/draws.js
Normal file
215
backend/src/db/models/draws.js
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
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 draws = sequelize.define(
|
||||||
|
'draws',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
draw_number: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
scheduled_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
result_published_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"placeholder",
|
||||||
|
|
||||||
|
|
||||||
|
"scheduled",
|
||||||
|
|
||||||
|
|
||||||
|
"completed",
|
||||||
|
|
||||||
|
|
||||||
|
"cancelled"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
winning_numbers: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
bonus_numbers: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
jackpot_amount: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
prize_pool_amount: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
source_url: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
import_batch_tag: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
is_verified: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
admin_notes: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
draws.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.draws.hasMany(db.number_sets, {
|
||||||
|
as: 'number_sets_target_draw',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'target_drawId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.draws.hasMany(db.generation_jobs, {
|
||||||
|
as: 'generation_jobs_target_draw',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'target_drawId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.draws.hasMany(db.model_runs, {
|
||||||
|
as: 'model_runs_target_draw',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'target_drawId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.draws.belongsTo(db.lottery_games, {
|
||||||
|
as: 'game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.draws.hasMany(db.file, {
|
||||||
|
as: 'source_file',
|
||||||
|
foreignKey: 'belongsToId',
|
||||||
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
belongsTo: db.draws.getTableName(),
|
||||||
|
belongsToColumn: 'source_file',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.draws.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.draws.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return draws;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
};
|
||||||
191
backend/src/db/models/generation_jobs.js
Normal file
191
backend/src/db/models/generation_jobs.js
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
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 generation_jobs = sequelize.define(
|
||||||
|
'generation_jobs',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
job_type: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"generate_combinations",
|
||||||
|
|
||||||
|
|
||||||
|
"simulate",
|
||||||
|
|
||||||
|
|
||||||
|
"forecast"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"queued",
|
||||||
|
|
||||||
|
|
||||||
|
"running",
|
||||||
|
|
||||||
|
|
||||||
|
"succeeded",
|
||||||
|
|
||||||
|
|
||||||
|
"failed",
|
||||||
|
|
||||||
|
|
||||||
|
"cancelled"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
started_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
finished_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
requested_count: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
produced_count: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
parameters_json: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
error_message: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
generation_jobs.associate = (db) => {
|
||||||
|
|
||||||
|
db.generation_jobs.belongsToMany(db.number_sets, {
|
||||||
|
as: 'produced_number_sets',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'generation_jobs_produced_number_setsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
through: 'generation_jobsProduced_number_setsNumber_sets',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.generation_jobs.belongsToMany(db.number_sets, {
|
||||||
|
as: 'produced_number_sets_filter',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'generation_jobs_produced_number_setsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
through: 'generation_jobsProduced_number_setsNumber_sets',
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.generation_jobs.belongsTo(db.lottery_games, {
|
||||||
|
as: 'game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.generation_jobs.belongsTo(db.draws, {
|
||||||
|
as: 'target_draw',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'target_drawId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.generation_jobs.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.generation_jobs.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return generation_jobs;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
186
backend/src/db/models/imports.js
Normal file
186
backend/src/db/models/imports.js
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
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 imports = sequelize.define(
|
||||||
|
'imports',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
import_type: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"historical_results",
|
||||||
|
|
||||||
|
|
||||||
|
"future_placeholders",
|
||||||
|
|
||||||
|
|
||||||
|
"mixed"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"queued",
|
||||||
|
|
||||||
|
|
||||||
|
"running",
|
||||||
|
|
||||||
|
|
||||||
|
"completed",
|
||||||
|
|
||||||
|
|
||||||
|
"failed"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
started_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
finished_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
source_url: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
records_read: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
records_created: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
records_updated: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
records_failed: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
log: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
imports.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.imports.belongsTo(db.lottery_games, {
|
||||||
|
as: 'game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.imports.hasMany(db.file, {
|
||||||
|
as: 'input_file',
|
||||||
|
foreignKey: 'belongsToId',
|
||||||
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
belongsTo: db.imports.getTableName(),
|
||||||
|
belongsToColumn: 'input_file',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.imports.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.imports.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
38
backend/src/db/models/index.js
Normal file
38
backend/src/db/models/index.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
'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;
|
||||||
266
backend/src/db/models/lottery_games.js
Normal file
266
backend/src/db/models/lottery_games.js
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
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 lottery_games = sequelize.define(
|
||||||
|
'lottery_games',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
slug: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"active",
|
||||||
|
|
||||||
|
|
||||||
|
"paused",
|
||||||
|
|
||||||
|
|
||||||
|
"archived"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
draw_schedule_type: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"fixed_weekdays",
|
||||||
|
|
||||||
|
|
||||||
|
"interval_days",
|
||||||
|
|
||||||
|
|
||||||
|
"manual"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
draw_days: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
timezone: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
numbers_per_ticket: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
min_number: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
max_number: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
bonus_numbers_count: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
bonus_min_number: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
bonus_max_number: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
future_draws_target: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
first_draw_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
notes: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
lottery_games.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.hasMany(db.draws, {
|
||||||
|
as: 'draws_game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.hasMany(db.number_sets, {
|
||||||
|
as: 'number_sets_game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.hasMany(db.generation_jobs, {
|
||||||
|
as: 'generation_jobs_game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.hasMany(db.analysis_models, {
|
||||||
|
as: 'analysis_models_game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.hasMany(db.model_runs, {
|
||||||
|
as: 'model_runs_game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.hasMany(db.imports, {
|
||||||
|
as: 'imports_game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.belongsTo(db.countries, {
|
||||||
|
as: 'country',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'countryId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.hasMany(db.file, {
|
||||||
|
as: 'rules_document',
|
||||||
|
foreignKey: 'belongsToId',
|
||||||
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
belongsTo: db.lottery_games.getTableName(),
|
||||||
|
belongsToColumn: 'rules_document',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.lottery_games.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.lottery_games.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return lottery_games;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
189
backend/src/db/models/model_runs.js
Normal file
189
backend/src/db/models/model_runs.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
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 model_runs = sequelize.define(
|
||||||
|
'model_runs',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
run_type: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"backtest",
|
||||||
|
|
||||||
|
|
||||||
|
"forecast",
|
||||||
|
|
||||||
|
|
||||||
|
"evaluation"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"queued",
|
||||||
|
|
||||||
|
|
||||||
|
"running",
|
||||||
|
|
||||||
|
|
||||||
|
"succeeded",
|
||||||
|
|
||||||
|
|
||||||
|
"failed"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
started_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
finished_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
input_window: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
metrics_json: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
output_summary: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
model_runs.associate = (db) => {
|
||||||
|
|
||||||
|
db.model_runs.belongsToMany(db.number_sets, {
|
||||||
|
as: 'suggested_number_sets',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'model_runs_suggested_number_setsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
through: 'model_runsSuggested_number_setsNumber_sets',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.model_runs.belongsToMany(db.number_sets, {
|
||||||
|
as: 'suggested_number_sets_filter',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'model_runs_suggested_number_setsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
through: 'model_runsSuggested_number_setsNumber_sets',
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.model_runs.belongsTo(db.analysis_models, {
|
||||||
|
as: 'model',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'modelId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.model_runs.belongsTo(db.lottery_games, {
|
||||||
|
as: 'game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.model_runs.belongsTo(db.draws, {
|
||||||
|
as: 'target_draw',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'target_drawId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.model_runs.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.model_runs.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return model_runs;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
178
backend/src/db/models/number_sets.js
Normal file
178
backend/src/db/models/number_sets.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
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 number_sets = sequelize.define(
|
||||||
|
'number_sets',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
set_type: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"generated",
|
||||||
|
|
||||||
|
|
||||||
|
"favorite",
|
||||||
|
|
||||||
|
|
||||||
|
"manual"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
main_numbers: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
bonus_numbers: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
size: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"active",
|
||||||
|
|
||||||
|
|
||||||
|
"archived"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
generated_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
generation_method: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
constraints_summary: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
score: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
number_sets.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.number_sets.belongsTo(db.lottery_games, {
|
||||||
|
as: 'game',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'gameId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.number_sets.belongsTo(db.draws, {
|
||||||
|
as: 'target_draw',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'target_drawId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.number_sets.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.number_sets.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return number_sets;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
78
backend/src/db/models/permissions.js
Normal file
78
backend/src/db/models/permissions.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
111
backend/src/db/models/roles.js
Normal file
111
backend/src/db/models/roles.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
127
backend/src/db/models/system_settings.js
Normal file
127
backend/src/db/models/system_settings.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
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 system_settings = sequelize.define(
|
||||||
|
'system_settings',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
setting_key: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
setting_value: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
value_type: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"string",
|
||||||
|
|
||||||
|
|
||||||
|
"int",
|
||||||
|
|
||||||
|
|
||||||
|
"decimal",
|
||||||
|
|
||||||
|
|
||||||
|
"boolean",
|
||||||
|
|
||||||
|
|
||||||
|
"json"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
description: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
is_secret: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
system_settings.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.system_settings.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.system_settings.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return system_settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
244
backend/src/db/models/users.js
Normal file
244
backend/src/db/models/users.js
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
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.admin_keys, {
|
||||||
|
as: 'admin_keys_issued_for_user',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'issued_for_userId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.users.belongsTo(db.roles, {
|
||||||
|
as: 'app_role',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'app_roleId',
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
});
|
||||||
66
backend/src/db/seeders/20200430130759-admin-user.js
Normal file
66
backend/src/db/seeders/20200430130759-admin-user.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
'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',
|
||||||
|
]
|
||||||
|
|
||||||
|
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()
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1258
backend/src/db/seeders/20200430130760-user-roles.js
Normal file
1258
backend/src/db/seeders/20200430130760-user-roles.js
Normal file
File diff suppressed because it is too large
Load Diff
3718
backend/src/db/seeders/20231127130745-sample-data.js
Normal file
3718
backend/src/db/seeders/20231127130745-sample-data.js
Normal file
File diff suppressed because it is too large
Load Diff
27
backend/src/db/utils.js
Normal file
27
backend/src/db/utils.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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'});
|
||||||
|
};
|
||||||
|
};
|
||||||
178
backend/src/index.js
Normal file
178
backend/src/index.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
|
||||||
|
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 sqlRoutes = require('./routes/sql');
|
||||||
|
const pexelsRoutes = require('./routes/pexels');
|
||||||
|
|
||||||
|
const openaiRoutes = require('./routes/openai');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const usersRoutes = require('./routes/users');
|
||||||
|
|
||||||
|
const rolesRoutes = require('./routes/roles');
|
||||||
|
|
||||||
|
const permissionsRoutes = require('./routes/permissions');
|
||||||
|
|
||||||
|
const admin_keysRoutes = require('./routes/admin_keys');
|
||||||
|
|
||||||
|
const countriesRoutes = require('./routes/countries');
|
||||||
|
|
||||||
|
const lottery_gamesRoutes = require('./routes/lottery_games');
|
||||||
|
|
||||||
|
const drawsRoutes = require('./routes/draws');
|
||||||
|
|
||||||
|
const number_setsRoutes = require('./routes/number_sets');
|
||||||
|
|
||||||
|
const generation_jobsRoutes = require('./routes/generation_jobs');
|
||||||
|
|
||||||
|
const analysis_modelsRoutes = require('./routes/analysis_models');
|
||||||
|
|
||||||
|
const model_runsRoutes = require('./routes/model_runs');
|
||||||
|
|
||||||
|
const importsRoutes = require('./routes/imports');
|
||||||
|
|
||||||
|
const system_settingsRoutes = require('./routes/system_settings');
|
||||||
|
|
||||||
|
|
||||||
|
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: "Gerador de Loterias Admin",
|
||||||
|
description: "Gerador de Loterias Admin 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/roles', passport.authenticate('jwt', {session: false}), rolesRoutes);
|
||||||
|
|
||||||
|
app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/admin_keys', passport.authenticate('jwt', {session: false}), admin_keysRoutes);
|
||||||
|
|
||||||
|
app.use('/api/countries', passport.authenticate('jwt', {session: false}), countriesRoutes);
|
||||||
|
|
||||||
|
app.use('/api/lottery_games', passport.authenticate('jwt', {session: false}), lottery_gamesRoutes);
|
||||||
|
|
||||||
|
app.use('/api/draws', passport.authenticate('jwt', {session: false}), drawsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/number_sets', passport.authenticate('jwt', {session: false}), number_setsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/generation_jobs', passport.authenticate('jwt', {session: false}), generation_jobsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/analysis_models', passport.authenticate('jwt', {session: false}), analysis_modelsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/model_runs', passport.authenticate('jwt', {session: false}), model_runsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/imports', passport.authenticate('jwt', {session: false}), importsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/system_settings', passport.authenticate('jwt', {session: false}), system_settingsRoutes);
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
'/api/openai',
|
||||||
|
passport.authenticate('jwt', { session: false }),
|
||||||
|
openaiRoutes,
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
'/api/ai',
|
||||||
|
passport.authenticate('jwt', { session: false }),
|
||||||
|
openaiRoutes,
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
'/api/search',
|
||||||
|
passport.authenticate('jwt', { session: false }),
|
||||||
|
searchRoutes);
|
||||||
|
app.use(
|
||||||
|
'/api/sql',
|
||||||
|
passport.authenticate('jwt', { session: false }),
|
||||||
|
sqlRoutes);
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
149
backend/src/middlewares/check-permissions.js
Normal file
149
backend/src/middlewares/check-permissions.js
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
439
backend/src/routes/admin_keys.js
Normal file
439
backend/src/routes/admin_keys.js
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const Admin_keysService = require('../services/admin_keys');
|
||||||
|
const Admin_keysDBApi = require('../db/api/admin_keys');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('admin_keys'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Admin_keys:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* key_name:
|
||||||
|
* type: string
|
||||||
|
* default: key_name
|
||||||
|
* key_fingerprint:
|
||||||
|
* type: string
|
||||||
|
* default: key_fingerprint
|
||||||
|
|
||||||
|
* max_uses:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* uses_count:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Admin_keys
|
||||||
|
* description: The Admin_keys managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/admin_keys:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* 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/Admin_keys"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Admin_keys"
|
||||||
|
* 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 Admin_keysService.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: [Admin_keys]
|
||||||
|
* 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/Admin_keys"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Admin_keys"
|
||||||
|
* 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 Admin_keysService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/admin_keys/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* 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/Admin_keys"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Admin_keys"
|
||||||
|
* 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 Admin_keysService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/admin_keys/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* 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/Admin_keys"
|
||||||
|
* 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 Admin_keysService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/admin_keys/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* 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/Admin_keys"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Admin_keysService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/admin_keys:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* summary: Get all admin_keys
|
||||||
|
* description: Get all admin_keys
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Admin_keys list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Admin_keys"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Admin_keysDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','key_name','key_fingerprint',
|
||||||
|
'max_uses','uses_count',
|
||||||
|
|
||||||
|
'issued_at','expires_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/admin_keys/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* summary: Count all admin_keys
|
||||||
|
* description: Count all admin_keys
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Admin_keys count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Admin_keys"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Admin_keysDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/admin_keys/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* summary: Find all admin_keys that match search criteria
|
||||||
|
* description: Find all admin_keys that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Admin_keys list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Admin_keys"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await Admin_keysDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/admin_keys/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Admin_keys]
|
||||||
|
* 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/Admin_keys"
|
||||||
|
* 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 Admin_keysDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@ -1,32 +0,0 @@
|
|||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const { LocalAIApi } = require('../ai/LocalAIApi');
|
|
||||||
|
|
||||||
router.post('/calculate', async (req, res) => {
|
|
||||||
const { input, type } = req.body;
|
|
||||||
const prompt = `Calculate the following expression: ${input}. Provide the result in ${type} format. Return only the result.`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const resp = await LocalAIApi.createResponse(
|
|
||||||
{
|
|
||||||
input: [
|
|
||||||
{ role: 'system', content: 'You are a precise calculator that provides results in requested formats.' },
|
|
||||||
{ role: 'user', content: prompt },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ poll_interval: 5, poll_timeout: 300 }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (resp.success) {
|
|
||||||
const text = LocalAIApi.extractText(resp);
|
|
||||||
res.json({ result: text });
|
|
||||||
} else {
|
|
||||||
res.status(500).json({ error: 'AI calculation failed' });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Calculation error:', error);
|
|
||||||
res.status(500).json({ error: 'Internal Server Error' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
440
backend/src/routes/analysis_models.js
Normal file
440
backend/src/routes/analysis_models.js
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const Analysis_modelsService = require('../services/analysis_models');
|
||||||
|
const Analysis_modelsDBApi = require('../db/api/analysis_models');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('analysis_models'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Analysis_models:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
* description:
|
||||||
|
* type: string
|
||||||
|
* default: description
|
||||||
|
* hyperparameters_json:
|
||||||
|
* type: string
|
||||||
|
* default: hyperparameters_json
|
||||||
|
|
||||||
|
|
||||||
|
* quality_score:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Analysis_models
|
||||||
|
* description: The Analysis_models managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/analysis_models:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* 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/Analysis_models"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Analysis_models"
|
||||||
|
* 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 Analysis_modelsService.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: [Analysis_models]
|
||||||
|
* 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/Analysis_models"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Analysis_models"
|
||||||
|
* 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 Analysis_modelsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/analysis_models/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* 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/Analysis_models"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Analysis_models"
|
||||||
|
* 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 Analysis_modelsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/analysis_models/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* 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/Analysis_models"
|
||||||
|
* 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 Analysis_modelsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/analysis_models/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* 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/Analysis_models"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Analysis_modelsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/analysis_models:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* summary: Get all analysis_models
|
||||||
|
* description: Get all analysis_models
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Analysis_models list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Analysis_models"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Analysis_modelsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','name','description','hyperparameters_json',
|
||||||
|
|
||||||
|
'quality_score',
|
||||||
|
'trained_at','last_evaluated_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/analysis_models/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* summary: Count all analysis_models
|
||||||
|
* description: Count all analysis_models
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Analysis_models count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Analysis_models"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Analysis_modelsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/analysis_models/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* summary: Find all analysis_models that match search criteria
|
||||||
|
* description: Find all analysis_models that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Analysis_models list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Analysis_models"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await Analysis_modelsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/analysis_models/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Analysis_models]
|
||||||
|
* 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/Analysis_models"
|
||||||
|
* 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 Analysis_modelsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
207
backend/src/routes/auth.js
Normal file
207
backend/src/routes/auth.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
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,
|
||||||
|
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;
|
||||||
0
backend/src/routes/contactForm.js
Normal file
0
backend/src/routes/contactForm.js
Normal file
435
backend/src/routes/countries.js
Normal file
435
backend/src/routes/countries.js
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const CountriesService = require('../services/countries');
|
||||||
|
const CountriesDBApi = require('../db/api/countries');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('countries'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Countries:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
* iso_code:
|
||||||
|
* type: string
|
||||||
|
* default: iso_code
|
||||||
|
* currency:
|
||||||
|
* type: string
|
||||||
|
* default: currency
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Countries
|
||||||
|
* description: The Countries managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/countries:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* 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/Countries"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Countries"
|
||||||
|
* 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 CountriesService.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: [Countries]
|
||||||
|
* 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/Countries"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Countries"
|
||||||
|
* 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 CountriesService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/countries/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* 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/Countries"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Countries"
|
||||||
|
* 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 CountriesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/countries/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* 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/Countries"
|
||||||
|
* 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 CountriesService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/countries/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* 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/Countries"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await CountriesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/countries:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* summary: Get all countries
|
||||||
|
* description: Get all countries
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Countries list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Countries"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await CountriesDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','name','iso_code','currency',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
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/countries/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* summary: Count all countries
|
||||||
|
* description: Count all countries
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Countries count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Countries"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await CountriesDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/countries/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* summary: Find all countries that match search criteria
|
||||||
|
* description: Find all countries that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Countries list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Countries"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await CountriesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/countries/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Countries]
|
||||||
|
* 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/Countries"
|
||||||
|
* 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 CountriesDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
451
backend/src/routes/draws.js
Normal file
451
backend/src/routes/draws.js
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const DrawsService = require('../services/draws');
|
||||||
|
const DrawsDBApi = require('../db/api/draws');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('draws'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Draws:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* winning_numbers:
|
||||||
|
* type: string
|
||||||
|
* default: winning_numbers
|
||||||
|
* bonus_numbers:
|
||||||
|
* type: string
|
||||||
|
* default: bonus_numbers
|
||||||
|
* source_url:
|
||||||
|
* type: string
|
||||||
|
* default: source_url
|
||||||
|
* import_batch_tag:
|
||||||
|
* type: string
|
||||||
|
* default: import_batch_tag
|
||||||
|
* admin_notes:
|
||||||
|
* type: string
|
||||||
|
* default: admin_notes
|
||||||
|
|
||||||
|
* draw_number:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
* jackpot_amount:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* prize_pool_amount:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Draws
|
||||||
|
* description: The Draws managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/draws:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* 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/Draws"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Draws"
|
||||||
|
* 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 DrawsService.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: [Draws]
|
||||||
|
* 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/Draws"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Draws"
|
||||||
|
* 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 DrawsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/draws/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* 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/Draws"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Draws"
|
||||||
|
* 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 DrawsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/draws/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* 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/Draws"
|
||||||
|
* 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 DrawsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/draws/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* 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/Draws"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await DrawsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/draws:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* summary: Get all draws
|
||||||
|
* description: Get all draws
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Draws list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Draws"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await DrawsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','winning_numbers','bonus_numbers','source_url','import_batch_tag','admin_notes',
|
||||||
|
'draw_number',
|
||||||
|
'jackpot_amount','prize_pool_amount',
|
||||||
|
'scheduled_at','result_published_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/draws/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* summary: Count all draws
|
||||||
|
* description: Count all draws
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Draws count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Draws"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await DrawsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/draws/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* summary: Find all draws that match search criteria
|
||||||
|
* description: Find all draws that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Draws list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Draws"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await DrawsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/draws/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Draws]
|
||||||
|
* 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/Draws"
|
||||||
|
* 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 DrawsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
32
backend/src/routes/file.js
Normal file
32
backend/src/routes/file.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
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;
|
||||||
440
backend/src/routes/generation_jobs.js
Normal file
440
backend/src/routes/generation_jobs.js
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const Generation_jobsService = require('../services/generation_jobs');
|
||||||
|
const Generation_jobsDBApi = require('../db/api/generation_jobs');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('generation_jobs'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Generation_jobs:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* parameters_json:
|
||||||
|
* type: string
|
||||||
|
* default: parameters_json
|
||||||
|
* error_message:
|
||||||
|
* type: string
|
||||||
|
* default: error_message
|
||||||
|
|
||||||
|
* requested_count:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* produced_count:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Generation_jobs
|
||||||
|
* description: The Generation_jobs managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/generation_jobs:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_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/Generation_jobs"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Generation_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 Generation_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: [Generation_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/Generation_jobs"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Generation_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 Generation_jobsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/generation_jobs/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_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/Generation_jobs"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Generation_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 Generation_jobsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/generation_jobs/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_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/Generation_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 Generation_jobsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/generation_jobs/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_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/Generation_jobs"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Generation_jobsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/generation_jobs:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_jobs]
|
||||||
|
* summary: Get all generation_jobs
|
||||||
|
* description: Get all generation_jobs
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Generation_jobs list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Generation_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 currentUser = req.currentUser;
|
||||||
|
const payload = await Generation_jobsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','parameters_json','error_message',
|
||||||
|
'requested_count','produced_count',
|
||||||
|
|
||||||
|
'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/generation_jobs/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_jobs]
|
||||||
|
* summary: Count all generation_jobs
|
||||||
|
* description: Count all generation_jobs
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Generation_jobs count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Generation_jobs"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Generation_jobsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/generation_jobs/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_jobs]
|
||||||
|
* summary: Find all generation_jobs that match search criteria
|
||||||
|
* description: Find all generation_jobs that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Generation_jobs list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Generation_jobs"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await Generation_jobsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/generation_jobs/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Generation_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/Generation_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 Generation_jobsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
446
backend/src/routes/imports.js
Normal file
446
backend/src/routes/imports.js
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const ImportsService = require('../services/imports');
|
||||||
|
const ImportsDBApi = require('../db/api/imports');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('imports'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Imports:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* source_url:
|
||||||
|
* type: string
|
||||||
|
* default: source_url
|
||||||
|
* log:
|
||||||
|
* type: string
|
||||||
|
* default: log
|
||||||
|
|
||||||
|
* records_read:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* records_created:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* records_updated:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* records_failed:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Imports
|
||||||
|
* description: The Imports managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/imports:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* 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/Imports"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Imports"
|
||||||
|
* 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 ImportsService.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: [Imports]
|
||||||
|
* 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/Imports"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Imports"
|
||||||
|
* 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 ImportsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/imports/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* 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/Imports"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Imports"
|
||||||
|
* 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 ImportsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/imports/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* 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/Imports"
|
||||||
|
* 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 ImportsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/imports/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* 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/Imports"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await ImportsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/imports:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* summary: Get all imports
|
||||||
|
* description: Get all imports
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Imports list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Imports"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await ImportsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','source_url','log',
|
||||||
|
'records_read','records_created','records_updated','records_failed',
|
||||||
|
|
||||||
|
'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/imports/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* summary: Count all imports
|
||||||
|
* description: Count all imports
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Imports count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Imports"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await ImportsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/imports/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* summary: Find all imports that match search criteria
|
||||||
|
* description: Find all imports that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Imports list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Imports"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await ImportsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/imports/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Imports]
|
||||||
|
* 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/Imports"
|
||||||
|
* 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 ImportsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
464
backend/src/routes/lottery_games.js
Normal file
464
backend/src/routes/lottery_games.js
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const Lottery_gamesService = require('../services/lottery_games');
|
||||||
|
const Lottery_gamesDBApi = require('../db/api/lottery_games');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('lottery_games'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Lottery_games:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
* slug:
|
||||||
|
* type: string
|
||||||
|
* default: slug
|
||||||
|
* draw_days:
|
||||||
|
* type: string
|
||||||
|
* default: draw_days
|
||||||
|
* timezone:
|
||||||
|
* type: string
|
||||||
|
* default: timezone
|
||||||
|
* notes:
|
||||||
|
* type: string
|
||||||
|
* default: notes
|
||||||
|
|
||||||
|
* numbers_per_ticket:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* min_number:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* max_number:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* bonus_numbers_count:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* bonus_min_number:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* bonus_max_number:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* future_draws_target:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Lottery_games
|
||||||
|
* description: The Lottery_games managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/lottery_games:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* 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/Lottery_games"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Lottery_games"
|
||||||
|
* 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 Lottery_gamesService.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: [Lottery_games]
|
||||||
|
* 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/Lottery_games"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Lottery_games"
|
||||||
|
* 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 Lottery_gamesService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/lottery_games/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* 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/Lottery_games"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Lottery_games"
|
||||||
|
* 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 Lottery_gamesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/lottery_games/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* 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/Lottery_games"
|
||||||
|
* 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 Lottery_gamesService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/lottery_games/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* 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/Lottery_games"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Lottery_gamesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/lottery_games:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* summary: Get all lottery_games
|
||||||
|
* description: Get all lottery_games
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Lottery_games list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Lottery_games"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Lottery_gamesDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','name','slug','draw_days','timezone','notes',
|
||||||
|
'numbers_per_ticket','min_number','max_number','bonus_numbers_count','bonus_min_number','bonus_max_number','future_draws_target',
|
||||||
|
|
||||||
|
'first_draw_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/lottery_games/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* summary: Count all lottery_games
|
||||||
|
* description: Count all lottery_games
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Lottery_games count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Lottery_games"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Lottery_gamesDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/lottery_games/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* summary: Find all lottery_games that match search criteria
|
||||||
|
* description: Find all lottery_games that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Lottery_games list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Lottery_games"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await Lottery_gamesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/lottery_games/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Lottery_games]
|
||||||
|
* 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/Lottery_games"
|
||||||
|
* 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 Lottery_gamesDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
437
backend/src/routes/model_runs.js
Normal file
437
backend/src/routes/model_runs.js
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const Model_runsService = require('../services/model_runs');
|
||||||
|
const Model_runsDBApi = require('../db/api/model_runs');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('model_runs'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Model_runs:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* input_window:
|
||||||
|
* type: string
|
||||||
|
* default: input_window
|
||||||
|
* metrics_json:
|
||||||
|
* type: string
|
||||||
|
* default: metrics_json
|
||||||
|
* output_summary:
|
||||||
|
* type: string
|
||||||
|
* default: output_summary
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Model_runs
|
||||||
|
* description: The Model_runs managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/model_runs:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* 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/Model_runs"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Model_runs"
|
||||||
|
* 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 Model_runsService.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: [Model_runs]
|
||||||
|
* 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/Model_runs"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Model_runs"
|
||||||
|
* 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 Model_runsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/model_runs/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* 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/Model_runs"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Model_runs"
|
||||||
|
* 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 Model_runsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/model_runs/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* 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/Model_runs"
|
||||||
|
* 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 Model_runsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/model_runs/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* 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/Model_runs"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Model_runsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/model_runs:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* summary: Get all model_runs
|
||||||
|
* description: Get all model_runs
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Model_runs list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Model_runs"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Model_runsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','input_window','metrics_json','output_summary',
|
||||||
|
|
||||||
|
|
||||||
|
'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/model_runs/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* summary: Count all model_runs
|
||||||
|
* description: Count all model_runs
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Model_runs count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Model_runs"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Model_runsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/model_runs/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* summary: Find all model_runs that match search criteria
|
||||||
|
* description: Find all model_runs that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Model_runs list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Model_runs"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await Model_runsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/model_runs/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Model_runs]
|
||||||
|
* 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/Model_runs"
|
||||||
|
* 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 Model_runsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
449
backend/src/routes/number_sets.js
Normal file
449
backend/src/routes/number_sets.js
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const Number_setsService = require('../services/number_sets');
|
||||||
|
const Number_setsDBApi = require('../db/api/number_sets');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('number_sets'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Number_sets:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
* main_numbers:
|
||||||
|
* type: string
|
||||||
|
* default: main_numbers
|
||||||
|
* bonus_numbers:
|
||||||
|
* type: string
|
||||||
|
* default: bonus_numbers
|
||||||
|
* generation_method:
|
||||||
|
* type: string
|
||||||
|
* default: generation_method
|
||||||
|
* constraints_summary:
|
||||||
|
* type: string
|
||||||
|
* default: constraints_summary
|
||||||
|
|
||||||
|
* size:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
* score:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Number_sets
|
||||||
|
* description: The Number_sets managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/number_sets:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* 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/Number_sets"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Number_sets"
|
||||||
|
* 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 Number_setsService.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: [Number_sets]
|
||||||
|
* 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/Number_sets"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Number_sets"
|
||||||
|
* 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 Number_setsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/number_sets/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* 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/Number_sets"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Number_sets"
|
||||||
|
* 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 Number_setsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/number_sets/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* 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/Number_sets"
|
||||||
|
* 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 Number_setsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/number_sets/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* 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/Number_sets"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Number_setsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/number_sets:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* summary: Get all number_sets
|
||||||
|
* description: Get all number_sets
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Number_sets list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Number_sets"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Number_setsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','name','main_numbers','bonus_numbers','generation_method','constraints_summary',
|
||||||
|
'size',
|
||||||
|
'score',
|
||||||
|
'generated_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/number_sets/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* summary: Count all number_sets
|
||||||
|
* description: Count all number_sets
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Number_sets count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Number_sets"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Number_setsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/number_sets/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* summary: Find all number_sets that match search criteria
|
||||||
|
* description: Find all number_sets that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Number_sets list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Number_sets"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await Number_setsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/number_sets/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Number_sets]
|
||||||
|
* 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/Number_sets"
|
||||||
|
* 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 Number_setsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
328
backend/src/routes/openai.js
Normal file
328
backend/src/routes/openai.js
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
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 { LocalAIApi } = require('../ai/LocalAIApi');
|
||||||
|
|
||||||
|
const loadRolesModules = () => {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
RolesService: require('../services/roles'),
|
||||||
|
RolesDBApi: require('../db/api/roles'),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Roles modules are missing. Advanced roles are required for this endpoint.', error);
|
||||||
|
const err = new Error('Roles modules are missing. Advanced roles are required for this endpoint.');
|
||||||
|
err.originalError = error;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 { RolesService } = loadRolesModules();
|
||||||
|
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 { RolesService, RolesDBApi } = loadRolesModules();
|
||||||
|
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 { RolesService } = loadRolesModules();
|
||||||
|
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/response:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [OpenAI]
|
||||||
|
* summary: Proxy a Responses API request
|
||||||
|
* description: Sends the payload to the Flatlogic AI proxy and returns the response.
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* input:
|
||||||
|
* type: array
|
||||||
|
* description: List of messages with roles and content.
|
||||||
|
* items:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* role:
|
||||||
|
* type: string
|
||||||
|
* content:
|
||||||
|
* type: string
|
||||||
|
* options:
|
||||||
|
* type: object
|
||||||
|
* description: Optional polling controls.
|
||||||
|
* properties:
|
||||||
|
* poll_interval:
|
||||||
|
* type: number
|
||||||
|
* poll_timeout:
|
||||||
|
* type: number
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: AI response received
|
||||||
|
* 400:
|
||||||
|
* description: Invalid request
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 502:
|
||||||
|
* description: Proxy error
|
||||||
|
*/
|
||||||
|
router.post(
|
||||||
|
'/response',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const body = req.body || {};
|
||||||
|
const options = body.options || {};
|
||||||
|
const payload = { ...body };
|
||||||
|
delete payload.options;
|
||||||
|
|
||||||
|
const response = await LocalAIApi.createResponse(payload, options);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
return res.status(200).send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('AI proxy error:', response);
|
||||||
|
const status = response.error === 'input_missing' ? 400 : 502;
|
||||||
|
return res.status(status).send(response);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/openai/ask:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [OpenAI]
|
||||||
|
* summary: Ask a question to ChatGPT
|
||||||
|
* description: Send a question through the Flatlogic AI proxy and get a response
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* prompt:
|
||||||
|
* type: string
|
||||||
|
* description: The question to ask ChatGPT
|
||||||
|
* 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: 'Prompt is 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;
|
||||||
2
backend/src/routes/organizationLogin.js
Normal file
2
backend/src/routes/organizationLogin.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
|
||||||
429
backend/src/routes/permissions.js
Normal file
429
backend/src/routes/permissions.js
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const PermissionsService = require('../services/permissions');
|
||||||
|
const PermissionsDBApi = require('../db/api/permissions');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('permissions'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Permissions:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Permissions
|
||||||
|
* description: The Permissions managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Add new item
|
||||||
|
* description: Add new item
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await PermissionsService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/budgets/bulk-import:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Bulk import items
|
||||||
|
* description: Bulk import items
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated items
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await PermissionsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Update the data of the selected item
|
||||||
|
* description: Update the data of the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to update
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* requestBody:
|
||||||
|
* description: Set new item data
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* description: ID of the updated item
|
||||||
|
* type: string
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await PermissionsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Delete the selected item
|
||||||
|
* description: Delete the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to delete
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await PermissionsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Delete the selected item list
|
||||||
|
* description: Delete the selected item list
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* ids:
|
||||||
|
* description: IDs of the updated items
|
||||||
|
* type: array
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await PermissionsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Get all permissions
|
||||||
|
* description: Get all permissions
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Permissions list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await PermissionsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','name',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
const opts = { fields };
|
||||||
|
try {
|
||||||
|
const csv = parse(payload.rows, opts);
|
||||||
|
res.status(200).attachment(csv);
|
||||||
|
res.send(csv)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Count all permissions
|
||||||
|
* description: Count all permissions
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Permissions count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await PermissionsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Find all permissions that match search criteria
|
||||||
|
* description: Find all permissions that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Permissions list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await PermissionsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/permissions/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Permissions]
|
||||||
|
* summary: Get selected item
|
||||||
|
* description: Get selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: ID of item to get
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Selected item successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Permissions"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await PermissionsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
104
backend/src/routes/pexels.js
Normal file
104
backend/src/routes/pexels.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { pexelsKey, pexelsQuery } = require('../config');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
const KEY = pexelsKey;
|
||||||
|
|
||||||
|
router.get('/image', async (req, res) => {
|
||||||
|
const headers = {
|
||||||
|
Authorization: `${KEY}`,
|
||||||
|
};
|
||||||
|
const query = pexelsQuery || 'nature';
|
||||||
|
const orientation = 'portrait';
|
||||||
|
const perPage = 1;
|
||||||
|
const url = `https://api.pexels.com/v1/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, { headers });
|
||||||
|
const data = await response.json();
|
||||||
|
res.status(200).json(data.photos[0]);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(200).json({ error: 'Failed to fetch image' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/video', async (req, res) => {
|
||||||
|
const headers = {
|
||||||
|
Authorization: `${KEY}`,
|
||||||
|
};
|
||||||
|
const query = pexelsQuery || 'nature';
|
||||||
|
const orientation = 'portrait';
|
||||||
|
const perPage = 1;
|
||||||
|
const url = `https://api.pexels.com/videos/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, { headers });
|
||||||
|
const data = await response.json();
|
||||||
|
res.status(200).json(data.videos[0]);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(200).json({ error: 'Failed to fetch video' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/multiple-images', async (req, res) => {
|
||||||
|
const headers = {
|
||||||
|
Authorization: `${KEY}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const queries = req.query.queries
|
||||||
|
? req.query.queries.split(',')
|
||||||
|
: ['home', 'apple', 'pizza', 'mountains', 'cat'];
|
||||||
|
const orientation = 'square';
|
||||||
|
const perPage = 1;
|
||||||
|
|
||||||
|
const fallbackImage = {
|
||||||
|
src: 'https://images.pexels.com/photos/8199252/pexels-photo-8199252.jpeg',
|
||||||
|
photographer: 'Yan Krukau',
|
||||||
|
photographer_url: 'https://www.pexels.com/@yankrukov',
|
||||||
|
};
|
||||||
|
const fetchFallbackImage = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://picsum.photos/600');
|
||||||
|
return {
|
||||||
|
src: response.url,
|
||||||
|
photographer: 'Random Picsum',
|
||||||
|
photographer_url: 'https://picsum.photos/',
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return fallbackImage;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const fetchImage = async (query) => {
|
||||||
|
const url = `https://api.pexels.com/v1/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`;
|
||||||
|
const response = await fetch(url, { headers });
|
||||||
|
const data = await response.json();
|
||||||
|
return data.photos[0] || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const imagePromises = queries.map((query) => fetchImage(query));
|
||||||
|
const imagesResults = await Promise.allSettled(imagePromises);
|
||||||
|
|
||||||
|
const formattedImages = await Promise.all(imagesResults.map(async (result) => {
|
||||||
|
if (result.status === 'fulfilled' && result.value) {
|
||||||
|
const image = result.value;
|
||||||
|
return {
|
||||||
|
src: image.src?.original || fallbackImage.src,
|
||||||
|
photographer: image.photographer || fallbackImage.photographer,
|
||||||
|
photographer_url: image.photographer_url || fallbackImage.photographer_url,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const fallback = await fetchFallbackImage();
|
||||||
|
return {
|
||||||
|
src: fallback.src || '',
|
||||||
|
photographer: fallback.photographer || 'Unknown',
|
||||||
|
photographer_url: fallback.photographer_url || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
res.json(formattedImages);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
429
backend/src/routes/roles.js
Normal file
429
backend/src/routes/roles.js
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const RolesService = require('../services/roles');
|
||||||
|
const RolesDBApi = require('../db/api/roles');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('roles'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Roles:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Roles
|
||||||
|
* description: The Roles managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Add new item
|
||||||
|
* description: Add new item
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await RolesService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/budgets/bulk-import:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Bulk import items
|
||||||
|
* description: Bulk import items
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated items
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await RolesService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Update the data of the selected item
|
||||||
|
* description: Update the data of the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to update
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* requestBody:
|
||||||
|
* description: Set new item data
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* description: ID of the updated item
|
||||||
|
* type: string
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await RolesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Delete the selected item
|
||||||
|
* description: Delete the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to delete
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await RolesService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Delete the selected item list
|
||||||
|
* description: Delete the selected item list
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* ids:
|
||||||
|
* description: IDs of the updated items
|
||||||
|
* type: array
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await RolesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Get all roles
|
||||||
|
* description: Get all roles
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Roles list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await RolesDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','name',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
const opts = { fields };
|
||||||
|
try {
|
||||||
|
const csv = parse(payload.rows, opts);
|
||||||
|
res.status(200).attachment(csv);
|
||||||
|
res.send(csv)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Count all roles
|
||||||
|
* description: Count all roles
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Roles count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await RolesDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Find all roles that match search criteria
|
||||||
|
* description: Find all roles that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Roles list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await RolesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/roles/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Roles]
|
||||||
|
* summary: Get selected item
|
||||||
|
* description: Get selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: ID of item to get
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Selected item successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Roles"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await RolesDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
52
backend/src/routes/search.js
Normal file
52
backend/src/routes/search.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const SearchService = require('../services/search');
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
router.use(checkCrudPermissions('search'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* path:
|
||||||
|
* /api/search:
|
||||||
|
* post:
|
||||||
|
* summary: Search
|
||||||
|
* description: Search results across multiple tables
|
||||||
|
* requestBody:
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* searchQuery:
|
||||||
|
* type: string
|
||||||
|
* required:
|
||||||
|
* - searchQuery
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Successful request
|
||||||
|
* 400:
|
||||||
|
* description: Invalid request
|
||||||
|
* 500:
|
||||||
|
* description: Internal server error
|
||||||
|
*/
|
||||||
|
|
||||||
|
router.post('/', async (req, res) => {
|
||||||
|
const { searchQuery } = req.body;
|
||||||
|
|
||||||
|
if (!searchQuery) {
|
||||||
|
return res.status(400).json({ error: 'Please enter a search query' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const foundMatches = await SearchService.search(searchQuery, req.currentUser );
|
||||||
|
res.json(foundMatches);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Internal Server Error', error);
|
||||||
|
res.status(500).json({ error: 'Internal Server Error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
61
backend/src/routes/sql.js
Normal file
61
backend/src/routes/sql.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const db = require('../db/models');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/sql:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* summary: Execute a SELECT-only SQL query
|
||||||
|
* description: Executes a read-only SQL query and returns rows.
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* sql:
|
||||||
|
* type: string
|
||||||
|
* required:
|
||||||
|
* - sql
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Query result
|
||||||
|
* 400:
|
||||||
|
* description: Invalid SQL
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 500:
|
||||||
|
* description: Internal server error
|
||||||
|
*/
|
||||||
|
router.post(
|
||||||
|
'/',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const { sql } = req.body;
|
||||||
|
if (typeof sql !== 'string' || !sql.trim()) {
|
||||||
|
return res.status(400).json({ error: 'SQL is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = sql.trim().replace(/;+\s*$/, '');
|
||||||
|
if (!/^select\b/i.test(normalized)) {
|
||||||
|
return res.status(400).json({ error: 'Only SELECT statements are allowed' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized.includes(';')) {
|
||||||
|
return res.status(400).json({ error: 'Only a single SELECT statement is allowed' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = await db.sequelize.query(normalized, {
|
||||||
|
type: db.Sequelize.QueryTypes.SELECT,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({ rows });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
436
backend/src/routes/system_settings.js
Normal file
436
backend/src/routes/system_settings.js
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const System_settingsService = require('../services/system_settings');
|
||||||
|
const System_settingsDBApi = require('../db/api/system_settings');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('system_settings'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* System_settings:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* setting_key:
|
||||||
|
* type: string
|
||||||
|
* default: setting_key
|
||||||
|
* setting_value:
|
||||||
|
* type: string
|
||||||
|
* default: setting_value
|
||||||
|
* description:
|
||||||
|
* type: string
|
||||||
|
* default: description
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: System_settings
|
||||||
|
* description: The System_settings managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* 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/System_settings"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/System_settings"
|
||||||
|
* 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 System_settingsService.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: [System_settings]
|
||||||
|
* 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/System_settings"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/System_settings"
|
||||||
|
* 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 System_settingsService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* 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/System_settings"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/System_settings"
|
||||||
|
* 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 System_settingsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* 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/System_settings"
|
||||||
|
* 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 System_settingsService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* 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/System_settings"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await System_settingsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* summary: Get all system_settings
|
||||||
|
* description: Get all system_settings
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: System_settings list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/System_settings"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await System_settingsDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','setting_key','setting_value','description',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
const opts = { fields };
|
||||||
|
try {
|
||||||
|
const csv = parse(payload.rows, opts);
|
||||||
|
res.status(200).attachment(csv);
|
||||||
|
res.send(csv)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* summary: Count all system_settings
|
||||||
|
* description: Count all system_settings
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: System_settings count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/System_settings"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await System_settingsDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* summary: Find all system_settings that match search criteria
|
||||||
|
* description: Find all system_settings that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: System_settings list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/System_settings"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await System_settingsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/system_settings/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [System_settings]
|
||||||
|
* 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/System_settings"
|
||||||
|
* 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 System_settingsDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
440
backend/src/routes/users.js
Normal file
440
backend/src/routes/users.js
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const UsersService = require('../services/users');
|
||||||
|
const UsersDBApi = require('../db/api/users');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('users'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Users:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* firstName:
|
||||||
|
* type: string
|
||||||
|
* default: firstName
|
||||||
|
* lastName:
|
||||||
|
* type: string
|
||||||
|
* default: lastName
|
||||||
|
* phoneNumber:
|
||||||
|
* type: string
|
||||||
|
* default: phoneNumber
|
||||||
|
* email:
|
||||||
|
* type: string
|
||||||
|
* default: email
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Users
|
||||||
|
* description: The Users managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Add new item
|
||||||
|
* description: Add new item
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await UsersService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/budgets/bulk-import:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Bulk import items
|
||||||
|
* description: Bulk import items
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated items
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await UsersService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Update the data of the selected item
|
||||||
|
* description: Update the data of the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to update
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* requestBody:
|
||||||
|
* description: Set new item data
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* description: ID of the updated item
|
||||||
|
* type: string
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await UsersService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Delete the selected item
|
||||||
|
* description: Delete the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to delete
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await UsersService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Delete the selected item list
|
||||||
|
* description: Delete the selected item list
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* ids:
|
||||||
|
* description: IDs of the updated items
|
||||||
|
* type: array
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await UsersService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Get all users
|
||||||
|
* description: Get all users
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Users list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await UsersDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','firstName','lastName','phoneNumber','email',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
const opts = { fields };
|
||||||
|
try {
|
||||||
|
const csv = parse(payload.rows, opts);
|
||||||
|
res.status(200).attachment(csv);
|
||||||
|
res.send(csv)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Count all users
|
||||||
|
* description: Count all users
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Users count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await UsersDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Find all users that match search criteria
|
||||||
|
* description: Find all users that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Users list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await UsersDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/users/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Users]
|
||||||
|
* summary: Get selected item
|
||||||
|
* description: Get selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: ID of item to get
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Selected item successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Users"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await UsersDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
delete payload.password;
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
138
backend/src/services/admin_keys.js
Normal file
138
backend/src/services/admin_keys.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const Admin_keysDBApi = require('../db/api/admin_keys');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Admin_keysService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Admin_keysDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await Admin_keysDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let admin_keys = await Admin_keysDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!admin_keys) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'admin_keysNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedAdmin_keys = await Admin_keysDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedAdmin_keys;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Admin_keysDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Admin_keysDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
138
backend/src/services/analysis_models.js
Normal file
138
backend/src/services/analysis_models.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const Analysis_modelsDBApi = require('../db/api/analysis_models');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Analysis_modelsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Analysis_modelsDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await Analysis_modelsDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let analysis_models = await Analysis_modelsDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!analysis_models) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'analysis_modelsNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedAnalysis_models = await Analysis_modelsDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedAnalysis_models;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Analysis_modelsDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Analysis_modelsDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
312
backend/src/services/auth.js
Normal file
312
backend/src/services/auth.js
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
const UsersDBApi = require('../db/api/users');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const ForbiddenError = require('./notifications/errors/forbidden');
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const EmailAddressVerificationEmail = require('./email/list/addressVerification');
|
||||||
|
const InvitationEmail = require("./email/list/invitation");
|
||||||
|
const PasswordResetEmail = require('./email/list/passwordReset');
|
||||||
|
const EmailSender = require('./email');
|
||||||
|
const config = require('../config');
|
||||||
|
const helpers = require('../helpers');
|
||||||
|
|
||||||
|
class Auth {
|
||||||
|
static async signup(email, password, options = {}, host) {
|
||||||
|
const user = await UsersDBApi.findBy({email});
|
||||||
|
|
||||||
|
const hashedPassword = await bcrypt.hash(
|
||||||
|
password,
|
||||||
|
config.bcrypt.saltRounds,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
if (user.authenticationUid) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.emailAlreadyInUse',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.disabled) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.userDisabled',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await UsersDBApi.updatePassword(
|
||||||
|
user.id,
|
||||||
|
hashedPassword,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (EmailSender.isConfigured) {
|
||||||
|
await this.sendEmailAddressVerificationEmail(
|
||||||
|
user.email,
|
||||||
|
host,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return helpers.jwtSign(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = await UsersDBApi.createFromAuth(
|
||||||
|
{
|
||||||
|
firstName: email.split('@')[0],
|
||||||
|
password: hashedPassword,
|
||||||
|
email: email,
|
||||||
|
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (EmailSender.isConfigured) {
|
||||||
|
await this.sendEmailAddressVerificationEmail(
|
||||||
|
newUser.email,
|
||||||
|
host,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
user: {
|
||||||
|
id: newUser.id,
|
||||||
|
email: newUser.email
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return helpers.jwtSign(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async signin(email, password, options = {}) {
|
||||||
|
const user = await UsersDBApi.findBy({email});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.userNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.disabled) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.userDisabled',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.password) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.wrongPassword',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EmailSender.isConfigured) {
|
||||||
|
user.emailVerified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.emailVerified) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.userNotVerified',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordsMatch = await bcrypt.compare(
|
||||||
|
password,
|
||||||
|
user.password,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!passwordsMatch) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.wrongPassword',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return helpers.jwtSign(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async sendEmailAddressVerificationEmail(
|
||||||
|
email,
|
||||||
|
host,
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
let link;
|
||||||
|
try {
|
||||||
|
const token = await UsersDBApi.generateEmailVerificationToken(
|
||||||
|
email,
|
||||||
|
);
|
||||||
|
link = `${host}/verify-email?token=${token}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.emailAddressVerificationEmail.error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailAddressVerificationEmail = new EmailAddressVerificationEmail(
|
||||||
|
email,
|
||||||
|
link,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new EmailSender(
|
||||||
|
emailAddressVerificationEmail,
|
||||||
|
).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async sendPasswordResetEmail(email, type = 'register', host) {
|
||||||
|
|
||||||
|
|
||||||
|
let link;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = await UsersDBApi.generatePasswordResetToken(
|
||||||
|
email,
|
||||||
|
);
|
||||||
|
link = `${host}/password-reset?token=${token}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.passwordReset.error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let passwordResetEmail;
|
||||||
|
if (type === 'register') {
|
||||||
|
passwordResetEmail = new PasswordResetEmail(
|
||||||
|
email,
|
||||||
|
link,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (type === 'invitation') {
|
||||||
|
passwordResetEmail = new InvitationEmail(
|
||||||
|
email,
|
||||||
|
link,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EmailSender(passwordResetEmail).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async verifyEmail(token, options = {}) {
|
||||||
|
const user = await UsersDBApi.findByEmailVerificationToken(
|
||||||
|
token,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.emailAddressVerificationEmail.invalidToken',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UsersDBApi.markEmailVerified(
|
||||||
|
user.id,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async passwordUpdate(currentPassword, newPassword, options) {
|
||||||
|
const currentUser = options.currentUser || null;
|
||||||
|
if (!currentUser) {
|
||||||
|
throw new ForbiddenError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPasswordMatch = await bcrypt.compare(
|
||||||
|
currentPassword,
|
||||||
|
currentUser.password,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!currentPasswordMatch) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.wrongPassword'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPasswordMatch = await bcrypt.compare(
|
||||||
|
newPassword,
|
||||||
|
currentUser.password,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newPasswordMatch) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.passwordUpdate.samePassword'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashedPassword = await bcrypt.hash(
|
||||||
|
newPassword,
|
||||||
|
config.bcrypt.saltRounds,
|
||||||
|
);
|
||||||
|
|
||||||
|
return UsersDBApi.updatePassword(
|
||||||
|
currentUser.id,
|
||||||
|
hashedPassword,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async passwordReset(
|
||||||
|
token,
|
||||||
|
password,
|
||||||
|
options = {},
|
||||||
|
) {
|
||||||
|
const user = await UsersDBApi.findByPasswordResetToken(
|
||||||
|
token,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'auth.passwordReset.invalidToken',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashedPassword = await bcrypt.hash(
|
||||||
|
password,
|
||||||
|
config.bcrypt.saltRounds,
|
||||||
|
);
|
||||||
|
|
||||||
|
return UsersDBApi.updatePassword(
|
||||||
|
user.id,
|
||||||
|
hashedPassword,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateProfile(data, currentUser) {
|
||||||
|
let transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await UsersDBApi.findBy(
|
||||||
|
{id: currentUser.id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
await UsersDBApi.update(
|
||||||
|
currentUser.id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Auth;
|
||||||
138
backend/src/services/countries.js
Normal file
138
backend/src/services/countries.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const CountriesDBApi = require('../db/api/countries');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class CountriesService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await CountriesDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await CountriesDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let countries = await CountriesDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!countries) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'countriesNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedCountries = await CountriesDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedCountries;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CountriesDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CountriesDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
138
backend/src/services/draws.js
Normal file
138
backend/src/services/draws.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const DrawsDBApi = require('../db/api/draws');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class DrawsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await DrawsDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await DrawsDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let draws = await DrawsDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!draws) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'drawsNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedDraws = await DrawsDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedDraws;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await DrawsDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await DrawsDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
.email-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.email-header {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: #fff;
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.email-body {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.email-footer {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #f7fafc;
|
||||||
|
text-align: center;
|
||||||
|
color: #4a5568;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.link-primary {
|
||||||
|
color: #3498db;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="email-container">
|
||||||
|
<div class="email-header">
|
||||||
|
Verify your email for {appTitle}!
|
||||||
|
</div>
|
||||||
|
<div class="email-body">
|
||||||
|
<p>Hello,</p>
|
||||||
|
<p>Follow this link to verify your email address.</p>
|
||||||
|
<p>If you didn't ask to verify this address, you can ignore this email.</p>
|
||||||
|
<p><a href="{signupUrl}" class="link-primary">{signupUrl}</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="email-footer">
|
||||||
|
Thanks,<br/>
|
||||||
|
The {appTitle} Team
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
.email-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.email-header {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: #fff;
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.email-body {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.email-footer {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #f7fafc;
|
||||||
|
text-align: center;
|
||||||
|
color: #4a5568;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: #fff!important;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="email-container">
|
||||||
|
<div class="email-header">
|
||||||
|
Welcome to {appTitle}!
|
||||||
|
</div>
|
||||||
|
<div class="email-body">
|
||||||
|
<p>Hello,</p>
|
||||||
|
<p>You've been invited to join {appTitle}. Please click the button below to set up your account.</p>
|
||||||
|
<a href="{signupUrl}" class="btn-primary">Set up account</a>
|
||||||
|
</div>
|
||||||
|
<div class="email-footer">
|
||||||
|
Thanks,<br/>
|
||||||
|
The {appTitle} Team
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
.email-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.email-header {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: #fff;
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.email-body {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.email-footer {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #f7fafc;
|
||||||
|
text-align: center;
|
||||||
|
color: #4a5568;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.link-primary {
|
||||||
|
color: #3498db;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="email-container">
|
||||||
|
<div class="email-header">
|
||||||
|
Reset your password for {appTitle}
|
||||||
|
</div>
|
||||||
|
<div class="email-body">
|
||||||
|
<p>Hello,</p>
|
||||||
|
<p>Follow this link to reset your {appTitle} password for your {accountName} account.</p>
|
||||||
|
<p><a href="{resetUrl}" class="link-primary">{resetUrl}</a></p>
|
||||||
|
<p>If you didn't ask to reset your password, you can ignore this email.</p>
|
||||||
|
</div>
|
||||||
|
<div class="email-footer">
|
||||||
|
Thanks,<br/>
|
||||||
|
The {appTitle} Team
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
44
backend/src/services/email/index.js
Normal file
44
backend/src/services/email/index.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const config = require('../../config');
|
||||||
|
const assert = require('assert');
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
|
module.exports = class EmailSender {
|
||||||
|
constructor(email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
async send() {
|
||||||
|
assert(this.email, 'email is required');
|
||||||
|
assert(this.email.to, 'email.to is required');
|
||||||
|
assert(this.email.subject, 'email.subject is required');
|
||||||
|
assert(this.email.html, 'email.html is required');
|
||||||
|
|
||||||
|
const htmlContent = await this.email.html();
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport(this.transportConfig);
|
||||||
|
|
||||||
|
const mailOptions = {
|
||||||
|
from: this.from,
|
||||||
|
to: this.email.to,
|
||||||
|
subject: this.email.subject,
|
||||||
|
html: htmlContent,
|
||||||
|
headers: {
|
||||||
|
'X-SES-CONFIGURATION-SET': 'flatlogic-app',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return transporter.sendMail(mailOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isConfigured() {
|
||||||
|
return !!config.email?.auth?.pass && !!config.email?.auth?.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
get transportConfig() {
|
||||||
|
return config.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
get from() {
|
||||||
|
return config.email.from;
|
||||||
|
}
|
||||||
|
};
|
||||||
38
backend/src/services/email/list/addressVerification.js
Normal file
38
backend/src/services/email/list/addressVerification.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const { getNotification } = require('../../notifications/helpers');
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = class EmailAddressVerificationEmail {
|
||||||
|
constructor(to, link) {
|
||||||
|
this.to = to;
|
||||||
|
this.link = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
get subject() {
|
||||||
|
return getNotification(
|
||||||
|
'emails.emailAddressVerification.subject',
|
||||||
|
getNotification('app.title'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async html() {
|
||||||
|
try {
|
||||||
|
const templatePath = path.join(__dirname, '../../email/htmlTemplates/addressVerification/emailAddressVerification.html');
|
||||||
|
|
||||||
|
const template = await fs.readFile(templatePath, 'utf8');
|
||||||
|
|
||||||
|
const appTitle = getNotification('app.title');
|
||||||
|
const signupUrl = this.link;
|
||||||
|
|
||||||
|
let html = template.replace(/{appTitle}/g, appTitle)
|
||||||
|
.replace(/{signupUrl}/g, signupUrl)
|
||||||
|
.replace(/{to}/g, this.to);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating invitation email HTML:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
37
backend/src/services/email/list/invitation.js
Normal file
37
backend/src/services/email/list/invitation.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
const fs = require('fs').promises;
|
||||||
|
const path = require('path');
|
||||||
|
const { getNotification } = require('../../notifications/helpers');
|
||||||
|
|
||||||
|
module.exports = class InvitationEmail {
|
||||||
|
constructor(to, host) {
|
||||||
|
this.to = to;
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
get subject() {
|
||||||
|
return getNotification(
|
||||||
|
'emails.invitation.subject',
|
||||||
|
getNotification('app.title'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async html() {
|
||||||
|
try {
|
||||||
|
const templatePath = path.join(__dirname, '../../email/htmlTemplates/invitation/invitationTemplate.html');
|
||||||
|
|
||||||
|
const template = await fs.readFile(templatePath, 'utf8');
|
||||||
|
|
||||||
|
const appTitle = getNotification('app.title');
|
||||||
|
const signupUrl = `${this.host}&invitation=true`;
|
||||||
|
|
||||||
|
let html = template.replace(/{appTitle}/g, appTitle)
|
||||||
|
.replace(/{signupUrl}/g, signupUrl)
|
||||||
|
.replace(/{to}/g, this.to);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating invitation email HTML:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
38
backend/src/services/email/list/passwordReset.js
Normal file
38
backend/src/services/email/list/passwordReset.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const { getNotification } = require('../../notifications/helpers');
|
||||||
|
const path = require("path");
|
||||||
|
const {promises: fs} = require("fs");
|
||||||
|
|
||||||
|
module.exports = class PasswordResetEmail {
|
||||||
|
constructor(to, link) {
|
||||||
|
this.to = to;
|
||||||
|
this.link = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
get subject() {
|
||||||
|
return getNotification(
|
||||||
|
'emails.passwordReset.subject',
|
||||||
|
getNotification('app.title'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async html() {
|
||||||
|
try {
|
||||||
|
const templatePath = path.join(__dirname, '../../email/htmlTemplates/passwordReset/passwordResetEmail.html');
|
||||||
|
|
||||||
|
const template = await fs.readFile(templatePath, 'utf8');
|
||||||
|
|
||||||
|
const appTitle = getNotification('app.title');
|
||||||
|
const resetUrl = this.link;
|
||||||
|
const accountName = this.to;
|
||||||
|
|
||||||
|
let html = template.replace(/{appTitle}/g, appTitle)
|
||||||
|
.replace(/{resetUrl}/g, resetUrl)
|
||||||
|
.replace(/{accountName}/g, accountName);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating invitation email HTML:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
213
backend/src/services/file.js
Normal file
213
backend/src/services/file.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
const formidable = require('formidable');
|
||||||
|
const fs = require('fs');
|
||||||
|
const config = require('../config');
|
||||||
|
const path = require('path');
|
||||||
|
const { format } = require("util");
|
||||||
|
|
||||||
|
const ensureDirectoryExistence = (filePath) => {
|
||||||
|
const dirname = path.dirname(filePath);
|
||||||
|
|
||||||
|
if (fs.existsSync(dirname)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureDirectoryExistence(dirname);
|
||||||
|
fs.mkdirSync(dirname);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadLocal = (
|
||||||
|
folder,
|
||||||
|
validations = {
|
||||||
|
entity: null,
|
||||||
|
maxFileSize: null,
|
||||||
|
folderIncludesAuthenticationUid: false,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
return (req, res) => {
|
||||||
|
if (!req.currentUser) {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
validations.entity
|
||||||
|
) {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validations.folderIncludesAuthenticationUid) {
|
||||||
|
folder = folder.replace(
|
||||||
|
':userId',
|
||||||
|
req.currentUser.authenticationUid,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!req.currentUser.authenticationUid ||
|
||||||
|
!folder.includes(req.currentUser.authenticationUid)
|
||||||
|
) {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = new formidable.IncomingForm();
|
||||||
|
form.uploadDir = config.uploadDir;
|
||||||
|
|
||||||
|
if (validations && validations.maxFileSize) {
|
||||||
|
form.maxFileSize = validations.maxFileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.parse(req, function (err, fields, files) {
|
||||||
|
const filename = String(fields.filename);
|
||||||
|
const fileTempUrl = files.file.path;
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
fs.unlinkSync(fileTempUrl);
|
||||||
|
res.sendStatus(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const privateUrl = path.join(
|
||||||
|
form.uploadDir,
|
||||||
|
folder,
|
||||||
|
filename,
|
||||||
|
);
|
||||||
|
ensureDirectoryExistence(privateUrl);
|
||||||
|
fs.renameSync(fileTempUrl, privateUrl);
|
||||||
|
res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
form.on('error', function (err) {
|
||||||
|
res.status(500).send(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadLocal = async (req, res) => {
|
||||||
|
const privateUrl = req.query.privateUrl;
|
||||||
|
if (!privateUrl) {
|
||||||
|
return res.sendStatus(404);
|
||||||
|
}
|
||||||
|
res.download(path.join(config.uploadDir, privateUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
const initGCloud = () => {
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const { Storage } = require("@google-cloud/storage");
|
||||||
|
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const hash = config.gcloud.hash
|
||||||
|
|
||||||
|
const privateKey = process.env.GC_PRIVATE_KEY.replace(/\\\n/g, "\n");
|
||||||
|
|
||||||
|
const storage = new Storage({
|
||||||
|
projectId: process.env.GC_PROJECT_ID,
|
||||||
|
credentials: {
|
||||||
|
client_email: process.env.GC_CLIENT_EMAIL,
|
||||||
|
private_key: privateKey
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bucket = storage.bucket(config.gcloud.bucket);
|
||||||
|
return {hash, bucket, processFile};
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadGCloud = async (folder, req, res) => {
|
||||||
|
try {
|
||||||
|
const {hash, bucket, processFile} = initGCloud();
|
||||||
|
await processFile(req, res);
|
||||||
|
let buffer = await req.file.buffer;
|
||||||
|
let filename = await req.body.filename;
|
||||||
|
|
||||||
|
if (!req.file) {
|
||||||
|
return res.status(400).send({ message: "Please upload a file!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = `${hash}/${folder}/${filename}`;
|
||||||
|
let blob = bucket.file(path);
|
||||||
|
|
||||||
|
console.log(path);
|
||||||
|
|
||||||
|
const blobStream = blob.createWriteStream({
|
||||||
|
resumable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
blobStream.on("error", (err) => {
|
||||||
|
console.log('Upload error');
|
||||||
|
console.log(err.message);
|
||||||
|
res.status(500).send({ message: err.message });
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`https://storage.googleapis.com/${bucket.name}/${blob.name}`);
|
||||||
|
|
||||||
|
blobStream.on("finish", async (data) => {
|
||||||
|
const publicUrl = format(
|
||||||
|
`https://storage.googleapis.com/${bucket.name}/${blob.name}`
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send({
|
||||||
|
message: "Uploaded the file successfully: " + path,
|
||||||
|
url: publicUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
blobStream.end(buffer)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
|
||||||
|
res.status(500).send({
|
||||||
|
message: `Could not upload the file. ${err}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadGCloud = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const {hash, bucket, processFile} = initGCloud();
|
||||||
|
|
||||||
|
const privateUrl = await req.query.privateUrl;
|
||||||
|
const filePath = `${hash}/${privateUrl}`;
|
||||||
|
const file = bucket.file(filePath)
|
||||||
|
const fileExists = await file.exists();
|
||||||
|
|
||||||
|
if (fileExists[0]) {
|
||||||
|
const stream = file.createReadStream();
|
||||||
|
stream.pipe(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.status(404).send({
|
||||||
|
message: "Could not download the file. " + err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
res.status(404).send({
|
||||||
|
message: "Could not download the file. " + err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteGCloud = async (privateUrl) => {
|
||||||
|
try {
|
||||||
|
const {hash, bucket, processFile} = initGCloud();
|
||||||
|
const filePath = `${hash}/${privateUrl}`;
|
||||||
|
|
||||||
|
const file = bucket.file(filePath)
|
||||||
|
const fileExists = await file.exists();
|
||||||
|
|
||||||
|
if (fileExists[0]) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`Cannot find the file ${privateUrl}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initGCloud,
|
||||||
|
uploadLocal,
|
||||||
|
downloadLocal,
|
||||||
|
deleteGCloud,
|
||||||
|
uploadGCloud,
|
||||||
|
downloadGCloud
|
||||||
|
}
|
||||||
|
|
||||||
138
backend/src/services/generation_jobs.js
Normal file
138
backend/src/services/generation_jobs.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const Generation_jobsDBApi = require('../db/api/generation_jobs');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Generation_jobsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Generation_jobsDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await Generation_jobsDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let generation_jobs = await Generation_jobsDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!generation_jobs) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'generation_jobsNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedGeneration_jobs = await Generation_jobsDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedGeneration_jobs;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Generation_jobsDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Generation_jobsDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
138
backend/src/services/imports.js
Normal file
138
backend/src/services/imports.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const ImportsDBApi = require('../db/api/imports');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class ImportsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await ImportsDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await ImportsDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let imports = await ImportsDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!imports) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'importsNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedImports = await ImportsDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedImports;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ImportsDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ImportsDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
138
backend/src/services/lottery_games.js
Normal file
138
backend/src/services/lottery_games.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const Lottery_gamesDBApi = require('../db/api/lottery_games');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Lottery_gamesService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Lottery_gamesDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await Lottery_gamesDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let lottery_games = await Lottery_gamesDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!lottery_games) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'lottery_gamesNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedLottery_games = await Lottery_gamesDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedLottery_games;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Lottery_gamesDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Lottery_gamesDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
138
backend/src/services/model_runs.js
Normal file
138
backend/src/services/model_runs.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const Model_runsDBApi = require('../db/api/model_runs');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Model_runsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Model_runsDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await Model_runsDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let model_runs = await Model_runsDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!model_runs) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'model_runsNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedModel_runs = await Model_runsDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedModel_runs;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Model_runsDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Model_runsDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
17
backend/src/services/notifications/errors/forbidden.js
Normal file
17
backend/src/services/notifications/errors/forbidden.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
18
backend/src/services/notifications/errors/validation.js
Normal file
18
backend/src/services/notifications/errors/validation.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
35
backend/src/services/notifications/helpers.js
Normal file
35
backend/src/services/notifications/helpers.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
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;
|
||||||
104
backend/src/services/notifications/list.js
Normal file
104
backend/src/services/notifications/list.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
const errors = {
|
||||||
|
app: {
|
||||||
|
title: 'Gerador de Loterias Admin',
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
138
backend/src/services/number_sets.js
Normal file
138
backend/src/services/number_sets.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const Number_setsDBApi = require('../db/api/number_sets');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Number_setsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Number_setsDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await Number_setsDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let number_sets = await Number_setsDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!number_sets) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'number_setsNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedNumber_sets = await Number_setsDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedNumber_sets;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Number_setsDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Number_setsDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
80
backend/src/services/openai.js
Normal file
80
backend/src/services/openai.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const { LocalAIApi } = require('../ai/LocalAIApi');
|
||||||
|
|
||||||
|
const loadRoleService = () => {
|
||||||
|
try {
|
||||||
|
return require('./roles');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Role service is missing. Advanced roles are required for this operation.', error);
|
||||||
|
const err = new Error('Role service is missing. Advanced roles are required for this operation.');
|
||||||
|
err.originalError = error;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = class OpenAiService {
|
||||||
|
static async getWidget(payload, userId, roleId) {
|
||||||
|
const RoleService = loadRoleService();
|
||||||
|
const response = await axios.post(
|
||||||
|
`${config.flHost}/${config.project_uuid}/project_customization_widgets.json`,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
const { widget_id } = await response.data;
|
||||||
|
await RoleService.addRoleInfo(roleId, userId, 'widgets', widget_id);
|
||||||
|
return widget_id;
|
||||||
|
} else {
|
||||||
|
console.error('=======error=======', response.data);
|
||||||
|
return { value: null, error: response.data };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async askGpt(prompt) {
|
||||||
|
if (!prompt) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Prompt is required'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await LocalAIApi.createResponse(
|
||||||
|
{
|
||||||
|
input: [{ role: 'user', content: prompt }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
poll_interval: 5,
|
||||||
|
poll_timeout: 300,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
let text = LocalAIApi.extractText(response);
|
||||||
|
if (!text) {
|
||||||
|
try {
|
||||||
|
const decoded = LocalAIApi.decodeJsonFromResponse(response);
|
||||||
|
text = JSON.stringify(decoded);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('AI JSON decode failed:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'AI response parsing failed',
|
||||||
|
details: error.message || String(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('AI proxy error:', response);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: response.error || response.message || 'AI proxy error',
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
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