Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b65ff99a0 | |||
| 74a3f52324 | |||
| 7c2a4fdfa8 | |||
| fecee3a098 | |||
| 45f7111fb9 | |||
| f2e23a8062 | |||
| 36a3631f5e | |||
| a758d19a9f | |||
| aac3e53a30 | |||
| 85d87ee210 | |||
| 2f3ee80ed4 | |||
| 3632bea54e | |||
| 4a0521751a | |||
| 7d6266c241 | |||
| 1d69b9ffea |
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": ["airbnb-base", "plugin:jest/recommended", "plugin:security/recommended", "plugin:prettier/recommended"],
|
||||
"plugins": ["jest", "security", "prettier", "import"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any":"off",
|
||||
"no-console": "error",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
"jest/expect-expect": "off",
|
||||
"security/detect-object-injection": "off",
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
"js": "never",
|
||||
"jsx": "never",
|
||||
"ts": "never",
|
||||
"tsx": "never"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"jest": true,
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "error",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
"jest/expect-expect": "off",
|
||||
"security/detect-object-injection": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn"
|
||||
}
|
||||
}
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"CodeGPT.apiKey": "OpenAI",
|
||||
"cSpell.words": [
|
||||
"subcode"
|
||||
]
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.7.29](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.29-alpha.0...coopback@1.7.29) (2024-08-05)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.28](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.28-alpha.0...coopback@1.7.28) (2024-07-26)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
Generated
+4
-4
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.28",
|
||||
"version": "1.7.29",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.28",
|
||||
"version": "1.7.29",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@a2seven/yoo-checkout": "^1.1.4",
|
||||
@@ -32,7 +32,7 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"moment": "^2.24.0",
|
||||
"mongoose": "^5.7.7",
|
||||
"mongoose": "^8.5.2",
|
||||
"morgan": "^1.9.1",
|
||||
"nodemailer": "^6.3.1",
|
||||
"nunjucks": "^3.2.4",
|
||||
@@ -54,7 +54,7 @@
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.11.26",
|
||||
"coveralls": "^3.0.7",
|
||||
"eslint": "^7.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "coopback",
|
||||
"version": "1.7.28",
|
||||
"version": "1.7.29",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"bin": "bin/createNodejsApp.js",
|
||||
@@ -8,6 +8,7 @@
|
||||
"repository": "https://github.com/hagopj13/node-express-boilerplate.git",
|
||||
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
||||
"license": "MIT",
|
||||
"types": "src/types/index.ts",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
@@ -88,7 +89,7 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"moment": "^2.24.0",
|
||||
"mongoose": "^5.7.7",
|
||||
"mongoose": "^8.5.2",
|
||||
"morgan": "^1.9.1",
|
||||
"nodemailer": "^6.3.1",
|
||||
"nunjucks": "^3.2.4",
|
||||
@@ -107,13 +108,16 @@
|
||||
"xss-clean": "^0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.24.1",
|
||||
"@redocly/cli": "^1.18.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/faker": "^6.6.9",
|
||||
"@types/node": "^20.11.26",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"coveralls": "^3.0.7",
|
||||
"eslint": "^7.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import config from '../src/config/config';
|
||||
import logger from '../src/config/logger';
|
||||
import { User } from '../src/models';
|
||||
import { tokenService } from '../src/services';
|
||||
import { userService } from '../src/services';
|
||||
@@ -7,7 +8,7 @@ import mongoose from 'mongoose';
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 1) {
|
||||
console.error('Ошибка: Необходимо указать имя пользователя');
|
||||
logger.error('Ошибка: Необходимо указать имя пользователя');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -19,9 +20,9 @@ async function createServiceUser(username: string) {
|
||||
|
||||
const user = await userService.createServiceUser(username);
|
||||
const token = await tokenService.generateServiceAccessToken(user);
|
||||
console.log('token:', token.access.token);
|
||||
logger.log('token:', token.access.token);
|
||||
} catch (e: any) {
|
||||
console.log('Ошибка: ', e.message);
|
||||
logger.log('Ошибка: ', e.message);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
@@ -7,7 +7,7 @@ async function types(): Promise<void> {
|
||||
// Configure your settings here
|
||||
const result = await convertFromDirectory({
|
||||
schemaDirectory: './src/validations',
|
||||
typeOutputDirectory: './src/types',
|
||||
typeOutputDirectory: './src/types/auto-generated',
|
||||
debug: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import dotenv from 'dotenv';
|
||||
import Joi from 'joi';
|
||||
import path from 'path';
|
||||
// import { fileURLToPath } from 'url';
|
||||
// import { dirname, join } from 'path';
|
||||
|
||||
// const __filename = fileURLToPath(import.meta.url);
|
||||
// const __dirname = dirname(__filename);
|
||||
|
||||
dotenv.config({ path: path.join(__dirname, '../../.env') });
|
||||
|
||||
const envVarsSchema = Joi.object()
|
||||
.keys({
|
||||
NODE_ENV: Joi.string().valid('production', 'development', 'test').required(),
|
||||
BASE_URL: Joi.string().required(),
|
||||
SERVICE_USERNAME: Joi.string().required(),
|
||||
SERVICE_WIF: Joi.string().required(),
|
||||
PORT: Joi.number().default(3000),
|
||||
MONGODB_URL: Joi.string().required().description('Mongo DB url'),
|
||||
JWT_SECRET: Joi.string().required().description('JWT secret key'),
|
||||
@@ -28,6 +26,8 @@ const envVarsSchema = Joi.object()
|
||||
SMTP_USERNAME: Joi.string().description('username for email server'),
|
||||
SMTP_PASSWORD: Joi.string().description('password for email server'),
|
||||
EMAIL_FROM: Joi.string().description('the from field in the emails sent by the app'),
|
||||
|
||||
COOPNAME: Joi.string().description('имя аккаунта кооператива'),
|
||||
})
|
||||
.unknown();
|
||||
|
||||
@@ -39,13 +39,16 @@ if (error) {
|
||||
|
||||
export default {
|
||||
env: envVars.NODE_ENV,
|
||||
base_url: envVars.BASE_URL,
|
||||
port: envVars.PORT,
|
||||
service_wif: envVars.SERVICE_WIF,
|
||||
service_username: envVars.SERVICE_USERNAME,
|
||||
mongoose: {
|
||||
url: envVars.MONGODB_URL + (envVars.NODE_ENV === 'test' ? '-test' : ''),
|
||||
options: {
|
||||
useCreateIndex: true,
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
// useCreateIndex: true,
|
||||
// useNewUrlParser: true,
|
||||
// useUnifiedTopology: true,
|
||||
},
|
||||
},
|
||||
jwt: {
|
||||
@@ -66,4 +69,5 @@ export default {
|
||||
},
|
||||
from: envVars.EMAIL_FROM,
|
||||
},
|
||||
coopname: envVars.COOPNAME,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import winston from 'winston'
|
||||
import config from './config'
|
||||
import winston from 'winston';
|
||||
import config from './config';
|
||||
|
||||
const enumerateErrorFormat = winston.format((info) => {
|
||||
if (info instanceof Error) {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import httpStatus from 'http-status';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
|
||||
import { authService, userService, tokenService, emailService, blockchainService } from '../services';
|
||||
import { RForgotPassword, RRefreshTokens, RResetPassword, RSendNotification, RVerifyEmail } from '../types';
|
||||
import { authService, tokenService, emailService } from '../services';
|
||||
import { RForgotKey, RRefreshTokens, RResetKey, RVerifyEmail } from '../types';
|
||||
|
||||
const { CREATED, NO_CONTENT } = httpStatus;
|
||||
const { NO_CONTENT } = httpStatus;
|
||||
|
||||
export const login = catchAsync(async (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
const user = await authService.loginUserWithEmailAndPassword(email, password);
|
||||
const { now, signature, email } = req.body;
|
||||
|
||||
// if (user.is_registered === false)
|
||||
// throw new Error('Регистрация не была завершена. Пожалуйста, подождите решения совета или начните с начала. ');
|
||||
const user = await authService.loginUserWithSignature(email, now, signature);
|
||||
|
||||
const tokens = await tokenService.generateAuthTokens(user);
|
||||
res.send({ user, tokens });
|
||||
@@ -27,14 +25,14 @@ export const refreshTokens = catchAsync(async (req: RRefreshTokens, res: any) =>
|
||||
res.send(tokens);
|
||||
});
|
||||
|
||||
export const forgotPassword = catchAsync(async (req: RForgotPassword, res) => {
|
||||
const resetPasswordToken = await tokenService.generateResetPasswordToken(req.body.email);
|
||||
await emailService.sendResetPasswordEmail(req.body.email, resetPasswordToken);
|
||||
export const lostKey = catchAsync(async (req: RForgotKey, res) => {
|
||||
const resetKeyToken = await tokenService.generateResetPasswordToken(req.body.email);
|
||||
await emailService.sendResetPasswordEmail(req.body.email, resetKeyToken);
|
||||
res.status(NO_CONTENT).send();
|
||||
});
|
||||
|
||||
export const resetPassword = catchAsync(async (req: RResetPassword, res) => {
|
||||
await authService.resetPassword(req.query.token, req.body.password);
|
||||
export const resetKey = catchAsync(async (req: RResetKey, res) => {
|
||||
await authService.resetKey(req.body.token, req.body.public_key);
|
||||
res.status(NO_CONTENT).send();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { coopService, documentService } from '../services';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { generator } from '../services/document.service';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import httpStatus from 'http-status';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
export const loadInfo = catchAsync(async (req, res) => {
|
||||
export const loadInfo = catchAsync(async (req: Request, res: Response) => {
|
||||
const cooperative = await coopService.loadInfo(String(process.env.COOPNAME));
|
||||
res.send(cooperative);
|
||||
});
|
||||
|
||||
export const loadContacts = catchAsync(async (req, res) => {
|
||||
export const loadContacts = catchAsync(async (req: Request, res: Response) => {
|
||||
const contacts = await coopService.loadContacts(String(process.env.COOPNAME));
|
||||
|
||||
res.send(contacts);
|
||||
});
|
||||
|
||||
export const loadAgenda = catchAsync(async (req, res) => {
|
||||
export const loadAgenda = catchAsync(async (req: Request, res: Response) => {
|
||||
const { coopname } = req.query;
|
||||
const agenda = await coopService.loadAgenda(coopname);
|
||||
const agenda = await coopService.loadAgenda(coopname as string);
|
||||
|
||||
const complexAgenda: Cooperative.Documents.IComplexAgenda[] = [];
|
||||
|
||||
@@ -30,7 +28,7 @@ export const loadAgenda = catchAsync(async (req, res) => {
|
||||
res.send(complexAgenda);
|
||||
});
|
||||
|
||||
export const loadStaff = catchAsync(async (req, res) => {
|
||||
export const loadStaff = catchAsync(async (req, res: Response) => {
|
||||
const { coopname } = req.query;
|
||||
const staff = await coopService.loadStaff(coopname);
|
||||
res.send(staff);
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { documentService } from '../services/index';
|
||||
import type {
|
||||
IGetDocuments,
|
||||
RDeletePaymentMethod,
|
||||
RGenerate,
|
||||
RGetDocuments,
|
||||
RGetListPaymentMethods,
|
||||
RSavePaymentMethod,
|
||||
} from '../types';
|
||||
import type { RGenerate, RGetDocuments } from '../types';
|
||||
|
||||
import httpStatus from 'http-status';
|
||||
import pick from '../utils/pick';
|
||||
import { IGetResponse } from '../types/common';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import ApiError from '../utils/ApiError';
|
||||
|
||||
export const generateDocument = catchAsync(async (req: RGenerate, res) => {
|
||||
const document = await documentService.generateDocument(req.body);
|
||||
@@ -21,9 +11,9 @@ export const generateDocument = catchAsync(async (req: RGenerate, res) => {
|
||||
});
|
||||
|
||||
export const getDocuments = catchAsync(async (req: RGetDocuments, res) => {
|
||||
let filter = req.query?.filter;
|
||||
let limit = req.query?.limit as number;
|
||||
let page = req.query?.page as number;
|
||||
const filter = req.query?.filter;
|
||||
const limit = req.query?.limit as number;
|
||||
const page = req.query?.page as number;
|
||||
|
||||
const documents = await documentService.queryDocuments(filter, page, limit);
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ export * as documentController from './document.controller';
|
||||
export * as paymentController from './payment.controller';
|
||||
export * as coopController from './coop.controller';
|
||||
export * as notifyController from './notify.controller';
|
||||
export * as monoController from './mono.controller';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import httpStatus from 'http-status';
|
||||
import { getMonoStatus } from '../services/mono.service';
|
||||
import { getBlockchainInfo } from '../services/blockchain.service';
|
||||
import { IHealthResponse } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
|
||||
export const getHealth = catchAsync(async (req: Request, res: Response) => {
|
||||
const status = await getMonoStatus();
|
||||
const blockchain = await getBlockchainInfo();
|
||||
|
||||
const result: IHealthResponse = {
|
||||
status,
|
||||
blockchain,
|
||||
};
|
||||
|
||||
res.status(httpStatus.OK).send(result);
|
||||
});
|
||||
@@ -44,7 +44,7 @@ export const getUsers = catchAsync(async (req, res) => {
|
||||
const result = {} as IGetResponse<IUser>;
|
||||
|
||||
const data = [] as any;
|
||||
for await (let user of users.results) {
|
||||
for await (const user of users.results) {
|
||||
const json = user.toJSON();
|
||||
if (user.type != 'service') {
|
||||
json.private_data = (await user.getPrivateData()) || {};
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
export * from './types';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import app from './app';
|
||||
import config from './config/config';
|
||||
import logger from './config/logger';
|
||||
import { updateAuth } from './services/auth.service';
|
||||
import { connectGenerator } from './services/document.service';
|
||||
import { initSocketConnection } from './controllers/ws.controller';
|
||||
import { monoService } from './services';
|
||||
|
||||
const SERVER_URL: string = process.env.SOCKET_SERVER || 'http://localhost:2222';
|
||||
|
||||
let server: any;
|
||||
|
||||
mongoose.connect(config.mongoose.url, config.mongoose.options).then(async () => {
|
||||
mongoose.connect(config.mongoose.url).then(async () => {
|
||||
logger.info('Connected to MongoDB');
|
||||
|
||||
//подключаемся к хранилищу приватных данных
|
||||
await monoService.init();
|
||||
|
||||
// подключаемся к хранилищу приватных данных
|
||||
await connectGenerator();
|
||||
|
||||
//подключаемся к ws-серверу
|
||||
// подключаемся к ws-серверу
|
||||
await initSocketConnection(SERVER_URL);
|
||||
|
||||
server = app.listen(config.port, () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { default as Token } from './token.model';
|
||||
export { default as User } from './user.model';
|
||||
export { default as Order } from './order.model';
|
||||
export { default as Mono } from './mono.model';
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import mongoose, { Schema } from 'mongoose';
|
||||
import { paginate, toJSON } from './plugins';
|
||||
import { IHealthStatus } from '../types';
|
||||
|
||||
export interface IMono {
|
||||
coopname: string;
|
||||
status: IHealthStatus;
|
||||
}
|
||||
|
||||
const Status = {
|
||||
Install: 'install',
|
||||
Active: 'active',
|
||||
Maintenance: 'maintenance',
|
||||
};
|
||||
|
||||
const MonoSchema = new Schema<IMono>({
|
||||
coopname: { type: String, required: true },
|
||||
status: { type: String, required: true, enum: Object.values(Status) },
|
||||
});
|
||||
|
||||
MonoSchema.plugin(toJSON);
|
||||
MonoSchema.plugin(paginate);
|
||||
|
||||
const MonoModel = mongoose.model<IMono>('Mono', MonoSchema);
|
||||
|
||||
export default MonoModel;
|
||||
@@ -5,9 +5,9 @@ interface IOrder extends Document {
|
||||
creator: string;
|
||||
type: string;
|
||||
data: {
|
||||
username: string
|
||||
provider: string
|
||||
quantity: string
|
||||
username: string;
|
||||
provider: string;
|
||||
quantity: string;
|
||||
};
|
||||
order_id?: number;
|
||||
payed?: boolean;
|
||||
@@ -16,59 +16,61 @@ interface IOrder extends Document {
|
||||
expired_at?: Date;
|
||||
}
|
||||
|
||||
const orderSchema = new Schema<IOrder>({
|
||||
creator: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
provider: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
username: {
|
||||
const orderSchema = new Schema<IOrder>(
|
||||
{
|
||||
creator: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
quantity: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
order_id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
payed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
delivered: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
expired_at: {
|
||||
type: Date,
|
||||
default: () => {
|
||||
const now = new Date();
|
||||
now.setDate(now.getDate() + 1); // Увеличиваем на одни сутки
|
||||
return now;
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
provider: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
quantity: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
order_id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
payed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
delivered: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
expired_at: {
|
||||
type: Date,
|
||||
default: () => {
|
||||
const now = new Date();
|
||||
now.setDate(now.getDate() + 1); // Увеличиваем на одни сутки
|
||||
return now;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
});
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
// add plugin that converts mongoose to json
|
||||
orderSchema.plugin(toJSON);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import mongoose, { Document, Schema, model, Model } from 'mongoose';
|
||||
import mongoose, { Schema, model, Model } from 'mongoose';
|
||||
import validator from 'validator/index';
|
||||
import bcryptjs from 'bcryptjs';
|
||||
import { toJSON, paginate } from './plugins/index';
|
||||
@@ -14,6 +14,7 @@ export interface IUser {
|
||||
status: 'created' | 'joined' | 'payed' | 'registered' | 'active' | 'failed' | 'blocked';
|
||||
message: string;
|
||||
is_registered: boolean;
|
||||
has_account: boolean;
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
public_key: string;
|
||||
referer: string;
|
||||
@@ -23,7 +24,7 @@ export interface IUser {
|
||||
is_email_verified: boolean;
|
||||
statement: {
|
||||
hash: string;
|
||||
meta: Object;
|
||||
meta: object;
|
||||
public_key: string;
|
||||
signature: string;
|
||||
};
|
||||
@@ -163,11 +164,13 @@ userSchema.methods.getPrivateData = async function (): Promise<
|
||||
};
|
||||
|
||||
userSchema.methods.isPasswordMatch = async function (password) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const user = this;
|
||||
return compare(password, user.password);
|
||||
};
|
||||
|
||||
userSchema.pre('save', async function (next) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const user = this;
|
||||
if (user.isModified('password')) {
|
||||
user.password = await hash(user.password, 8);
|
||||
|
||||
@@ -10,8 +10,8 @@ const router = Router();
|
||||
router.post('/login', validate(authValidation.RLogin), authController.login);
|
||||
router.post('/logout', validate(authValidation.RLogout), authController.logout);
|
||||
router.post('/refresh-tokens', validate(authValidation.RRefreshTokens), authController.refreshTokens);
|
||||
router.post('/forgot-password', validate(authValidation.RForgotPassword), authController.forgotPassword);
|
||||
router.post('/reset-password', validate(authValidation.RResetPassword), authController.resetPassword);
|
||||
router.post('/lost-key', validate(authValidation.RForgotKey), authController.lostKey);
|
||||
router.post('/reset-key', validate(authValidation.RResetKey), authController.resetKey);
|
||||
router.post('/send-verification-email', auth(), authController.sendVerificationEmail);
|
||||
router.post('/verify-email', validate(authValidation.RVerifyEmail), authController.verifyEmail);
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@ import { Router } from 'express';
|
||||
import auth from '../../middlewares/auth';
|
||||
import validate from '../../middlewares/validate';
|
||||
import * as coopValidation from '../../validations/coop.validation';
|
||||
// import { loadAgenda, loadStaff, } from '../../validations/coop.validation';
|
||||
import * as coopController from '../../controllers/coop.controller';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.route('/agenda').get(auth('loadAgenda'), validate(coopValidation.loadAgenda), coopController.loadAgenda);
|
||||
router.route('/staff').get(auth('loadStaff'), validate(coopValidation.loadStaff), coopController.loadStaff);
|
||||
|
||||
router.route('/contacts').get(coopController.loadContacts);
|
||||
router.route('/info').get(auth('loadInfo'), coopController.loadInfo);
|
||||
|
||||
router.route('/contacts').get(coopController.loadContacts);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -4,6 +4,7 @@ import userRoute from './user.route';
|
||||
import docsRoute from './docs.route';
|
||||
import paymentRoute from './payment.route';
|
||||
import coopRoute from './coop.route';
|
||||
import monoRoute from './mono.route';
|
||||
import dataRoute from './document.route';
|
||||
import notifyRoute from './notify.route';
|
||||
|
||||
@@ -12,6 +13,10 @@ import config from '../../config/config';
|
||||
const router = Router();
|
||||
|
||||
const defaultRoutes = [
|
||||
{
|
||||
path: '/mono',
|
||||
route: monoRoute,
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
route: authRoute,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import { monoController } from '../../controllers';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.route('/health').get(monoController.getHealth);
|
||||
|
||||
export default router;
|
||||
@@ -1,55 +1,51 @@
|
||||
import httpStatus from 'http-status';
|
||||
import * as tokenService from './token.service';
|
||||
import * as userService from './user.service';
|
||||
import { getSoviet } from './blockchain.service';
|
||||
import Token from '../models/token.model';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import { tokenTypes } from '../config/tokens';
|
||||
import { IRefreshTokens } from '../types';
|
||||
import { getUserByEmail } from './user.service';
|
||||
import { Bytes, Checksum256, Signature } from '@wharfkit/antelope';
|
||||
import { getBlockchainAccount, getBlockchainInfo, hasActiveKey } from '../services/blockchain.service';
|
||||
import config from '../config/config';
|
||||
import { blockchainService } from '.';
|
||||
|
||||
export const updateAuth = async () => {
|
||||
try {
|
||||
const board = await getSoviet(process.env.COOPNAME);
|
||||
// TODO снимать права с тех, кто уже не в совете
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const member of board.members) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const user = await userService.getUserByUsername(member.username);
|
||||
if (member.position === 'chairman' && !user) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
// await userService.createUser({
|
||||
// username: member.username,
|
||||
// public_key: '-',
|
||||
// email: process.env.CHAIRMAN_EMAIL ,
|
||||
// password: process.env.CHAIRMAN_PASSWORD as string,
|
||||
// is_registered: true,
|
||||
// is_organization: false,
|
||||
// user_profile: {
|
||||
// first_name: 'Имя',
|
||||
// last_name: 'Фамилия',
|
||||
// middle_name: 'Отчество',
|
||||
// birthday: '23-42-3423',
|
||||
// phone: '+7902294404',
|
||||
// },
|
||||
// signature: '-',
|
||||
// signature_hash: '-',
|
||||
// role: 'chairman',
|
||||
// });
|
||||
} else if (member.position === 'chairman' && user) {
|
||||
user.role = 'chairman';
|
||||
user.password = process.env.CHAIRMAN_PASSWORD || 'password';
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await user.save();
|
||||
} else if (user) {
|
||||
user.role = 'admin';
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await user.save();
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Ошибка при автоматической проверке целевой авторизации: ', e.message);
|
||||
export const loginUserWithSignature = async (email, now, signature) => {
|
||||
const user = await getUserByEmail(email);
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Пользователь не найден');
|
||||
}
|
||||
|
||||
const bytes = Bytes.fromString(now, 'utf8');
|
||||
const checksum = Checksum256.hash(bytes);
|
||||
const wharf_signature = Signature.from(signature);
|
||||
const publicKey = wharf_signature.recoverDigest(checksum).toLegacyString();
|
||||
|
||||
const info = await getBlockchainInfo();
|
||||
const blockchainDate = new Date(info.head_block_time).getTime();
|
||||
const userData = new Date(now).getTime();
|
||||
|
||||
const differenceInSeconds = (blockchainDate - userData) / 1000;
|
||||
|
||||
if (differenceInSeconds > 30) {
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'Время подписи и время блокчейна превышает допустимое расхождение');
|
||||
}
|
||||
|
||||
if (config.env !== 'test') {
|
||||
try {
|
||||
const blockchainAccount = await getBlockchainAccount(user.username);
|
||||
|
||||
const hasKey = hasActiveKey(blockchainAccount, publicKey);
|
||||
|
||||
if (!hasKey) throw new ApiError(httpStatus.UNAUTHORIZED, 'Неверный приватный ключ');
|
||||
} catch (e) {
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'Аккаунт в блокчейне не найден');
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -76,7 +72,7 @@ export const logout = async (refreshToken) => {
|
||||
if (!refreshTokenDoc) {
|
||||
throw new ApiError(httpStatus.NOT_FOUND, 'Not found');
|
||||
}
|
||||
await refreshTokenDoc.remove();
|
||||
await refreshTokenDoc.deleteOne();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -91,7 +87,7 @@ export const refreshAuth = async (data: IRefreshTokens) => {
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
await refreshTokenDoc.remove();
|
||||
await refreshTokenDoc.deleteOne();
|
||||
return tokenService.generateAuthTokens(user);
|
||||
} catch (error) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
|
||||
@@ -99,21 +95,32 @@ export const refreshAuth = async (data: IRefreshTokens) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password
|
||||
* @param {string} resetPasswordToken
|
||||
* @param {string} newPassword
|
||||
* Reset key
|
||||
* @param {string} resetKeyToken
|
||||
* @param {string} publicKey
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const resetPassword = async (resetPasswordToken, newPassword) => {
|
||||
export const resetKey = async (resetKeyToken, publicKey) => {
|
||||
try {
|
||||
const resetPasswordTokenDoc = await tokenService.verifyToken(resetPasswordToken, tokenTypes.RESET_PASSWORD);
|
||||
const resetPasswordTokenDoc = await tokenService.verifyToken(resetKeyToken, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
const user = await userService.getUserById(resetPasswordTokenDoc.user);
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
await userService.updateUserById(user._id, { password: newPassword });
|
||||
|
||||
await blockchainService.changeKey({
|
||||
coopname: config.coopname,
|
||||
changer: config.service_username,
|
||||
username: user.username,
|
||||
public_key: publicKey,
|
||||
});
|
||||
|
||||
await userService.updateUserById(user._id, { public_key: publicKey });
|
||||
|
||||
await Token.deleteMany({ user: user._id, type: tokenTypes.RESET_PASSWORD });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Password reset failed');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,9 +6,30 @@ import EosApi from 'eosjs-api';
|
||||
import getInternalAction from '../utils/getInternalAction';
|
||||
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import { IUser } from '../models/user.model';
|
||||
import { GetAccountResult, GetInfoResult } from 'eosjs/dist/eosjs-rpc-interfaces';
|
||||
import config from '../config/config';
|
||||
|
||||
const rpc = new JsonRpc(process.env.BLOCKCHAIN_RPC as string, { fetch });
|
||||
|
||||
function hasActiveKey(account, publicKey) {
|
||||
const activePermissions = account.permissions.find((p: any) => p.perm_name === 'active');
|
||||
if (!activePermissions) return false;
|
||||
|
||||
return activePermissions.required_auth.keys.some((key: any) => key.key === publicKey);
|
||||
}
|
||||
|
||||
async function getBlockchainInfo(): Promise<GetInfoResult> {
|
||||
const api = getApi();
|
||||
|
||||
return await api.getInfo({});
|
||||
}
|
||||
|
||||
async function getBlockchainAccount(username): Promise<GetAccountResult> {
|
||||
const api = getApi();
|
||||
|
||||
return await api.getAccount(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить инстанс для осуществления транзакции в блокчейн.
|
||||
* @param {username} - авторизация от аккаунта
|
||||
@@ -22,7 +43,7 @@ async function getInstance(wif) {
|
||||
return api;
|
||||
}
|
||||
|
||||
async function getApi() {
|
||||
function getApi() {
|
||||
const options = {
|
||||
httpEndpoint: process.env.BLOCKCHAIN_RPC, // default, null for cold-storage
|
||||
verbose: false, // API logging
|
||||
@@ -245,6 +266,34 @@ async function failOrder(data) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function changeKey(data: RegistratorContract.Actions.ChangeKey.IChangeKey) {
|
||||
const eos = await getInstance(config.service_wif);
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.ChangeKey.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.service_username,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
];
|
||||
|
||||
await eos.transact(
|
||||
{
|
||||
actions,
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function getSoviet(coopname) {
|
||||
const api = await getApi();
|
||||
|
||||
@@ -271,4 +320,7 @@ export {
|
||||
failOrder,
|
||||
completeOrder,
|
||||
getSoviet,
|
||||
getBlockchainInfo,
|
||||
getBlockchainAccount,
|
||||
hasActiveKey,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { userService, blockchainService } from './index';
|
||||
import { Cooperative, RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import httpStatus from 'http-status';
|
||||
import logger from '../config/logger';
|
||||
|
||||
export const loadAgenda = async (coopname: string): Promise<Cooperative.Documents.IAgenda[]> => {
|
||||
const api = await blockchainService.getApi();
|
||||
@@ -85,7 +86,9 @@ export const loadContacts = async (coopname: string) => {
|
||||
if (coopAccount.meta) {
|
||||
try {
|
||||
announce = JSON.parse(coopAccount.meta);
|
||||
} catch (e: any) {}
|
||||
} catch (e: any) {
|
||||
logger.warn(`Ошибка при получении контактов: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import eosjsecc from 'eosjs-ecc';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import { getActions, getTables } from '../utils/getFetch';
|
||||
import * as coopService from './coop.service';
|
||||
const { verify, sha256 } = eosjsecc;
|
||||
import { getActions } from '../utils/getFetch';
|
||||
import config from '../config/config';
|
||||
import { Generator } from 'coopdoc-generator-ts';
|
||||
import type { IGenerate, IGetDocuments } from '../types';
|
||||
import type { IGenerate } from '../types';
|
||||
import { Cooperative, SovietContract } from 'cooptypes';
|
||||
import { User } from '../models';
|
||||
import { IUser } from '../models/user.model';
|
||||
|
||||
export const generator = new Generator();
|
||||
|
||||
@@ -99,8 +94,8 @@ export async function buildComplexDocument(
|
||||
|
||||
export const queryDocuments = async (
|
||||
filter: any,
|
||||
page: number = 1,
|
||||
limit: number = 100
|
||||
page = 1,
|
||||
limit = 100
|
||||
): Promise<Cooperative.Documents.IGetComplexDocuments> => {
|
||||
const actions = await getActions<SovietContract.Actions.Registry.NewResolved.INewResolved>(
|
||||
`${process.env.SIMPLE_EXPLORER_API}/get-actions`,
|
||||
@@ -116,7 +111,7 @@ export const queryDocuments = async (
|
||||
}
|
||||
);
|
||||
|
||||
let response: Cooperative.Documents.IGetComplexDocuments = {
|
||||
const response: Cooperative.Documents.IGetComplexDocuments = {
|
||||
results: [],
|
||||
page,
|
||||
limit,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createTransport } from 'nodemailer';
|
||||
import config from '../config/config';
|
||||
import logger from '../config/logger';
|
||||
|
||||
const { email, env } = config
|
||||
const { email, env } = config;
|
||||
|
||||
const transport = createTransport(email.smtp);
|
||||
|
||||
@@ -33,12 +33,14 @@ const sendEmail = async (to, subject, text) => {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const sendResetPasswordEmail = async (to, token) => {
|
||||
const subject = 'Reset password';
|
||||
// replace this url with the link to the reset password page of your front-end app
|
||||
const resetPasswordUrl = `http://link-to-app/reset-password?token=${token}`;
|
||||
const text = `Dear user,
|
||||
To reset your password, click on this link: ${resetPasswordUrl}
|
||||
If you did not request any password resets, then ignore this email.`;
|
||||
const subject = 'Восстановление доступа';
|
||||
|
||||
const resetPasswordUrl = `${config.base_url}/#/${config.coopname}/auth/reset-key?token=${token}`;
|
||||
const text = `Мы получили запрос на перевыпуск приватного ключа,
|
||||
Для перевыпуска нажмите на ссылку: ${resetPasswordUrl}. Время действия ссылки - 10 минут.
|
||||
|
||||
Если вы не запрашивали перевыпуск ключа - проигнорируйте это сообщение.`;
|
||||
|
||||
await sendEmail(to, subject, text);
|
||||
};
|
||||
|
||||
@@ -58,9 +60,4 @@ If you did not create an account, then ignore this email.`;
|
||||
await sendEmail(to, subject, text);
|
||||
};
|
||||
|
||||
export {
|
||||
transport,
|
||||
sendEmail,
|
||||
sendResetPasswordEmail,
|
||||
sendVerificationEmail,
|
||||
};
|
||||
export { transport, sendEmail, sendResetPasswordEmail, sendVerificationEmail };
|
||||
|
||||
@@ -7,3 +7,4 @@ export * as paymentService from './payment.service';
|
||||
export * as documentService from './document.service';
|
||||
export * as coopService from './coop.service';
|
||||
export * as wsService from './ws.service';
|
||||
export * as monoService from './mono.service';
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import httpStatus from 'http-status';
|
||||
import { Mono } from '../models';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import config from '../config/config';
|
||||
import logger from '../config/logger';
|
||||
import { IHealthStatus } from '../types';
|
||||
|
||||
export const init = async (): Promise<void> => {
|
||||
const mono = await Mono.findOne({ coopname: config.coopname });
|
||||
|
||||
if (!mono)
|
||||
await Mono.create({
|
||||
coopname: config.coopname,
|
||||
status: 'install',
|
||||
});
|
||||
|
||||
logger.info('MONO инициализирован');
|
||||
};
|
||||
|
||||
export const getMonoStatus = async (): Promise<IHealthStatus> => {
|
||||
const mono = await Mono.findOne({ coopname: config.coopname });
|
||||
|
||||
if (!mono) throw new ApiError(httpStatus.BAD_REQUEST, 'Установщик не найден');
|
||||
|
||||
return mono.status;
|
||||
};
|
||||
@@ -1 +1 @@
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -40,7 +40,7 @@ function getAmountPlusFee(amount: number, provider: string): number {
|
||||
}
|
||||
|
||||
export async function createDeposit(username: string, data: ICreateDeposit): Promise<ICreatedPayment> {
|
||||
let cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
const cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
|
||||
if (!cooperative) throw new Error('Кооператив не найден');
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function createDeposit(username: string, data: ICreateDeposit): Pro
|
||||
type: 'deposit',
|
||||
data: { username, provider: data.provider, quantity: data.quantity },
|
||||
});
|
||||
const internal_id = db_order._id;
|
||||
const internal_id = db_order.id;
|
||||
|
||||
let order_id = 0;
|
||||
|
||||
@@ -118,9 +118,9 @@ export async function createDeposit(username: string, data: ICreateDeposit): Pro
|
||||
}
|
||||
|
||||
export async function createInitialOrder(username: string, data: ICreateInitialPayment): Promise<ICreatedPayment> {
|
||||
let cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
const cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
|
||||
if (!cooperative) throw new Error('Кооператив не найден');
|
||||
if (!cooperative) throw new ApiError(httpStatus.BAD_REQUEST, 'Кооператив не найден');
|
||||
|
||||
const user = await getUserByUsername(username);
|
||||
let amount = '';
|
||||
@@ -139,7 +139,7 @@ export async function createInitialOrder(username: string, data: ICreateInitialP
|
||||
type: 'registration',
|
||||
data: { provider: data.provider, username, quantity: amount },
|
||||
});
|
||||
const internal_id = db_order._id;
|
||||
const internal_id = db_order.id;
|
||||
|
||||
if (data.provider === 'yookassa') {
|
||||
// 2. Используешь внутренний айди для получения ордера в системе платежей
|
||||
@@ -237,7 +237,7 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
memo: '',
|
||||
});
|
||||
|
||||
console.log('Зарегистрирован новый пользователь: ', user.username);
|
||||
logger.info('Зарегистрирован новый пользователь: ', user.username);
|
||||
user.status = 'registered';
|
||||
user.is_registered = true;
|
||||
} else if (order.type === 'deposit') {
|
||||
@@ -248,7 +248,7 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
memo: '',
|
||||
});
|
||||
const quantity = parseFloat(order.data.quantity);
|
||||
console.log(`Принят паевый взнос пользователя: ${user.username} на сумму ${order.data.quantity}`);
|
||||
logger.info(`Принят паевый взнос пользователя: ${user.username} на сумму ${order.data.quantity}`);
|
||||
}
|
||||
|
||||
await user.save();
|
||||
@@ -256,7 +256,7 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
order.delivered = true;
|
||||
await order.save();
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
logger.warn(e);
|
||||
order.error = e;
|
||||
order.delivered = false;
|
||||
await order.save();
|
||||
@@ -281,6 +281,7 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
logger.warn(`Ордер не найден: `, ipnBody);
|
||||
throw new Error(`Ордер не найден`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +113,9 @@ export const generateResetPasswordToken = async (email) => {
|
||||
throw new ApiError(httpStatus.NOT_FOUND, 'No users found with this email');
|
||||
}
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = generateToken(user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await saveToken(resetPasswordToken, user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
return resetPasswordToken;
|
||||
const resetKeyToken = generateToken(user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await saveToken(resetKeyToken, user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
return resetKeyToken;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import http from 'http-status';
|
||||
import { User } from '../models';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import { generator } from './document.service';
|
||||
import { ICreateUser, IJoinCooperative } from '../types/user.validation';
|
||||
import { ICreateUser, IJoinCooperative } from '../types/auto-generated/user.validation';
|
||||
import ecc from 'eosjs-ecc';
|
||||
import { PublicKey, Signature } from '@wharfkit/antelope';
|
||||
import faker from 'faker';
|
||||
@@ -191,6 +191,6 @@ export const deleteUserById = async (username) => {
|
||||
if (!user) {
|
||||
throw new ApiError(http.NOT_FOUND, 'Пользователь не найден');
|
||||
}
|
||||
await user.remove();
|
||||
await user.deleteOne();
|
||||
return user;
|
||||
};
|
||||
|
||||
+9
-12
@@ -9,7 +9,8 @@ export interface IForgotPassword {
|
||||
|
||||
export interface ILogin {
|
||||
email: string;
|
||||
password: string;
|
||||
now: string;
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface ILogout {
|
||||
@@ -20,11 +21,8 @@ export interface IRefreshTokens {
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export interface IResetPasswordBody {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface IResetPasswordQuery {
|
||||
export interface IResetKeyBody {
|
||||
public_key: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
@@ -32,7 +30,7 @@ export interface IVerifyEmail {
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface RForgotPassword {
|
||||
export interface RForgotKey {
|
||||
body: {
|
||||
email: string;
|
||||
};
|
||||
@@ -41,7 +39,8 @@ export interface RForgotPassword {
|
||||
export interface RLogin {
|
||||
body: {
|
||||
email: string;
|
||||
password: string;
|
||||
now: string;
|
||||
signature: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,11 +56,9 @@ export interface RRefreshTokens {
|
||||
};
|
||||
}
|
||||
|
||||
export interface RResetPassword {
|
||||
export interface RResetKey {
|
||||
body: {
|
||||
password: string;
|
||||
};
|
||||
query: {
|
||||
public_key: string;
|
||||
token: string;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export * from './auth.validation';
|
||||
export * from './coop.validation';
|
||||
export * from './document.validation';
|
||||
export * from './notify.validation';
|
||||
export * from './payment.validation';
|
||||
export * from './user.validation';
|
||||
@@ -122,7 +122,7 @@ export interface IAction {
|
||||
elapsed: number;
|
||||
}
|
||||
|
||||
export interface IExtendedTable extends ITable {}
|
||||
export type IExtendedTable = ITable;
|
||||
|
||||
export interface IExtendedAction extends IAction {
|
||||
user: IIndividualData | IEntrepreneurData | IOrganizationData | null;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export interface IGenerate {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
action: string;
|
||||
block_num?: number;
|
||||
code: string;
|
||||
coopname: string;
|
||||
created_at?: string;
|
||||
generator?: string;
|
||||
lang?: 'ru';
|
||||
timezone?: string;
|
||||
username: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface IGetDocuments {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
filter?: {
|
||||
receiver?: string;
|
||||
};
|
||||
limit?: number;
|
||||
page?: number;
|
||||
sortBy?: string;
|
||||
}
|
||||
|
||||
export interface RGenerate {
|
||||
body: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
action: string;
|
||||
block_num?: number;
|
||||
code: string;
|
||||
coopname: string;
|
||||
created_at?: string;
|
||||
generator?: string;
|
||||
lang?: 'ru';
|
||||
timezone?: string;
|
||||
username: string;
|
||||
version?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RGetDocuments {
|
||||
query?: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
filter?: {
|
||||
receiver?: string;
|
||||
};
|
||||
limit?: number;
|
||||
page?: number;
|
||||
sortBy?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RGetMyDocuments {
|
||||
query?: {
|
||||
username?: string;
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,3 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export * from './auth.validation';
|
||||
export * from './coop.validation';
|
||||
export * from './document.validation';
|
||||
export * from './notify.validation';
|
||||
export * from './payment.validation';
|
||||
export * from './user.validation';
|
||||
export * from './auto-generated';
|
||||
export * from './mono.types';
|
||||
export * from './common';
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GetInfoResult } from 'eosjs/dist/eosjs-rpc-interfaces';
|
||||
|
||||
export type IHealthStatus = 'install' | 'active' | 'maintenance';
|
||||
|
||||
export interface IHealthResponse {
|
||||
status: IHealthStatus;
|
||||
blockchain: GetInfoResult;
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export interface ICreateDeposit {
|
||||
provider: 'yookassa';
|
||||
quantity: string;
|
||||
}
|
||||
|
||||
export interface ICreateInitialPayment {
|
||||
provider: 'yookassa';
|
||||
}
|
||||
|
||||
export interface IRecieveIPN {
|
||||
event: string;
|
||||
object: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
amount: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
currency: string;
|
||||
value: string;
|
||||
};
|
||||
authorization_details?: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
auth_code?: string;
|
||||
rrn?: string;
|
||||
three_d_secure?: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
applied?: boolean;
|
||||
};
|
||||
};
|
||||
created_at?: string;
|
||||
description?: string;
|
||||
expires_at?: string;
|
||||
id: string;
|
||||
income_amount: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
currency: string;
|
||||
value: string;
|
||||
};
|
||||
metadata?: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
};
|
||||
paid: boolean;
|
||||
payment_method: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
card?: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
card_type?: string;
|
||||
expiry_month?: string;
|
||||
expiry_year?: string;
|
||||
first6?: string;
|
||||
issuer_country?: string;
|
||||
issuer_name?: string;
|
||||
last4?: string;
|
||||
};
|
||||
id: string;
|
||||
saved?: boolean;
|
||||
title?: string;
|
||||
type: string;
|
||||
};
|
||||
refundable?: boolean;
|
||||
refunded_amount: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
currency: string;
|
||||
value: string;
|
||||
};
|
||||
status: string;
|
||||
test?: boolean;
|
||||
};
|
||||
type: string;
|
||||
}
|
||||
@@ -14,28 +14,26 @@ export const IForgotPassword = Joi.object({
|
||||
email: Joi.string().email().required(),
|
||||
});
|
||||
|
||||
export const RForgotPassword = Joi.object({
|
||||
export const RForgotKey = Joi.object({
|
||||
body: IForgotPassword.required(),
|
||||
});
|
||||
|
||||
// Внутренние параметры
|
||||
export const ILogin = Joi.object({
|
||||
signature: Joi.string().required(),
|
||||
email: Joi.string().email().required(),
|
||||
password: Joi.string().required(),
|
||||
now: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const ILogout = Joi.object({
|
||||
refreshToken: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const IResetPasswordQuery = Joi.object({
|
||||
export const IResetKeyBody = Joi.object({
|
||||
public_key: Joi.string().required(),
|
||||
token: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const IResetPasswordBody = Joi.object({
|
||||
password: Joi.string().required().custom(password),
|
||||
});
|
||||
|
||||
export const IVerifyEmail = Joi.object({
|
||||
token: Joi.string().required(),
|
||||
});
|
||||
@@ -49,9 +47,8 @@ export const RLogout = Joi.object({
|
||||
body: ILogout.required(),
|
||||
});
|
||||
|
||||
export const RResetPassword = Joi.object({
|
||||
query: IResetPasswordQuery.required(),
|
||||
body: IResetPasswordBody.required(),
|
||||
export const RResetKey = Joi.object({
|
||||
body: IResetKeyBody.required(),
|
||||
});
|
||||
|
||||
export const RVerifyEmail = Joi.object({
|
||||
|
||||
+6
-6
@@ -27,12 +27,12 @@ export const installInitialCooperativeData = async () => {
|
||||
is_cooperative: true,
|
||||
is_branched: false,
|
||||
coop_type: 'conscoop',
|
||||
registration: '2.0000 TRUB',
|
||||
initial: '1.0000 TRUB',
|
||||
minimum: '1.0000 TRUB',
|
||||
org_registration: '20.0000 TRUB',
|
||||
org_initial: '10.0000 TRUB',
|
||||
org_minimum: '10.0000 TRUB',
|
||||
registration: '200.0000 TRUB',
|
||||
initial: '100.0000 TRUB',
|
||||
minimum: '100.0000 TRUB',
|
||||
org_registration: '2000.0000 TRUB',
|
||||
org_initial: '1000.0000 TRUB',
|
||||
org_minimum: '1000.0000 TRUB',
|
||||
} as RegistratorContract.Tables.Cooperatives.ICooperative);
|
||||
|
||||
await insertDelta(delta1);
|
||||
|
||||
@@ -35,6 +35,7 @@ export const admin: testUser = {
|
||||
email: email1,
|
||||
password,
|
||||
status: 'active',
|
||||
has_account: false,
|
||||
message: '',
|
||||
is_registered: true,
|
||||
role: 'chairman',
|
||||
@@ -68,6 +69,7 @@ export const userOne: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email2,
|
||||
password,
|
||||
has_account: false,
|
||||
status: 'active',
|
||||
message: '',
|
||||
is_registered: true,
|
||||
@@ -101,6 +103,7 @@ const usernameTwo = generateUsername();
|
||||
export const userTwo: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email3,
|
||||
has_account: false,
|
||||
password,
|
||||
status: 'active',
|
||||
message: '',
|
||||
@@ -134,6 +137,7 @@ export const userTwo: testUser = {
|
||||
export const chairman: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email4,
|
||||
has_account: false,
|
||||
password,
|
||||
status: 'active',
|
||||
message: '',
|
||||
@@ -168,6 +172,7 @@ export const voskhod: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email5,
|
||||
password,
|
||||
has_account: false,
|
||||
status: 'active',
|
||||
message: '',
|
||||
is_registered: true,
|
||||
|
||||
@@ -16,6 +16,8 @@ import { tokenTypes } from '../../src/config/tokens';
|
||||
import { userOne, admin, insertUsers } from '../fixtures/user.fixture';
|
||||
import { userOneAccessToken, adminAccessToken } from '../fixtures/token.fixture';
|
||||
import { generateUsername } from '../utils/generateUsername';
|
||||
import { getBlockchainInfo } from '../../src/services/blockchain.service';
|
||||
import { Bytes, Checksum256, PrivateKey } from '@wharfkit/session';
|
||||
|
||||
setupTestDB();
|
||||
|
||||
@@ -116,9 +118,19 @@ describe('Auth routes', () => {
|
||||
describe('POST /v1/auth/login', () => {
|
||||
test('should return 200 and login user if email and password match', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
const now = (await getBlockchainInfo()).head_block_time;
|
||||
|
||||
const privateKey = PrivateKey.fromString('5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3');
|
||||
|
||||
const bytes = Bytes.fromString(now, 'utf8');
|
||||
const checksum = Checksum256.hash(bytes);
|
||||
const signature = privateKey.signDigest(checksum);
|
||||
|
||||
const loginCredentials = {
|
||||
email: userOne.email,
|
||||
password: userOne.password,
|
||||
now,
|
||||
signature,
|
||||
};
|
||||
|
||||
const res = await request(app).post('/v1/auth/login').send(loginCredentials);
|
||||
@@ -140,27 +152,38 @@ describe('Auth routes', () => {
|
||||
});
|
||||
|
||||
test('should return 401 error if there are no users with that email', async () => {
|
||||
const now = (await getBlockchainInfo()).head_block_time;
|
||||
|
||||
const loginCredentials = {
|
||||
now,
|
||||
email: userOne.email,
|
||||
password: userOne.password,
|
||||
signature: userOne.password,
|
||||
};
|
||||
|
||||
const res = await request(app).post('/v1/auth/login').send(loginCredentials).expect(httpStatus.UNAUTHORIZED);
|
||||
|
||||
expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Incorrect email or password' });
|
||||
expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Пользователь не найден' });
|
||||
});
|
||||
|
||||
test('should return 401 error if password is wrong', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const loginCredentials = {
|
||||
email: userOne.email,
|
||||
password: 'wrongPassword1',
|
||||
};
|
||||
|
||||
const res = await request(app).post('/v1/auth/login').send(loginCredentials).expect(httpStatus.UNAUTHORIZED);
|
||||
|
||||
expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Incorrect email or password' });
|
||||
});
|
||||
// test('should return 401 error if signature is wrong', async () => {
|
||||
// Восстановить проверку. Необходимо подложить аккаунт тестового юзера в коде вместо обращения к блокчейну.
|
||||
// await insertUsers([userOne]);
|
||||
// const now = (await getBlockchainInfo()).head_block_time;
|
||||
// const privateKey = PrivateKey.fromString('5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3');
|
||||
// const bytes = Bytes.fromString(now, 'utf8');
|
||||
// const checksum = Checksum256.hash(bytes);
|
||||
// const signature = privateKey.signDigest(checksum);
|
||||
// const now2 = '2024-08-02T11:50:40.000';
|
||||
// const loginCredentials = {
|
||||
// email: userOne.email,
|
||||
// now: now2,
|
||||
// signature,
|
||||
// };
|
||||
// const res = await request(app).post('/v1/auth/login').send(loginCredentials);
|
||||
// console.log(res.body);
|
||||
// expect(res.status).toBe(httpStatus.UNAUTHORIZED);
|
||||
// expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Incorrect email or password' });
|
||||
// });
|
||||
});
|
||||
|
||||
describe('POST /v1/auth/logout', () => {
|
||||
@@ -260,7 +283,7 @@ describe('Auth routes', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /v1/auth/forgot-password', () => {
|
||||
describe('POST /v1/auth/lost-key', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(emailService.transport, 'sendMail').mockResolvedValue();
|
||||
});
|
||||
@@ -269,38 +292,35 @@ describe('Auth routes', () => {
|
||||
await insertUsers([userOne]);
|
||||
const sendResetPasswordEmailSpy = jest.spyOn(emailService, 'sendResetPasswordEmail');
|
||||
|
||||
await request(app).post('/v1/auth/forgot-password').send({ email: userOne.email }).expect(httpStatus.NO_CONTENT);
|
||||
await request(app).post('/v1/auth/lost-key').send({ email: userOne.email }).expect(httpStatus.NO_CONTENT);
|
||||
|
||||
expect(sendResetPasswordEmailSpy).toHaveBeenCalledWith(userOne.email, expect.any(String));
|
||||
const resetPasswordToken = sendResetPasswordEmailSpy.mock.calls[0][1];
|
||||
const dbResetPasswordTokenDoc = await Token.findOne({ token: resetPasswordToken, user: userOne._id.toString() });
|
||||
const resetKeyToken = sendResetPasswordEmailSpy.mock.calls[0][1];
|
||||
const dbResetPasswordTokenDoc = await Token.findOne({ token: resetKeyToken, user: userOne._id.toString() });
|
||||
expect(dbResetPasswordTokenDoc).toBeDefined();
|
||||
});
|
||||
|
||||
test('should return 400 if email is missing', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
const res = await request(app).post('/v1/auth/forgot-password').send();
|
||||
const res = await request(app).post('/v1/auth/lost-key').send();
|
||||
|
||||
expect(res.status).toBe(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 404 if email does not belong to any user', async () => {
|
||||
await request(app).post('/v1/auth/forgot-password').send({ email: userOne.email }).expect(httpStatus.NOT_FOUND);
|
||||
await request(app).post('/v1/auth/lost-key').send({ email: userOne.email }).expect(httpStatus.NOT_FOUND);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /v1/auth/reset-password', () => {
|
||||
describe('POST /v1/auth/reset-key', () => {
|
||||
test('should return 204 and reset the password', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
const res = await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' });
|
||||
const res = await request(app).post('/v1/auth/reset-key').send({ token: resetKeyToken, password: 'password2' });
|
||||
|
||||
expect(res.status).toBe(httpStatus.NO_CONTENT);
|
||||
|
||||
@@ -322,71 +342,65 @@ describe('Auth routes', () => {
|
||||
test('should return 400 if reset password token is missing', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
await request(app).post('/v1/auth/reset-password').send({ password: 'password2' }).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/auth/reset-key').send({ password: 'password2' }).expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 401 if reset password token is blacklisted', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD, true);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD, true);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV' })
|
||||
.expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 if reset password token is expired', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().subtract(1, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV' })
|
||||
.expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 if user is not found', async () => {
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV' })
|
||||
.expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 400 if password is missing or invalid', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
await request(app).post('/v1/auth/reset-password').query({ token: resetPasswordToken }).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/auth/reset-key').send({ token: resetKeyToken }).expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'short1' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'stort' })
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'password' })
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: '11111111' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: '11111111' })
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('Проверка получения документов', () => {
|
||||
statement: signedDocument,
|
||||
};
|
||||
|
||||
let joincoop_result = await request(app)
|
||||
const joincoop_result = await request(app)
|
||||
.post('/v1/users/join-cooperative')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(joinCoopData);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
"lib": ["ES2015"],
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"esModuleInterop": true,
|
||||
"allowJs": false,
|
||||
"noImplicitAny": false,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.73](https://github.com/copenomics/coopdoc-generator-ts/compare/coopdoc-generator-ts@1.0.73-alpha.0...coopdoc-generator-ts@1.0.73) (2024-08-05)
|
||||
|
||||
**Note:** Version bump only for package coopdoc-generator-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.72](https://github.com/copenomics/coopdoc-generator-ts/compare/coopdoc-generator-ts@1.0.72-alpha.3...coopdoc-generator-ts@1.0.72) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package coopdoc-generator-ts
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coopdoc-generator-ts",
|
||||
"type": "module",
|
||||
"version": "1.0.72",
|
||||
"version": "1.0.73",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
|
||||
@@ -40,8 +40,18 @@ export class PDFService implements IPDFService {
|
||||
|
||||
private static async generatePDFBuffer(htmlContent: string): Promise<Uint8Array> {
|
||||
const browser = await puppeteer.launch({
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-gpu',
|
||||
'--disable-extensions',
|
||||
'--disable-software-rasterizer',
|
||||
],
|
||||
timeout: 120000, // увеличиваем общее время ожидания до 2 минут
|
||||
protocolTimeout: 120000, // увеличиваем время ожидания протокола до 2 минут
|
||||
})
|
||||
|
||||
const page = await browser.newPage()
|
||||
|
||||
// const data = await inlineCss(htmlContent, { url: 'about:blank' })
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.18](https://github.com/coopenomics/cooptypes/compare/cooptypes@1.0.18-alpha.0...cooptypes@1.0.18) (2024-08-05)
|
||||
|
||||
**Note:** Version bump only for package cooptypes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.17](https://github.com/coopenomics/cooptypes/compare/cooptypes@1.0.17-alpha.3...cooptypes@1.0.17) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package cooptypes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cooptypes",
|
||||
"type": "module",
|
||||
"version": "1.0.17",
|
||||
"version": "1.0.18",
|
||||
"description": "_description_",
|
||||
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -30,6 +30,8 @@ export interface IBalancesBase {
|
||||
}
|
||||
|
||||
export interface IChangekey {
|
||||
coopname: IName
|
||||
changer: IName
|
||||
username: IName
|
||||
public_key: IPublicKey
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.0.22](https://github.com/copenomics/coop-notificator/compare/coop-notificator@0.0.22-alpha.0...coop-notificator@0.0.22) (2024-08-05)
|
||||
|
||||
**Note:** Version bump only for package coop-notificator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.0.21](https://github.com/copenomics/coop-notificator/compare/coop-notificator@0.0.21-alpha.3...coop-notificator@0.0.21) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package coop-notificator
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coop-notificator",
|
||||
"type": "module",
|
||||
"version": "0.0.21",
|
||||
"version": "0.0.22",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.1.1",
|
||||
"description": "_description_",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.1.33](https://github.com/coopenomics/monocoop/compare/terminal@0.1.33-alpha.0...terminal@0.1.33) (2024-08-05)
|
||||
|
||||
**Note:** Version bump only for package terminal
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.32](https://github.com/coopenomics/monocoop/compare/terminal@0.1.32-alpha.0...terminal@0.1.32) (2024-07-31)
|
||||
|
||||
**Note:** Version bump only for package terminal
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.31](https://github.com/coopenomics/monocoop/compare/terminal@0.1.31-alpha.0...terminal@0.1.31) (2024-07-26)
|
||||
|
||||
**Note:** Version bump only for package terminal
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "terminal",
|
||||
"version": "0.1.31",
|
||||
"version": "0.1.33",
|
||||
"description": "A Terminal Project",
|
||||
"productName": "Terminal App",
|
||||
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
||||
@@ -34,6 +34,7 @@
|
||||
"@wharfkit/session": "^1.3.1",
|
||||
"@wharfkit/wallet-plugin-privatekey": "^1.1.0",
|
||||
"axios": "^1.2.1",
|
||||
"coopback": "workspace:*",
|
||||
"cooptypes": "workspace:*",
|
||||
"dotenv": "^16.4.5",
|
||||
"email-regex": "^5.0.0",
|
||||
|
||||
@@ -102,6 +102,7 @@ module.exports = configure(function (/* ctx */) {
|
||||
eslint: {
|
||||
lintCommand: 'eslint "./**/*.{js,ts,mjs,cjs,vue}"',
|
||||
},
|
||||
overlay: false
|
||||
},
|
||||
{ server: false },
|
||||
],
|
||||
@@ -111,6 +112,7 @@ module.exports = configure(function (/* ctx */) {
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
|
||||
devServer: {
|
||||
// https: true
|
||||
vueDevtools: false,
|
||||
open: false, // opens browser window automatically
|
||||
port: 3005,
|
||||
hmr:{
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { RenderError } from '@quasar/app-vite';
|
||||
import { ssrMiddleware } from 'quasar/wrappers';
|
||||
|
||||
// This middleware should execute as last one
|
||||
// since it captures everything and tries to
|
||||
// render the page with Vue
|
||||
|
||||
export default ssrMiddleware(({ app, resolve, render, serve }) => {
|
||||
// we capture any other Express route and hand it
|
||||
// over to Vue and Vue Router to render our page
|
||||
app.get(resolve.urlPath('*'), (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
|
||||
render(/* the ssrContext: */ { req, res })
|
||||
.then((html) => {
|
||||
// now let's send the rendered html to the client
|
||||
res.send(html);
|
||||
})
|
||||
.catch((err: RenderError) => {
|
||||
// oops, we had an error while rendering the page
|
||||
|
||||
// we were told to redirect to another URL
|
||||
if (err.url) {
|
||||
if (err.code) {
|
||||
res.redirect(err.code, err.url);
|
||||
} else {
|
||||
res.redirect(err.url);
|
||||
}
|
||||
} else if (err.code === 404) {
|
||||
// hmm, Vue Router could not find the requested route
|
||||
|
||||
// Should reach here only if no "catch-all" route
|
||||
// is defined in /src/routes
|
||||
res.status(404).send('404 | Page Not Found');
|
||||
} else if (process.env.DEV) {
|
||||
// well, we treat any other code as error;
|
||||
// if we're in dev mode, then we can use Quasar CLI
|
||||
// to display a nice error page that contains the stack
|
||||
// and other useful information
|
||||
|
||||
// serve.error is available on dev only
|
||||
serve.error({ err, req, res });
|
||||
} else {
|
||||
// we're in production, so we should have another method
|
||||
// to display something to the client when we encounter an error
|
||||
// (for security reasons, it's not ok to display the same wealth
|
||||
// of information as we do in development)
|
||||
|
||||
// Render Error Page on production or
|
||||
// create a route (/src/routes) for an error page and redirect to it
|
||||
res.status(500).send('500 | Internal Server Error');
|
||||
|
||||
if (process.env.DEBUGGING) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||
import "quasar/dist/types/feature-flag";
|
||||
|
||||
declare module "quasar/dist/types/feature-flag" {
|
||||
interface QuasarFeatureFlags {
|
||||
ssr: true;
|
||||
}
|
||||
}
|
||||
@@ -3,24 +3,62 @@ router-view(v-if="isLoaded")
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
console.log('on start')
|
||||
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { FailAlert } from 'src/shared/api/alerts'
|
||||
import { handleException } from 'src/shared/api';
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import 'src/app/styles/quasar-variables.sass'
|
||||
import { useDesktopStore } from 'src/entities/Desktop/model';
|
||||
import { Cookies, LocalStorage, QSpinner, useQuasar } from 'quasar';
|
||||
import { COOPNAME } from 'src/shared/config';
|
||||
import { useCardStore } from './providers/card/store';
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const desktop = useDesktopStore()
|
||||
const card = useCardStore()
|
||||
|
||||
const isLoaded = ref(false)
|
||||
|
||||
const enableLoading = () => {
|
||||
$q.loading.show({
|
||||
spinner: QSpinner,
|
||||
message: 'Цифровой Кооператив ушел оффлайн, но обещал вернуться. Пожалуйста, проверьте ваше интернет-соединение. А мы попытаемся его восстановить.',
|
||||
spinnerSize: 50,
|
||||
})
|
||||
}
|
||||
|
||||
const disableLoading = () => {
|
||||
$q.loading.hide()
|
||||
}
|
||||
|
||||
const onlineCheck = () => {
|
||||
if (desktop.online === false) {
|
||||
enableLoading()
|
||||
} else {
|
||||
card.initWallet()
|
||||
disableLoading()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onlineCheck()
|
||||
|
||||
// Watch the online status
|
||||
watch(() => desktop.online, () => onlineCheck())
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
|
||||
removeLoader()
|
||||
isLoaded.value = true
|
||||
|
||||
const ref = Cookies.get('referer') || String(route.query.r || '')
|
||||
|
||||
if (ref) {
|
||||
LocalStorage.setItem(`${COOPNAME}:referer`, ref)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
|
||||
console.error(e)
|
||||
@@ -29,6 +67,7 @@ onMounted(async () => {
|
||||
isLoaded.value = true
|
||||
removeLoader()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
function removeLoader() {
|
||||
@@ -40,3 +79,8 @@ function removeLoader() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.q-loading__backdrop {
|
||||
opacity: 1 !important
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { useSessionStore } from 'src/entities/Session';
|
||||
import { useCurrentUserStore } from 'src/entities/User';
|
||||
import { COOPNAME } from 'src/shared/config';
|
||||
|
||||
const namespace = 'cardstore'
|
||||
|
||||
interface ICardStore {
|
||||
initWallet: () => Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
export const useCardStore = defineStore(namespace, (): ICardStore => {
|
||||
const currentUser = useCurrentUserStore();
|
||||
const session = useSessionStore();
|
||||
|
||||
async function initWallet(): Promise<void> {
|
||||
if (!session.isAuth && !currentUser.userAccount){
|
||||
await session.init()
|
||||
if (session.isAuth)
|
||||
try{
|
||||
await currentUser.loadProfile(session.username, COOPNAME)
|
||||
} catch(e: any){
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initWallet
|
||||
}
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import { route } from 'quasar/wrappers';
|
||||
import {
|
||||
RouteLocationNormalizedGeneric,
|
||||
RouteRecordRaw,
|
||||
createMemoryHistory,
|
||||
createRouter,
|
||||
createWebHashHistory,
|
||||
@@ -9,20 +10,36 @@ import {
|
||||
import { routes } from 'src/app/providers/routes';
|
||||
import { useSessionStore } from 'src/entities/Session';
|
||||
import {
|
||||
AUTHORIZED_HOME_PAGE,
|
||||
COOPNAME,
|
||||
NOT_AUTHORIZED_HOME_PAGE,
|
||||
} from 'src/shared/config';
|
||||
import { IUserAccountData, useCurrentUserStore } from 'src/entities/User';
|
||||
import { useMenuStore } from 'src/entities/Menu';
|
||||
import { useDesktopStore } from '../../entities/Desktop/model';
|
||||
import { useCardStore } from './card/store';
|
||||
|
||||
export default route(function (/* { store, ssrContext } */) {
|
||||
export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
|
||||
export default route(async function (/* { store, ssrContext } */) {
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: process.env.VUE_ROUTER_MODE === 'history'
|
||||
? createWebHistory
|
||||
: createWebHashHistory;
|
||||
|
||||
|
||||
const desktops = useDesktopStore()
|
||||
await desktops.healthCheck()
|
||||
|
||||
const { initWallet } = useCardStore()
|
||||
await initWallet()
|
||||
|
||||
await desktops.loadDesktops()
|
||||
await desktops.setActiveDesktop(desktops.defaultDesktopHash)
|
||||
|
||||
desktops.currentDesktop?.routes.forEach(route => {
|
||||
routes[0].children?.push(route as RouteRecordRaw)
|
||||
})
|
||||
|
||||
const router = createRouter({
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||
routes,
|
||||
@@ -39,6 +56,7 @@ export default route(function (/* { store, ssrContext } */) {
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
router.beforeEach(
|
||||
async (
|
||||
to: RouteLocationNormalizedGeneric,
|
||||
@@ -46,31 +64,26 @@ export default route(function (/* { store, ssrContext } */) {
|
||||
next: any
|
||||
) => {
|
||||
|
||||
desktops.healthCheck()
|
||||
const { initWallet } = useCardStore()
|
||||
await initWallet()
|
||||
|
||||
const currentUser = useCurrentUserStore();
|
||||
const session = useSessionStore();
|
||||
|
||||
if (!session.isAuth && !currentUser.userAccount){
|
||||
await session.init()
|
||||
if (session.isAuth)
|
||||
try{
|
||||
await currentUser.loadProfile(session.username, COOPNAME)
|
||||
} catch(e: any){
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const menuStore = useMenuStore()
|
||||
menuStore.setRoutes(routes)
|
||||
|
||||
if (to.name == 'index') {
|
||||
console.log('7')
|
||||
/** проверяем установлено ли приложение и не переход ли это на страницу установки */
|
||||
if (desktops.health?.status === 'install' && to.name !== 'install') {
|
||||
/** переадресуем на страницу установки */
|
||||
next({name: 'install', params: { coopname: COOPNAME }})
|
||||
return
|
||||
} else if (to.name == 'index') {
|
||||
/** Если переходим на Главную, то определяем её на основе рабочего стола и имеющейся авторизации */
|
||||
if (session.isAuth){
|
||||
next({ name: AUTHORIZED_HOME_PAGE, params: { coopname: COOPNAME } });
|
||||
next({ name: desktops.currentDesktop?.authorizedHome, params: { coopname: COOPNAME } });
|
||||
return
|
||||
} else {
|
||||
next({
|
||||
name: NOT_AUTHORIZED_HOME_PAGE,
|
||||
name: desktops.currentDesktop?.nonAuthorizedHome,
|
||||
params: { coopname: COOPNAME },
|
||||
});
|
||||
return;
|
||||
@@ -78,10 +91,15 @@ export default route(function (/* { store, ssrContext } */) {
|
||||
}
|
||||
|
||||
if (hasAccess(to, currentUser.userAccount)) {
|
||||
/** Если переход на любую другую страницу, то проверяем права доступа к ней на основе роли и продолжаем загрузку */
|
||||
next(); // Продолжить переход, если доступ разрешен
|
||||
return
|
||||
} else {
|
||||
next({ name: 'permissionDenied' }); // Перенаправить на страницу с сообщением о недостатке прав доступа
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -2,24 +2,13 @@ import layout from 'src/pages/_layouts/default.vue';
|
||||
import index from 'src/pages/index.vue';
|
||||
import blank from 'src/pages/blank/blank.vue';
|
||||
import permissionDenied from 'src/pages/_layouts/permissionDenied.vue';
|
||||
|
||||
// import sovietPage from 'src/components/soviet/index.vue'
|
||||
import { CoopSettingsPage } from 'src/pages/CoopSettings';
|
||||
import { UserHomePage } from 'src/pages/UserHome/ui';
|
||||
import { ModerationPage } from 'src/pages/marketplace/Moderation';
|
||||
import { CreateParentOfferPage } from 'src/pages/marketplace/CreateParentOffer';
|
||||
import { ShowcasePage } from 'src/pages/marketplace/Showcase';
|
||||
import { SuppliesListPage } from 'src/pages/marketplace/SuppliesList';
|
||||
import { UserParentOffersPage } from 'src/pages/marketplace/UserParentOffers';
|
||||
import { UserSuppliesListPage } from 'src/pages/marketplace/UserSuppliesList';
|
||||
import { MainMarketplacePage } from 'src/pages/marketplace/MainPage';
|
||||
import { SignUpPage } from 'src/pages/SignUp';
|
||||
import { SignInPage } from 'src/pages/SignIn';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import decisions from 'src/components/soviet/decisions.vue';
|
||||
import participants from 'src/components/soviet/participants.vue';
|
||||
import documents from 'src/components/soviet/documents.vue';
|
||||
import { ContactsPage } from 'src/pages/Contacts';
|
||||
import { InstallCooperativePage } from 'src/pages/Install';
|
||||
import { LostKeyPage } from 'src/pages/Auth/LostKey/ui';
|
||||
import { ResetKeyPage } from 'src/pages/Auth/ResetKey';
|
||||
|
||||
|
||||
const baseRoutes = [
|
||||
{
|
||||
@@ -31,143 +20,6 @@ const baseRoutes = [
|
||||
name: 'index',
|
||||
component: index,
|
||||
},
|
||||
|
||||
//страница кошелька пользователя
|
||||
{
|
||||
path: '/:coopname/home',
|
||||
name: 'home',
|
||||
component: UserHomePage,
|
||||
children: [],
|
||||
meta: {
|
||||
is_desktop_menu: true,
|
||||
title: 'Профиль',
|
||||
icon: 'fa-solid fa-id-card',
|
||||
roles: [],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
//страница управления кооперативом
|
||||
{
|
||||
path: ':coopname/settings',
|
||||
name: 'soviet',
|
||||
component: CoopSettingsPage,
|
||||
meta: {
|
||||
is_desktop_menu: true,
|
||||
title: 'Настройки',
|
||||
icon: 'fa-solid fa-cog',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':coopname/agenda',
|
||||
name: 'agenda',
|
||||
component: decisions,
|
||||
meta: {
|
||||
is_desktop_menu: true,
|
||||
title: 'Повестка',
|
||||
icon: 'fa-solid fa-check-to-slot',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':coopname/participants',
|
||||
name: 'participants',
|
||||
component: participants,
|
||||
meta: {
|
||||
is_desktop_menu: true,
|
||||
title: 'Пайщики',
|
||||
icon: 'fa-solid fa-users',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':coopname/documents',
|
||||
name: 'documents',
|
||||
component: documents,
|
||||
meta: {
|
||||
is_desktop_menu: true,
|
||||
title: 'Документы',
|
||||
icon: 'fa-solid fa-file-invoice',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':coopname/auth/signin',
|
||||
name: 'signin',
|
||||
component: SignInPage,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: ':coopname/auth/signup',
|
||||
name: 'signup',
|
||||
component: SignUpPage,
|
||||
children: [],
|
||||
},
|
||||
|
||||
{
|
||||
path: '/:coopname/marketplace',
|
||||
name: 'marketplace',
|
||||
component: MainMarketplacePage,
|
||||
children: [
|
||||
{
|
||||
path: 'moderation',
|
||||
name: 'marketplace-moderation',
|
||||
component: ModerationPage,
|
||||
},
|
||||
{
|
||||
path: 'create-offer',
|
||||
name: 'marketplace-create-offer',
|
||||
component: CreateParentOfferPage,
|
||||
},
|
||||
{
|
||||
path: 'showcase',
|
||||
name: 'marketplace-showcase',
|
||||
component: ShowcasePage,
|
||||
children: [
|
||||
{
|
||||
path: ':id',
|
||||
name: 'marketplace-showcase-id',
|
||||
component: ShowcasePage,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'user-offers',
|
||||
name: 'marketplace-user-offers',
|
||||
component: UserParentOffersPage,
|
||||
children: [
|
||||
{
|
||||
path: ':id',
|
||||
name: 'marketplace-user-offer-id',
|
||||
component: UserParentOffersPage,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'supplies',
|
||||
name: 'marketplace-supplies',
|
||||
component: SuppliesListPage,
|
||||
},
|
||||
{
|
||||
path: 'user-supplies',
|
||||
name: 'marketplace-user-supplies',
|
||||
component: UserSuppliesListPage,
|
||||
},
|
||||
],
|
||||
},
|
||||
//страница контактов
|
||||
{
|
||||
path: ':coopname/contacts',
|
||||
name: 'contacts',
|
||||
component: ContactsPage,
|
||||
meta: {
|
||||
is_desktop_menu: true,
|
||||
title: 'Контакты',
|
||||
icon: 'fa-solid fa-info',
|
||||
roles: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/something-bad',
|
||||
name: 'somethingBad',
|
||||
@@ -178,6 +30,36 @@ const baseRoutes = [
|
||||
name: 'permissionDenied',
|
||||
component: permissionDenied,
|
||||
},
|
||||
{
|
||||
path: ':coopname/install',
|
||||
name: 'install',
|
||||
component: InstallCooperativePage,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: ':coopname/auth/signin',
|
||||
name: 'signin',
|
||||
component: SignInPage,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: ':coopname/auth/lost-key',
|
||||
name: 'lostkey',
|
||||
component: LostKeyPage,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: ':coopname/auth/reset-key',
|
||||
name: 'resetkey',
|
||||
component: ResetKeyPage,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: ':coopname/auth/signup',
|
||||
name: 'signup',
|
||||
component: SignUpPage,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'NotFound',
|
||||
@@ -187,6 +69,109 @@ const baseRoutes = [
|
||||
},
|
||||
];
|
||||
|
||||
// const baseRoutes = [
|
||||
// {
|
||||
// path: '/',
|
||||
// component: layout,
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// name: 'index',
|
||||
// component: index,
|
||||
// },
|
||||
|
||||
// //страница кошелька пользователя
|
||||
// {
|
||||
// path: '/:coopname/home',
|
||||
// name: 'home',
|
||||
// component: UserHomePage,
|
||||
// children: [],
|
||||
// meta: {
|
||||
// is_desktop_menu: true,
|
||||
// title: 'Профиль',
|
||||
// icon: 'fa-solid fa-id-card',
|
||||
// roles: [],
|
||||
// },
|
||||
// },
|
||||
|
||||
// //страница управления кооперативом
|
||||
// {
|
||||
// path: ':coopname/settings',
|
||||
// name: 'soviet',
|
||||
// component: CoopSettingsPage,
|
||||
// meta: {
|
||||
// is_desktop_menu: true,
|
||||
// title: 'Настройки',
|
||||
// icon: 'fa-solid fa-cog',
|
||||
// roles: ['chairman', 'member'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: ':coopname/agenda',
|
||||
// name: 'agenda',
|
||||
// component: decisions,
|
||||
// meta: {
|
||||
// is_desktop_menu: true,
|
||||
// title: 'Повестка',
|
||||
// icon: 'fa-solid fa-check-to-slot',
|
||||
// roles: ['chairman', 'member'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: ':coopname/participants',
|
||||
// name: 'participants',
|
||||
// component: participants,
|
||||
// meta: {
|
||||
// is_desktop_menu: true,
|
||||
// title: 'Пайщики',
|
||||
// icon: 'fa-solid fa-users',
|
||||
// roles: ['chairman', 'member'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: ':coopname/documents',
|
||||
// name: 'documents',
|
||||
// component: documents,
|
||||
// meta: {
|
||||
// is_desktop_menu: true,
|
||||
// title: 'Документы',
|
||||
// icon: 'fa-solid fa-file-invoice',
|
||||
// roles: ['chairman', 'member'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: ':coopname/auth/signin',
|
||||
// name: 'signin',
|
||||
// component: SignInPage,
|
||||
// children: [],
|
||||
// },
|
||||
// {
|
||||
// path: ':coopname/auth/signup',
|
||||
// name: 'signup',
|
||||
// component: SignUpPage,
|
||||
// children: [],
|
||||
// },
|
||||
|
||||
|
||||
// {
|
||||
// path: '/something-bad',
|
||||
// name: 'somethingBad',
|
||||
// component: blank,
|
||||
// },
|
||||
// {
|
||||
// path: '/permission-denied',
|
||||
// name: 'permissionDenied',
|
||||
// component: permissionDenied,
|
||||
// },
|
||||
// {
|
||||
// path: '/:pathMatch(.*)*',
|
||||
// name: 'NotFound',
|
||||
// component: blank,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ];
|
||||
|
||||
const rs = baseRoutes;
|
||||
|
||||
export const routes = rs as RouteRecordRaw[];
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
<template lang="pug">
|
||||
div.menu-container
|
||||
div.menu-items
|
||||
div(v-for="route in menuRoutes" :key="route.path")
|
||||
q-btn(
|
||||
v-ripple
|
||||
flat
|
||||
class="cursor-pointer btn-menu"
|
||||
:class="headerClass(route)"
|
||||
@click="open(route)"
|
||||
)
|
||||
q-icon(:name="route.meta.icon").btn-icon.q-pt-xs
|
||||
span.btn-font {{ t(route.meta.title) }}
|
||||
q-btn(
|
||||
v-for="route in menuRoutes" :key="route.path"
|
||||
v-ripple
|
||||
flat
|
||||
class="cursor-pointer btn-menu"
|
||||
:class="headerClass(route)"
|
||||
@click="open(route)"
|
||||
)
|
||||
q-icon(:name="route.meta.icon").btn-icon.q-pt-xs
|
||||
span.btn-font {{ route.meta.title }}
|
||||
|
||||
div.control-buttons
|
||||
div
|
||||
q-btn(flat @click="$q.dark.toggle()").btn-menu
|
||||
q-icon(:name="isDark ? 'brightness_7' : 'brightness_3'").btn-icon
|
||||
span.btn-font {{ isDark ? 'светлая' : 'тёмная' }}
|
||||
div
|
||||
q-btn(v-ripple flat class="cursor-pointer btn-menu" @click="logout")
|
||||
q-icon( color="red" name="logout").btn-icon.q-pt-xs
|
||||
div.btn-font Выход
|
||||
|
||||
q-btn(flat @click="$q.dark.toggle()" ).btn-menu
|
||||
q-icon(:name="isDark ? 'brightness_7' : 'brightness_3'").q-pt-xs.btn-icon
|
||||
span.btn-font {{ isDark ? 'светлая' : 'тёмная' }}
|
||||
|
||||
q-btn(v-ripple flat class="cursor-pointer btn-menu" @click="logout")
|
||||
q-icon( color="red" name="logout").q-pt-xs.btn-icon
|
||||
div.btn-font Выход
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useMenuStore } from 'src/entities/Menu'
|
||||
import type { IMenu } from 'src/entities/Menu'
|
||||
import { COOPNAME } from 'src/shared/config'
|
||||
import { useLogoutUser } from 'src/features/User/Logout/model'
|
||||
import { useLogoutUser } from 'src/features/User/Logout'
|
||||
import { FailAlert } from 'src/shared/api'
|
||||
import { useCurrentUserStore } from 'src/entities/User'
|
||||
import { type IRoute } from 'src/entities/Desktop/model/types'
|
||||
import { useDesktopStore } from 'src/entities/Desktop/model'
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
@@ -41,27 +41,24 @@ const isDark = computed(() => $q.dark.isActive)
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const user = useCurrentUserStore()
|
||||
|
||||
const isRouteActive = (currentRoute: IMenu) => {
|
||||
return route.name === currentRoute.name
|
||||
const isRouteActive = (currentRoute: IRoute) => {
|
||||
return route.matched.find(r => r.path === currentRoute.path) || route.name == currentRoute.name
|
||||
}
|
||||
|
||||
const headerClass = (route: IMenu) => {
|
||||
const headerClass = (route: IRoute) => {
|
||||
const isActive = isRouteActive(route)
|
||||
return isActive ? (isDark.value ? 'text-white bg-teal-8' : 'text-black bg-teal-2') : ''
|
||||
return isActive ? (isDark.value ? 'text-white bg-teal' : 'text-white bg-teal') : ''
|
||||
}
|
||||
|
||||
const currentUser = useCurrentUserStore()
|
||||
const menuStore = useMenuStore()
|
||||
const desktop = useDesktopStore()
|
||||
|
||||
const { t } = useI18n()
|
||||
const open = (route: IRoute) => {
|
||||
if (route.children)
|
||||
router.push({ name: route.children[0].name, params: { coopname: COOPNAME } })
|
||||
else router.push({ name: route.name, params: { coopname: COOPNAME } })
|
||||
|
||||
const menu = ref<IMenu[]>([])
|
||||
|
||||
menu.value = menuStore.getUserDesktopMenu(currentUser?.userAccount?.role)
|
||||
|
||||
const open = (route: IMenu) => {
|
||||
router.push({ name: route.name, params: { coopname: COOPNAME } })
|
||||
}
|
||||
|
||||
|
||||
@@ -78,15 +75,23 @@ const logout = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const menuRoutes = computed(() => {
|
||||
return menu.value
|
||||
const userRole = user.userAccount?.role || 'user';
|
||||
|
||||
return desktop.firstLevel.filter(
|
||||
(route) => route.meta.roles.includes(userRole) || route.meta.roles.length === 0
|
||||
);
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.btn-menu {
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
height: 54px;
|
||||
width: 65px;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
@@ -99,13 +104,13 @@ const menuRoutes = computed(() => {
|
||||
|
||||
.logout-btn {
|
||||
position: fixed;
|
||||
bottom: 70px;
|
||||
bottom: 54px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,77 +1,76 @@
|
||||
<template lang="pug">
|
||||
div.row.justify-center
|
||||
div.col-12
|
||||
q-card(flat)
|
||||
q-table(
|
||||
ref="tableRef" v-model:expanded="expanded"
|
||||
flat
|
||||
bordered
|
||||
:rows="decisions"
|
||||
:columns="columns"
|
||||
:table-colspan="9"
|
||||
row-key="table.id"
|
||||
:pagination="pagination"
|
||||
virtual-scroll
|
||||
:virtual-scroll-item-size="48"
|
||||
:rows-per-page-options="[10]"
|
||||
:loading="onLoading"
|
||||
:no-data-label="'У совета нет голосований на повестке'"
|
||||
).full-height.full-width
|
||||
|
||||
template(#header="props")
|
||||
q-tr(:props="props")
|
||||
q-th(auto-width)
|
||||
q-card(flat)
|
||||
q-table(
|
||||
ref="tableRef" v-model:expanded="expanded"
|
||||
flat
|
||||
bordered
|
||||
:rows="decisions"
|
||||
:columns="columns"
|
||||
:table-colspan="9"
|
||||
row-key="table.id"
|
||||
:pagination="pagination"
|
||||
virtual-scroll
|
||||
:virtual-scroll-item-size="48"
|
||||
:rows-per-page-options="[10]"
|
||||
:loading="onLoading"
|
||||
:no-data-label="'У совета нет голосований на повестке'"
|
||||
).full-width
|
||||
|
||||
q-th(
|
||||
v-for="col in props.cols"
|
||||
:key="col.name"
|
||||
:props="props"
|
||||
) {{ col.label }}
|
||||
template(#header="props")
|
||||
q-tr(:props="props")
|
||||
q-th(auto-width)
|
||||
|
||||
template(#body="props")
|
||||
q-tr(:key="`m_${props.row.table.id}`" :props="props")
|
||||
q-td(auto-width)
|
||||
// q-toggle(v-model="props.expand" checked-icon="fas fa-chevron-circle-left" unchecked-icon="fas fa-chevron-circle-right" )
|
||||
q-th(
|
||||
v-for="col in props.cols"
|
||||
:key="col.name"
|
||||
:props="props"
|
||||
) {{ col.label }}
|
||||
|
||||
q-btn(size="sm" color="primary" dense :icon="props.expand ? 'remove' : 'add'" round @click="props.expand = !props.expand")
|
||||
q-td {{ props.row.table.id }}
|
||||
q-td {{ props.row.table.username }}
|
||||
q-td
|
||||
q-badge {{ getTitle(props.row.table.type, props.row.documents.statement.action.user) }}
|
||||
template(#body="props")
|
||||
q-tr(:key="`m_${props.row.table.id}`" :props="props")
|
||||
q-td(auto-width)
|
||||
// q-toggle(v-model="props.expand" checked-icon="fas fa-chevron-circle-left" unchecked-icon="fas fa-chevron-circle-right" )
|
||||
|
||||
//- q-td
|
||||
//- q-checkbox(@click="updateValidation(props.row.id)" :model-value="props.row.validated")
|
||||
q-btn(size="sm" color="primary" dense :icon="props.expand ? 'remove' : 'add'" round @click="props.expand = !props.expand")
|
||||
q-td {{ props.row.table.id }}
|
||||
q-td {{ props.row.table.username }}
|
||||
q-td
|
||||
q-badge {{ getTitle(props.row.table.type, props.row.documents.statement.action.user) }}
|
||||
|
||||
q-td
|
||||
//- p Проголосовало {{ props.row.table.votes_for.length + props.row.table.votes_against.length}} из {{totalMembers}}
|
||||
//- q-td
|
||||
//- q-checkbox(@click="updateValidation(props.row.id)" :model-value="props.row.validated")
|
||||
|
||||
q-td
|
||||
//- p Проголосовало {{ props.row.table.votes_for.length + props.row.table.votes_against.length}} из {{totalMembers}}
|
||||
|
||||
|
||||
q-btn(v-if="isVotedFor(props.row.table) || !isVotedAny(props.row.table)" :disabled="isVotedAny(props.row.table)" dense flat @click="voteAgainst(props.row.table.id)").text-red
|
||||
q-icon(name="fa-regular fa-thumbs-down")
|
||||
p.q-pl-xs {{props.row.table.votes_against.length}}
|
||||
q-btn(v-if="isVotedFor(props.row.table) || !isVotedAny(props.row.table)" :disabled="isVotedAny(props.row.table)" dense flat @click="voteAgainst(props.row.table.id)").text-red
|
||||
q-icon(name="fa-regular fa-thumbs-down")
|
||||
p.q-pl-xs {{props.row.table.votes_against.length}}
|
||||
|
||||
q-btn(v-if="isVotedAgainst(props.row.table)" disabled dense flat).text-red
|
||||
q-icon(name="fas fa-thumbs-down")
|
||||
p.q-pl-xs {{props.row.table.votes_against.length}}
|
||||
q-btn(v-if="isVotedAgainst(props.row.table)" disabled dense flat).text-red
|
||||
q-icon(name="fas fa-thumbs-down")
|
||||
p.q-pl-xs {{props.row.table.votes_against.length}}
|
||||
|
||||
q-checkbox( v-model="props.row.table.approved" disable :true-value="1" :false-value="0" )
|
||||
q-checkbox( v-model="props.row.table.approved" disable :true-value="1" :false-value="0" )
|
||||
|
||||
q-btn(v-if="isVotedAgainst(props.row.table) || !isVotedAny(props.row.table)" :disabled="isVotedAny(props.row.table)" dense flat @click="voteFor(props.row.table.id)").text-green
|
||||
p.q-pr-xs {{props.row.table.votes_for.length}}
|
||||
q-icon(name="fa-regular fa-thumbs-up" style="transform: scaleX(-1)")
|
||||
q-btn(v-if="isVotedAgainst(props.row.table) || !isVotedAny(props.row.table)" :disabled="isVotedAny(props.row.table)" dense flat @click="voteFor(props.row.table.id)").text-green
|
||||
p.q-pr-xs {{props.row.table.votes_for.length}}
|
||||
q-icon(name="fa-regular fa-thumbs-up" style="transform: scaleX(-1)")
|
||||
|
||||
q-btn(v-if="isVotedFor(props.row.table)" disabled dense flat ).text-green
|
||||
p.q-pr-xs {{props.row.table.votes_for.length}}
|
||||
q-icon(name="fas fa-thumbs-up" style="transform: scaleX(-1)")
|
||||
q-btn(v-if="isVotedFor(props.row.table)" disabled dense flat ).text-green
|
||||
p.q-pr-xs {{props.row.table.votes_for.length}}
|
||||
q-icon(name="fas fa-thumbs-up" style="transform: scaleX(-1)")
|
||||
|
||||
q-td
|
||||
q-btn( :loading="isProcess(props.row.table.id)" @click="updateAuthorized(props.row.table.username, props.row.table.id)") утвердить
|
||||
//- q-checkbox(v-if="!isProcess(props.row.table.id)" :model-value="props.row.table.authorized" :true-value="1" :false-value="0" @click="updateAuthorized(props.row.table.username, props.row.table.id)")
|
||||
//- q-spinner(v-if="isProcess(props.row.table.id)" size="md")
|
||||
q-td
|
||||
q-btn( :loading="isProcess(props.row.table.id)" @click="updateAuthorized(props.row.table.username, props.row.table.id)") утвердить
|
||||
//- q-checkbox(v-if="!isProcess(props.row.table.id)" :model-value="props.row.table.authorized" :true-value="1" :false-value="0" @click="updateAuthorized(props.row.table.username, props.row.table.id)")
|
||||
//- q-spinner(v-if="isProcess(props.row.table.id)" size="md")
|
||||
|
||||
q-tr(v-show="props.expand" :key="`e_${props.row.table.id}`" :props="props" class="q-virtual-scroll--with-prev")
|
||||
q-td(colspan="100%")
|
||||
joincoopdoc(v-if="props.row.table.type == 'joincoop'" :documents="props.row.documents")
|
||||
q-tr(v-show="props.expand" :key="`e_${props.row.table.id}`" :props="props" class="q-virtual-scroll--with-prev")
|
||||
q-td(colspan="100%")
|
||||
joincoopdoc(v-if="props.row.table.type == 'joincoop'" :documents="props.row.documents")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
//TODO валидация на члена совета
|
||||
|
||||
q-tabs( v-model="tab" dense switch-indicator inline-label outside-arrows mobile-arrows align="justify" active-bg-color="teal" active-color="white" indicator-color="secondary")
|
||||
// q-tab(name="Дашборд" icon="mail" label="Дашборд")
|
||||
//- q-tab(name="Повестка" label="Повестка")
|
||||
@@ -9,16 +7,16 @@ div
|
||||
//- q-tab(name="Пайщики" label="Пайщики")
|
||||
//- q-tab(name="Документы" label="Документы")
|
||||
|
||||
//- q-tab(name="Администраторы" label="Администраторы")
|
||||
q-tab(name="Администраторы" label="Администраторы")
|
||||
|
||||
//- q-tab(name="Члены совета" label="Члены совета")
|
||||
//- q-tab(name="Кооператив" label="Кооператив")
|
||||
q-tab(name="Члены совета" label="Члены совета")
|
||||
q-tab(name="Кооператив" label="Кооператив")
|
||||
|
||||
//- // q-tab(name="Участки" icon="people" label="Участки")
|
||||
// q-tab(name="Участки" icon="people" label="Участки")
|
||||
|
||||
//- // q-tab(name="Программы" label="Программы")
|
||||
//- // q-tab(name="Собрания" icon="people" label="Собрания")
|
||||
//- // q-tab(name="Фонды" label="Фонды")
|
||||
// q-tab(name="Программы" label="Программы")
|
||||
// q-tab(name="Собрания" icon="people" label="Собрания")
|
||||
// q-tab(name="Фонды" label="Фонды")
|
||||
|
||||
q-tab-panels(
|
||||
v-model="tab"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * as ChairmanDesktopModel from './model'
|
||||
@@ -0,0 +1,63 @@
|
||||
import { UserIdentityPage } from 'src/pages/User/IdentityPage';
|
||||
import { UserPaymentMethodsPage } from 'src/pages/User/PaymentMethodsPage';
|
||||
import { UserWalletPage } from 'src/pages/User/WalletPage';
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
export const manifest = {
|
||||
'name': 'ChairmanDesktop',
|
||||
'hash': 'hash2',
|
||||
'authorizedHome': 'home',
|
||||
'nonAuthorizedHome': 'signup',
|
||||
'routes': [
|
||||
{
|
||||
meta: {
|
||||
title: 'Пайщик',
|
||||
icon: 'fa-solid fa-id-card',
|
||||
roles: [],
|
||||
},
|
||||
path: '/:coopname/user',
|
||||
name: 'home',
|
||||
children: [{
|
||||
meta: {
|
||||
title: 'Удостоверение',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'identity',
|
||||
name: 'user-identity',
|
||||
component: markRaw(UserIdentityPage),
|
||||
children: [],
|
||||
},{
|
||||
meta: {
|
||||
title: 'Кошелёк',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'wallet',
|
||||
name: 'user-wallet',
|
||||
component: markRaw(UserWalletPage),
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: 'Реквизиты',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'payment-methods',
|
||||
name: 'user-payment-methods',
|
||||
component: markRaw(UserPaymentMethodsPage),
|
||||
children: [],
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
'config': {
|
||||
'layout': 'default',
|
||||
'theme': 'light'
|
||||
},
|
||||
'schemas': {
|
||||
'layout': 'avj schema here',
|
||||
'theme': 'avj schema here'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * as MemberDesktopModel from './model'
|
||||
@@ -0,0 +1,63 @@
|
||||
import { UserIdentityPage } from 'src/pages/User/IdentityPage';
|
||||
import { UserPaymentMethodsPage } from 'src/pages/User/PaymentMethodsPage';
|
||||
import { UserWalletPage } from 'src/pages/User/WalletPage';
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
export const manifest = {
|
||||
'name': 'ChairmanDesktop',
|
||||
'hash': 'hash2',
|
||||
'authorizedHome': 'home',
|
||||
'nonAuthorizedHome': 'signup',
|
||||
'routes': [
|
||||
{
|
||||
meta: {
|
||||
title: 'Пайщик',
|
||||
icon: 'fa-solid fa-id-card',
|
||||
roles: [],
|
||||
},
|
||||
path: '/:coopname/user',
|
||||
name: 'home',
|
||||
children: [{
|
||||
meta: {
|
||||
title: 'Удостоверение',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'identity',
|
||||
name: 'user-identity',
|
||||
component: markRaw(UserIdentityPage),
|
||||
children: [],
|
||||
},{
|
||||
meta: {
|
||||
title: 'Кошелёк',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'wallet',
|
||||
name: 'user-wallet',
|
||||
component: markRaw(UserWalletPage),
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: 'Реквизиты',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'payment-methods',
|
||||
name: 'user-payment-methods',
|
||||
component: markRaw(UserPaymentMethodsPage),
|
||||
children: [],
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
'config': {
|
||||
'layout': 'default',
|
||||
'theme': 'light'
|
||||
},
|
||||
'schemas': {
|
||||
'layout': 'avj schema here',
|
||||
'theme': 'avj schema here'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * as SetupDesktopModel from './model'
|
||||
@@ -0,0 +1,63 @@
|
||||
import { UserIdentityPage } from 'src/pages/User/IdentityPage';
|
||||
import { UserPaymentMethodsPage } from 'src/pages/User/PaymentMethodsPage';
|
||||
import { UserWalletPage } from 'src/pages/User/WalletPage';
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
export const manifest = {
|
||||
'name': 'ChairmanDesktop',
|
||||
'hash': 'hash2',
|
||||
'authorizedHome': 'home',
|
||||
'nonAuthorizedHome': 'signup',
|
||||
'routes': [
|
||||
{
|
||||
meta: {
|
||||
title: 'Пайщик',
|
||||
icon: 'fa-solid fa-id-card',
|
||||
roles: [],
|
||||
},
|
||||
path: '/:coopname/user',
|
||||
name: 'home',
|
||||
children: [{
|
||||
meta: {
|
||||
title: 'Удостоверение',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'identity',
|
||||
name: 'user-identity',
|
||||
component: markRaw(UserIdentityPage),
|
||||
children: [],
|
||||
},{
|
||||
meta: {
|
||||
title: 'Кошелёк',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'wallet',
|
||||
name: 'user-wallet',
|
||||
component: markRaw(UserWalletPage),
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: 'Реквизиты',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'payment-methods',
|
||||
name: 'user-payment-methods',
|
||||
component: markRaw(UserPaymentMethodsPage),
|
||||
children: [],
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
'config': {
|
||||
'layout': 'default',
|
||||
'theme': 'light'
|
||||
},
|
||||
'schemas': {
|
||||
'layout': 'avj schema here',
|
||||
'theme': 'avj schema here'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * as UserDesktopModel from './model'
|
||||
@@ -0,0 +1,288 @@
|
||||
import { UserIdentityPage } from 'src/pages/User/IdentityPage';
|
||||
import { UserPaymentMethodsPage } from 'src/pages/User/PaymentMethodsPage';
|
||||
import { UserWalletPage } from 'src/pages/User/WalletPage';
|
||||
import decisions from 'src/components/soviet/decisions.vue';
|
||||
import participants from 'src/components/soviet/participants.vue';
|
||||
import documents from 'src/components/soviet/documents.vue';
|
||||
import { markRaw } from 'vue';
|
||||
// import { Commutator } from 'src/widgets/Commutator';
|
||||
|
||||
// import { MainMarketplacePage } from 'src/pages/Marketplace/MainPage';
|
||||
// import { ModerationPage } from 'src/pages/Marketplace/Moderation';
|
||||
// import { CreateParentOfferPage } from 'src/pages/Marketplace/CreateParentOffer';
|
||||
// import { ShowcasePage } from 'src/pages/Marketplace/Showcase';
|
||||
// import { UserParentOffersPage } from 'src/pages/Marketplace/UserParentOffers';
|
||||
// import { SuppliesListPage } from 'src/pages/Marketplace/SuppliesList';
|
||||
// import { UserSuppliesListPage } from 'src/pages/Marketplace/UserSuppliesList';
|
||||
import { ContactsPage } from 'src/pages/Cooperative/Contacts';
|
||||
import { CooperativeDetails } from 'src/widgets/Cooperative/Details';
|
||||
import { CooperativeMembers } from 'src/widgets/Cooperative/Members';
|
||||
import { ChangeCooperativeContributions } from 'src/widgets/Cooperative/Contributions';
|
||||
import { ChangeCooperativeContacts } from 'src/widgets/Cooperative/Contacts';
|
||||
|
||||
export const manifest = {
|
||||
'name': 'UserDesktop',
|
||||
'hash': 'hash1',
|
||||
'authorizedHome': 'user-identity',
|
||||
'nonAuthorizedHome': 'signin',
|
||||
'routes': [
|
||||
{
|
||||
meta: {
|
||||
title: 'Пайщик',
|
||||
icon: 'fa-solid fa-id-card',
|
||||
roles: [],
|
||||
},
|
||||
path: '/:coopname/user',
|
||||
name: 'home',
|
||||
children: [{
|
||||
meta: {
|
||||
title: 'Удостоверение',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'identity',
|
||||
name: 'user-identity',
|
||||
component: markRaw(UserIdentityPage),
|
||||
children: [],
|
||||
},{
|
||||
meta: {
|
||||
title: 'Кошелёк',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'wallet',
|
||||
name: 'user-wallet',
|
||||
component: markRaw(UserWalletPage),
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: 'Реквизиты',
|
||||
icon: '',
|
||||
roles: [],
|
||||
},
|
||||
path: 'payment-methods',
|
||||
name: 'user-payment-methods',
|
||||
component: markRaw(UserPaymentMethodsPage),
|
||||
children: [],
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
meta: {
|
||||
title: 'Совет',
|
||||
icon: 'fa-regular fa-circle',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
path: '/:coopname/soviet',
|
||||
name: 'soviet',
|
||||
children: [
|
||||
{
|
||||
path: 'agenda',
|
||||
name: 'agenda',
|
||||
component: markRaw(decisions),
|
||||
meta: {
|
||||
title: 'Повестка',
|
||||
icon: 'fa-solid fa-check-to-slot',
|
||||
roles: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'participants',
|
||||
name: 'participants',
|
||||
component: markRaw(participants),
|
||||
meta: {
|
||||
title: 'Пайщики',
|
||||
icon: 'fa-solid fa-users',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'documents',
|
||||
name: 'documents',
|
||||
component: markRaw(documents),
|
||||
meta: {
|
||||
title: 'Документы',
|
||||
icon: 'fa-solid fa-file-invoice',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
//КООПЕРАТИВ
|
||||
{
|
||||
meta: {
|
||||
title: 'Кооператив',
|
||||
icon: 'fa-solid fa-cog',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
path: '/:coopname/settings',
|
||||
name: 'settings',
|
||||
children: [
|
||||
{
|
||||
path: 'details',
|
||||
name: 'settings-details',
|
||||
component: markRaw(CooperativeDetails),
|
||||
meta: {
|
||||
title: 'Реквизиты',
|
||||
icon: 'fa-solid fa-check-to-slot',
|
||||
roles: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
name: 'settings-members',
|
||||
component: markRaw(CooperativeMembers),
|
||||
meta: {
|
||||
title: 'Члены Совета',
|
||||
icon: 'fa-solid fa-users',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'contributions',
|
||||
name: 'settings-contributions',
|
||||
component: markRaw(ChangeCooperativeContributions),
|
||||
meta: {
|
||||
title: 'Взносы',
|
||||
icon: 'fa-solid fa-file-invoice',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
path: 'contacts',
|
||||
name: 'settings-contacts',
|
||||
component: markRaw(ChangeCooperativeContacts),
|
||||
meta: {
|
||||
title: 'Контакты',
|
||||
icon: 'fa-solid fa-file-invoice',
|
||||
roles: ['chairman', 'member'],
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
// {
|
||||
// meta: {
|
||||
// title: 'Маркетплейс',
|
||||
// icon: 'fa-solid fa-cog',
|
||||
// roles: [],
|
||||
// },
|
||||
// path: '/:coopname/marketplace',
|
||||
// name: 'marketplace',
|
||||
// component: markRaw(MainMarketplacePage),
|
||||
// children: [
|
||||
// {
|
||||
// path: 'moderation',
|
||||
// name: 'marketplace-moderation',
|
||||
// component: markRaw(ModerationPage),
|
||||
// meta: {
|
||||
// title: 'Модерация',
|
||||
// icon: '',
|
||||
// roles: ['member', 'chairman'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'create-offer',
|
||||
// name: 'marketplace-create-offer',
|
||||
// component: markRaw(CreateParentOfferPage),
|
||||
// meta: {
|
||||
// title: 'Создать объявление',
|
||||
// icon: '',
|
||||
// roles: [],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'showcase',
|
||||
// name: 'marketplace-showcase',
|
||||
// component: markRaw(ShowcasePage),
|
||||
// children: [
|
||||
// {
|
||||
// path: ':id',
|
||||
// name: 'marketplace-showcase-id',
|
||||
// component: markRaw(ShowcasePage),
|
||||
// },
|
||||
// ],
|
||||
// meta: {
|
||||
// title: 'Витрина',
|
||||
// icon: '',
|
||||
// roles: [],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'user-offers',
|
||||
// name: 'marketplace-user-offers',
|
||||
// component: markRaw(UserParentOffersPage),
|
||||
// children: [
|
||||
// {
|
||||
// path: ':id',
|
||||
// name: 'marketplace-user-offer-id',
|
||||
// component: markRaw(UserParentOffersPage),
|
||||
// },
|
||||
// ],
|
||||
// meta: {
|
||||
// title: 'Мои объявления',
|
||||
// icon: '',
|
||||
// roles: [],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'supplies',
|
||||
// name: 'marketplace-supplies',
|
||||
// component: markRaw(SuppliesListPage),
|
||||
// meta: {
|
||||
// title: 'Все заказы',
|
||||
// icon: '',
|
||||
// roles: ['member', 'chairman'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'user-supplies',
|
||||
// name: 'marketplace-user-supplies',
|
||||
// component: markRaw(UserSuppliesListPage),
|
||||
// meta: {
|
||||
// title: 'Мои заказы',
|
||||
// icon: '',
|
||||
// roles: [],
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
//страница контактов
|
||||
{
|
||||
path: ':coopname/contacts',
|
||||
name: 'contacts',
|
||||
component: markRaw(ContactsPage),
|
||||
meta: {
|
||||
title: 'Контакты',
|
||||
icon: 'fa-solid fa-info',
|
||||
roles: [],
|
||||
},
|
||||
},
|
||||
// {
|
||||
// path: ':coopname/commutator',
|
||||
// name: 'commutator',
|
||||
// component: markRaw(Commutator),
|
||||
// meta: {
|
||||
// title: 'Коммутатор',
|
||||
// icon: '',
|
||||
// roles: [],
|
||||
// },
|
||||
// },
|
||||
],
|
||||
'config': {
|
||||
'layout': 'default',
|
||||
'theme': 'light'
|
||||
},
|
||||
'schemas': {
|
||||
'layout': 'avj schema here',
|
||||
'theme': 'avj schema here'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export * from './Chairman'
|
||||
export * from './Member'
|
||||
export * from './User'
|
||||
export * from './Setup'
|
||||
|
||||
// export desktopsMap {
|
||||
// Chairman: ChairmanDesktop,
|
||||
|
||||
// }
|
||||
@@ -15,7 +15,6 @@ const namespace = 'cooperative';
|
||||
interface ICooperativeStore {
|
||||
// методы
|
||||
loadFunds: (coopname: string) => Promise<void>;
|
||||
|
||||
loadAddresses: (params: ILoadCooperativeAddresses) => Promise<void>;
|
||||
loadMarketPrograms: (params: ILoadCoopMarketPrograms) => Promise<void>;
|
||||
loadPrivateCooperativeData: () => Promise<void>;
|
||||
@@ -33,7 +32,6 @@ interface ICooperativeStore {
|
||||
fundWallet: Ref<FundContract.Tables.FundWallet.IFundWallet | undefined>
|
||||
accumulationFunds: Ref<FundContract.Tables.AccumulatedFunds.IAccumulatedFund[]>
|
||||
expenseFunds: Ref<FundContract.Tables.ExpensedFunds.IExpensedFund[]>
|
||||
|
||||
}
|
||||
|
||||
export const useCooperativeStore = defineStore(
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { IHealthResponse } from 'coopback';
|
||||
import { sendGET } from 'src/shared/api';
|
||||
|
||||
async function healthCheck(): Promise<IHealthResponse> {
|
||||
return (await sendGET('/v1/mono/health', {}, true)) as IHealthResponse;
|
||||
}
|
||||
|
||||
export const api ={
|
||||
healthCheck
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './ui'
|
||||
export * as DesktopModel from './model'
|
||||
export * from './api'
|
||||
@@ -0,0 +1 @@
|
||||
export * from './store'
|
||||
@@ -0,0 +1,130 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import * as Desktops from 'src/desktops'
|
||||
import { computed, ComputedRef, ref, Ref } from 'vue'
|
||||
import { FailAlert } from 'src/shared/api'
|
||||
import { RouteRecordRaw, type RouteLocationNormalized} from 'vue-router'
|
||||
import { IDesktop, IRoute, IBlockchainDesktops, IHealthResponse } from './types'
|
||||
import { api } from '../api'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
|
||||
const desktopHashMap = {
|
||||
'hash1': Desktops.UserDesktopModel.manifest, //User
|
||||
'hash2': Desktops.ChairmanDesktopModel.manifest, //'Chairman',
|
||||
'hash3': Desktops.MemberDesktopModel.manifest, //'Member',
|
||||
'hash4': Desktops.SetupDesktopModel.manifest, //Setup
|
||||
}
|
||||
|
||||
const namespace = 'desktops';
|
||||
|
||||
interface IDesktopStore {
|
||||
online: Ref<boolean | undefined>
|
||||
health: Ref<IHealthResponse | undefined>
|
||||
currentDesktop: Ref<IDesktop | undefined>
|
||||
availableDesktops: Ref<IDesktop[]>
|
||||
defaultDesktopHash: Ref<string | undefined>
|
||||
healthCheck: () => Promise<void>;
|
||||
setActiveDesktop: (hash: string | undefined) => void;
|
||||
loadDesktops: () => void;
|
||||
getSecondLevel: (currentRoute: RouteLocationNormalized) => RouteRecordRaw[]
|
||||
firstLevel: ComputedRef<IRoute[]>;
|
||||
}
|
||||
|
||||
|
||||
function getNestedRoutes(route: RouteRecordRaw):RouteRecordRaw[] {
|
||||
if (!route.children) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let nestedRoutes: RouteRecordRaw[] = [];
|
||||
|
||||
for (const child of route.children) {
|
||||
nestedRoutes.push(child);
|
||||
nestedRoutes = nestedRoutes.concat(getNestedRoutes(child));
|
||||
}
|
||||
|
||||
return nestedRoutes;
|
||||
}
|
||||
|
||||
export const useDesktopStore = defineStore(namespace, (): IDesktopStore => {
|
||||
const currentDesktop = ref<IDesktop>()
|
||||
const availableDesktops = ref<IDesktop[]>([])
|
||||
const defaultDesktopHash = ref<string>()
|
||||
const health = ref<IHealthResponse | undefined>()
|
||||
const online = ref<boolean | undefined>()
|
||||
|
||||
|
||||
|
||||
async function healthCheck(): Promise<void> {
|
||||
try {
|
||||
|
||||
health.value = await api.healthCheck()
|
||||
|
||||
if (online.value === false) online.value = true
|
||||
|
||||
} catch (e) {
|
||||
online.value = false
|
||||
setTimeout(healthCheck, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
const firstLevel = computed(() => {
|
||||
if (currentDesktop.value)
|
||||
return currentDesktop.value.routes
|
||||
else return []
|
||||
});
|
||||
|
||||
|
||||
const getSecondLevel = (currentRoute: RouteLocationNormalized): RouteRecordRaw[] => {
|
||||
if (currentRoute.matched && currentDesktop.value && currentRoute) {
|
||||
|
||||
const matchingRootRoute = currentRoute.matched[1]
|
||||
|
||||
if (matchingRootRoute) {
|
||||
return getNestedRoutes(matchingRootRoute)
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
const setActiveDesktop = (hash: string | undefined) => {
|
||||
const desktop = availableDesktops.value.find(d => d.hash === hash)
|
||||
if (desktop)
|
||||
currentDesktop.value = desktop
|
||||
else {
|
||||
FailAlert('Рабочий стол не найден')
|
||||
}
|
||||
}
|
||||
|
||||
const loadAvailableDesktops = async(): Promise<IBlockchainDesktops> => {
|
||||
return { //load it from bc later
|
||||
defaultHash: 'hash1',
|
||||
availableHashes: ['hash1','hash2', 'hash3']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const loadDesktops = async () => {
|
||||
const {defaultHash, availableHashes} = await loadAvailableDesktops()
|
||||
availableDesktops.value = []
|
||||
defaultDesktopHash.value = defaultHash
|
||||
|
||||
availableHashes.map(hash => availableDesktops.value.push(
|
||||
desktopHashMap[hash as keyof typeof desktopHashMap]
|
||||
))
|
||||
}
|
||||
|
||||
return {
|
||||
online,
|
||||
health,
|
||||
defaultDesktopHash, //перезапишем через локал-сторадж для пользовательского управления стартовой страницей
|
||||
currentDesktop,
|
||||
availableDesktops,
|
||||
setActiveDesktop,
|
||||
loadDesktops,
|
||||
getSecondLevel,
|
||||
firstLevel,
|
||||
healthCheck
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
export interface IDesktop {
|
||||
name: string;
|
||||
hash: string;
|
||||
authorizedHome: string;
|
||||
nonAuthorizedHome: string;
|
||||
routes: IRoute[];
|
||||
config: object;
|
||||
}
|
||||
|
||||
export interface IBlockchainDesktops {
|
||||
defaultHash: string; //hash
|
||||
availableHashes: string[]; //hashes
|
||||
}
|
||||
|
||||
interface RouteMeta {
|
||||
title: string;
|
||||
icon: string;
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
export interface IRoute {
|
||||
path: string;
|
||||
name: string;
|
||||
component?: any;
|
||||
children?: IRoute[];
|
||||
meta: RouteMeta;
|
||||
}
|
||||
|
||||
export type { IHealthResponse } from 'coopback'
|
||||
@@ -0,0 +1,5 @@
|
||||
<template lang="pug">
|
||||
div menu
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
@@ -0,0 +1 @@
|
||||
export {default as FirstLevelMenu} from './FirstLevelMenu.vue'
|
||||
@@ -0,0 +1,52 @@
|
||||
<template lang="pug">
|
||||
|
||||
q-tabs(
|
||||
v-if="routes"
|
||||
dense
|
||||
switch-indicator
|
||||
inline-label
|
||||
outside-arrows
|
||||
mobile-arrows
|
||||
align="justify"
|
||||
active-class="bg-teal"
|
||||
active-bg-color="teal"
|
||||
active-color="white"
|
||||
indicator-color="secondary"
|
||||
)
|
||||
|
||||
q-route-tab(
|
||||
v-for="route in routes"
|
||||
:key="route.name"
|
||||
:name="route.meta.title"
|
||||
:label="route.meta.title"
|
||||
:to="{ name: route.name }"
|
||||
:params="{coopname: 'voskhod'}"
|
||||
)
|
||||
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
||||
import { useCurrentUserStore } from 'src/entities/User';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useDesktopStore } from '../../model';
|
||||
import { type IRoute } from '../../model/types';
|
||||
|
||||
const desktop = useDesktopStore()
|
||||
const routes = ref<IRoute[]>([])
|
||||
const route = useRoute()
|
||||
const user = useCurrentUserStore()
|
||||
|
||||
const init = () => {
|
||||
const userRole = user.userAccount?.role || 'user';
|
||||
|
||||
routes.value = (desktop.getSecondLevel(route) as unknown as IRoute[]).filter(
|
||||
(route) => route.meta?.roles?.includes(userRole) || route.meta?.roles?.length === 0
|
||||
);
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
watch(route, () => init())
|
||||
</script>
|
||||
@@ -0,0 +1 @@
|
||||
export {default as SecondLevelMenu} from './SecondLevelMenu.vue'
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './FirstLevelMenu'
|
||||
export * from './SecondLevelMenu'
|
||||
@@ -0,0 +1 @@
|
||||
export * as InstallerModel from './model'
|
||||
@@ -0,0 +1 @@
|
||||
export * from './store'
|
||||
@@ -0,0 +1,24 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
interface IInstallCooperative {
|
||||
data: {
|
||||
test1: string
|
||||
}
|
||||
}
|
||||
|
||||
const namespace = 'install';
|
||||
|
||||
export const useInstallCooperativeStore = defineStore(
|
||||
namespace,
|
||||
(): IInstallCooperative => {
|
||||
|
||||
const data = reactive({
|
||||
test1: 'test'
|
||||
})
|
||||
|
||||
return {
|
||||
data
|
||||
}
|
||||
})
|
||||
|
||||
@@ -77,7 +77,7 @@ export const useWalletStore = defineStore(namespace, (): IWalletStore => {
|
||||
const createDeposit = async (
|
||||
params: ICreateDeposit
|
||||
): Promise<IPaymentOrder> => {
|
||||
return sendPOST('/v1/orders/deposit', params);
|
||||
return sendPOST('/v1/payments/deposit', params);
|
||||
};
|
||||
|
||||
const createWithdraw = async (params: ICreateWithdraw): Promise<void> => {
|
||||
|
||||
@@ -4,13 +4,13 @@ const walletStore = useWalletStore()
|
||||
</script>
|
||||
|
||||
<template lang="pug">
|
||||
div.row.flex.justify-around.q-pa-sm
|
||||
div.q-mt-md
|
||||
span.text-grey Паевый счёт
|
||||
p.text-h3 {{ walletStore.wallet.available }}
|
||||
div
|
||||
q-input(readonly standout label="Паевый счёт" v-model="walletStore.wallet.available")
|
||||
//- span.text-grey Паевый счёт
|
||||
//- p.text-h3 {{ walletStore.wallet.available }}
|
||||
|
||||
|
||||
q-input(readonly standout label="Членский счёт" v-model="walletStore.wallet.blocked")
|
||||
|
||||
div.q-mt-md.text-right
|
||||
span.text-grey Членский счёт
|
||||
p.text-h3 {{ walletStore.wallet.blocked }}
|
||||
|
||||
</template>
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ const walletStore = useWalletStore()
|
||||
|
||||
<template lang="pug">
|
||||
div.q-pa-md
|
||||
q-list(v-if="walletStore.program_wallets.length > 0")
|
||||
q-list
|
||||
q-item(v-for="program_wallet of walletStore.program_wallets" :key="program_wallet.id" v-ripple clickable)
|
||||
q-item-section
|
||||
q-item-label {{ program_wallet.program_details.title }}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user