add a lot of queries and mutations
This commit is contained in:
Vendored
+1
-1
@@ -4,7 +4,7 @@
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabCompletion": "onlySnippets",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||
// "editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||
"eslint.validate": ["javascript", "typescript", "vue"],
|
||||
// "typescript.tsdk": "node_modules/typescript/lib",
|
||||
"i18n-ally.localesPaths": ["src/i18n"],
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
"glob": "^11.0.0",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-request": "^7.1.2",
|
||||
"graphql-scalars": "^1.24.0",
|
||||
"graphql-tools": "^9.0.2",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
"graphql-ws": "^5.16.0",
|
||||
|
||||
+1135
-15
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
export const tokenTypes = {
|
||||
ACCESS: 'access',
|
||||
REFRESH: 'refresh',
|
||||
RESET_PASSWORD: 'resetPassword',
|
||||
RESET_KEY: 'resetPassword',
|
||||
VERIFY_EMAIL: 'verifyEmail',
|
||||
INVITE: 'invite',
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import pick from '../utils/pick';
|
||||
// import { IGetResponse } from '../types/common';
|
||||
import { IUser, userStatus } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import { generateUsername } from '../../tests/utils/generateUsername';
|
||||
import { generateUsername } from '../utils/generate-username';
|
||||
import config from '../config/config';
|
||||
import logger from '../config/logger';
|
||||
// import { Body, Controller, Get, Path, Post, Query, Route, SuccessResponse } from 'tsoa';
|
||||
@@ -87,6 +87,22 @@ export const addUser = catchAsync(async (req: Request, res: Response) => {
|
||||
res.status(httpStatus.CREATED).send({ user });
|
||||
});
|
||||
|
||||
export const updateUser = catchAsync(async (req, res) => {
|
||||
if (req.user.role !== 'member' && req.user.role !== 'chairman')
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Только председатель или член совета может обновить данные пользователя');
|
||||
|
||||
const user = await userService.updateUserByUsername(req.params.username, req.body);
|
||||
res.send(user);
|
||||
});
|
||||
|
||||
export const deleteUser = catchAsync(async (req, res) => {
|
||||
if (req.user.role !== 'member' && req.user.role !== 'chairman')
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Только председатель или член совета может обновить данные пользователя');
|
||||
|
||||
await userService.deleteUserByUsername(req.params.username);
|
||||
res.status(http.NO_CONTENT).send();
|
||||
});
|
||||
|
||||
export const getUsers = catchAsync(async (req, res) => {
|
||||
const filter = pick(req.query, ['username', 'role']);
|
||||
const options = pick(req.query, ['sortBy', 'limit', 'page']);
|
||||
@@ -100,16 +116,3 @@ export const getUser = catchAsync(async (req, res) => {
|
||||
|
||||
res.send(user);
|
||||
});
|
||||
|
||||
export const updateUser = catchAsync(async (req, res) => {
|
||||
if (req.user.role !== 'member' && req.user.role !== 'chairman')
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Только председатель или член совета может обновить данные пользователя');
|
||||
|
||||
const user = await userService.updateUserByUsername(req.params.username, req.body);
|
||||
res.send(user);
|
||||
});
|
||||
|
||||
export const deleteUser = catchAsync(async (req, res) => {
|
||||
await userService.deleteUserByUsername(req.params.username);
|
||||
res.status(http.NO_CONTENT).send();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ export class AccountDomainEntity {
|
||||
public readonly username!: string;
|
||||
public blockchain_account!: BlockchainAccountInterface | null;
|
||||
public user_account!: RegistratorContract.Tables.Accounts.IAccount | null;
|
||||
public mono_account!: MonoAccountDomainInterface | null;
|
||||
public provider_account!: MonoAccountDomainInterface | null;
|
||||
public participant_account!: SovietContract.Tables.Participants.IParticipants | null;
|
||||
// public cardcoop_account!: ?
|
||||
|
||||
|
||||
@@ -1,28 +1,116 @@
|
||||
import { AccountDomainEntity } from '../entities/account-domain.entity';
|
||||
import config from '~/config/config';
|
||||
import { AccountDomainService } from '~/domain/account/services/account-domain.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { userService } from '~/services';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { tokenService, userService } from '~/services';
|
||||
import type { MonoAccountDomainInterface } from '../interfaces/mono-account-domain.interface';
|
||||
import type { GetAccountsInputDomainInterface } from '../interfaces/get-accounts-input.interface';
|
||||
import type {
|
||||
PaginationInputDomainInterface,
|
||||
PaginationResultDomainInterface,
|
||||
} from '~/domain/common/interfaces/pagination.interface';
|
||||
import type { QueryResultLegacy } from '~/domain/common/interfaces/query-result-legacy-domain.interface';
|
||||
import type { RegisterAccountDomainInterface } from '../interfaces/register-account-input.interface';
|
||||
import type { RegisteredAccountDomainInterface } from '../interfaces/registeted-account.interface';
|
||||
import type { UpdateAccountDomainInterface } from '../interfaces/update-account-input.interface';
|
||||
import { AccountType } from '~/modules/account/enum/account-type.enum';
|
||||
import { ORGANIZATION_REPOSITORY, OrganizationRepository } from '~/domain/common/repositories/organization.repository';
|
||||
import { INDIVIDUAL_REPOSITORY, IndividualRepository } from '~/domain/common/repositories/individual.repository';
|
||||
import { ENTREPRENEUR_REPOSITORY, EntrepreneurRepository } from '~/domain/common/repositories/entrepreneur.repository';
|
||||
import { IndividualDomainEntity } from '~/domain/branch/entities/individual-domain.entity';
|
||||
import { OrganizationDomainEntity } from '~/domain/branch/entities/organization-domain.entity';
|
||||
import { EntrepreneurDomainEntity } from '~/domain/branch/entities/entrepreneur-domain.entity';
|
||||
|
||||
@Injectable()
|
||||
export class AccountDomainInteractor {
|
||||
constructor(private readonly accountDomainService: AccountDomainService) {}
|
||||
constructor(
|
||||
private readonly accountDomainService: AccountDomainService,
|
||||
@Inject(ORGANIZATION_REPOSITORY) private readonly organizationRepository: OrganizationRepository,
|
||||
@Inject(INDIVIDUAL_REPOSITORY) private readonly individualRepository: IndividualRepository,
|
||||
@Inject(ENTREPRENEUR_REPOSITORY) private readonly entrepreneurRepository: EntrepreneurRepository
|
||||
) {}
|
||||
|
||||
async updateAccount(data: UpdateAccountDomainInterface): Promise<AccountDomainEntity> {
|
||||
await userService.updateUserByUsername(data.username, {
|
||||
email: data.email,
|
||||
});
|
||||
|
||||
// обновляем данные в хранилище
|
||||
if (data.type === AccountType.Individual && data.individual_data) {
|
||||
//здесь и далее мы подставляем email и username т.к. они требуются в интерфесе генератора
|
||||
const individual = new IndividualDomainEntity({ ...data.individual_data, username: data.username, email: data.email });
|
||||
await this.individualRepository.create(individual);
|
||||
} else if (data.type === AccountType.Organization && data.organization_data) {
|
||||
const organization = new OrganizationDomainEntity({
|
||||
...data.organization_data,
|
||||
username: data.username,
|
||||
email: data.email,
|
||||
});
|
||||
await this.organizationRepository.create(organization);
|
||||
} else if (data.type === AccountType.Entrepreneur && data.entrepreneur_data) {
|
||||
const entrepreneur = new EntrepreneurDomainEntity({
|
||||
...data.entrepreneur_data,
|
||||
username: data.username,
|
||||
email: data.email,
|
||||
});
|
||||
await this.entrepreneurRepository.create(entrepreneur);
|
||||
}
|
||||
|
||||
return await this.getAccount(data.username);
|
||||
}
|
||||
|
||||
async deleteAccount(username: string): Promise<void> {
|
||||
await userService.deleteUserByUsername(username);
|
||||
}
|
||||
|
||||
async registerAccount(data: RegisterAccountDomainInterface): Promise<RegisteredAccountDomainInterface> {
|
||||
//TODO refactor after migrate from mongo
|
||||
const user = await userService.createUser(data);
|
||||
const tokens = await tokenService.generateAuthTokens(user);
|
||||
|
||||
const account = await this.getAccount(data.username);
|
||||
|
||||
const result: RegisteredAccountDomainInterface = {
|
||||
account,
|
||||
tokens,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async getAccount(username: string): Promise<AccountDomainEntity> {
|
||||
const user_account = await this.accountDomainService.getUserAccount(username);
|
||||
const blockchain_account = await this.accountDomainService.getBlockchainAccount(username);
|
||||
const participant_account = await this.accountDomainService.getParticipantAccount(config.coopname, username);
|
||||
return await this.accountDomainService.getAccount(username);
|
||||
}
|
||||
|
||||
//TODO refactor after migrate from mongo
|
||||
const mono_account = (await userService.findUser(username)) as unknown as MonoAccountDomainInterface;
|
||||
async getAccounts(
|
||||
data: GetAccountsInputDomainInterface,
|
||||
options: PaginationInputDomainInterface
|
||||
): Promise<PaginationResultDomainInterface<AccountDomainEntity>> {
|
||||
const provider_accounts = (await userService.queryUsers(data, options)) as QueryResultLegacy<MonoAccountDomainInterface>;
|
||||
|
||||
return new AccountDomainEntity({
|
||||
username,
|
||||
user_account,
|
||||
blockchain_account,
|
||||
mono_account: mono_account,
|
||||
participant_account,
|
||||
});
|
||||
const result: PaginationResultDomainInterface<AccountDomainEntity> = {
|
||||
items: [],
|
||||
totalCount: provider_accounts.totalResults,
|
||||
totalPages: provider_accounts.totalPages,
|
||||
currentPage: provider_accounts.page,
|
||||
};
|
||||
|
||||
for (const account of provider_accounts.results) {
|
||||
const user_account = await this.accountDomainService.getUserAccount(account.username);
|
||||
const blockchain_account = await this.accountDomainService.getBlockchainAccount(account.username);
|
||||
const participant_account = await this.accountDomainService.getParticipantAccount(config.coopname, account.username);
|
||||
|
||||
const item = new AccountDomainEntity({
|
||||
username: account.username,
|
||||
user_account,
|
||||
blockchain_account,
|
||||
provider_account: account,
|
||||
participant_account,
|
||||
});
|
||||
|
||||
result.items.push(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface AccountBlockchainPort {
|
||||
username: string
|
||||
): Promise<SovietContract.Tables.Participants.IParticipants | null>;
|
||||
getUserAccount(username: string): Promise<RegistratorContract.Tables.Accounts.IAccount | null>;
|
||||
addParticipantAccount(data: RegistratorContract.Actions.AddUser.IAddUser): Promise<void>;
|
||||
}
|
||||
|
||||
export const ACCOUNT_BLOCKCHAIN_PORT = Symbol('AccountBlockchainPort');
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetAccountsInputDomainInterface {
|
||||
username?: string;
|
||||
role?: string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
|
||||
export interface RegisterAccountDomainInterface {
|
||||
email: string;
|
||||
entrepreneur_data?: Omit<Cooperative.Users.IEntrepreneurData, 'username'> & {
|
||||
bank_account: Cooperative.Payments.IBankAccount;
|
||||
};
|
||||
individual_data?: Omit<Cooperative.Users.IIndividualData, 'username'>;
|
||||
organization_data?: Omit<Cooperative.Users.IOrganizationData, 'username'> & {
|
||||
bank_account: Cooperative.Payments.IBankAccount;
|
||||
};
|
||||
public_key?: string;
|
||||
referer?: string;
|
||||
role: 'user';
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
username: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { AccountDomainEntity } from '../entities/account-domain.entity';
|
||||
import type { TokensDomainInterface } from './tokens-domain.interface';
|
||||
|
||||
export interface RegisteredAccountDomainInterface {
|
||||
account: AccountDomainEntity;
|
||||
tokens: TokensDomainInterface;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TokenDomainInterface {
|
||||
token: string;
|
||||
expires: Date;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { TokenDomainInterface } from './token-domain.interface';
|
||||
|
||||
export interface TokensDomainInterface {
|
||||
access: TokenDomainInterface;
|
||||
refresh: TokenDomainInterface;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
|
||||
export interface UpdateAccountDomainInterface {
|
||||
email: string;
|
||||
entrepreneur_data?: Omit<Cooperative.Users.IEntrepreneurData, 'username'>;
|
||||
individual_data?: Omit<Cooperative.Users.IIndividualData, 'username'>;
|
||||
organization_data?: Omit<Cooperative.Users.IOrganizationData, 'username'>;
|
||||
public_key?: string;
|
||||
referer?: string;
|
||||
role: 'user';
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
username: string;
|
||||
}
|
||||
@@ -1,12 +1,56 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||
import type { BlockchainAccountInterface } from '~/types/shared';
|
||||
import { ACCOUNT_BLOCKCHAIN_PORT, type AccountBlockchainPort } from '../interfaces/account-blockchain.port';
|
||||
import type { RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import config from '~/config/config';
|
||||
import { AccountDomainEntity } from '../entities/account-domain.entity';
|
||||
import type { MonoAccountDomainInterface } from '../interfaces/mono-account-domain.interface';
|
||||
import { blockchainService, userService } from '~/services';
|
||||
import type { RegisterAccountDomainInterface } from '../interfaces/register-account-input.interface';
|
||||
import { userStatus } from '~/types';
|
||||
|
||||
@Injectable()
|
||||
export class AccountDomainService {
|
||||
constructor(@Inject(ACCOUNT_BLOCKCHAIN_PORT) private readonly accountBlockchainPort: AccountBlockchainPort) {}
|
||||
|
||||
async addProviderAccount(data: RegisterAccountDomainInterface): Promise<MonoAccountDomainInterface> {
|
||||
//TODO refactor it after migrate from mongo
|
||||
const user = await userService.createUser(data);
|
||||
user.status = userStatus['4_Registered'];
|
||||
user.is_registered = true;
|
||||
user.has_account = true;
|
||||
|
||||
await user.save();
|
||||
return user as unknown as MonoAccountDomainInterface;
|
||||
}
|
||||
|
||||
async addParticipantAccount(data: RegistratorContract.Actions.AddUser.IAddUser): Promise<void> {
|
||||
try {
|
||||
await this.accountBlockchainPort.addParticipantAccount(data);
|
||||
} catch (e: any) {
|
||||
// удаляем аккаунт провайдера если транзакция в блокчейн не прошла (для возможности повтора)
|
||||
await userService.deleteUserByUsername(data.username);
|
||||
throw new BadRequestException(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async getAccount(username: string): Promise<AccountDomainEntity> {
|
||||
const user_account = await this.getUserAccount(username);
|
||||
const blockchain_account = await this.getBlockchainAccount(username);
|
||||
const participant_account = await this.getParticipantAccount(config.coopname, username);
|
||||
|
||||
//TODO refactor after migrate from mongo
|
||||
const provider_account = (await userService.findUser(username)) as unknown as MonoAccountDomainInterface;
|
||||
|
||||
return new AccountDomainEntity({
|
||||
username,
|
||||
user_account,
|
||||
blockchain_account,
|
||||
provider_account: provider_account,
|
||||
participant_account,
|
||||
});
|
||||
}
|
||||
|
||||
async getBlockchainAccount(username: string): Promise<BlockchainAccountInterface | null> {
|
||||
return await this.accountBlockchainPort.getBlockchainAccount(username);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DocumentDomainModule } from '../document/document.module';
|
||||
import { AgendaDomainInteractor } from './interactors/agenda-domain.interactor';
|
||||
|
||||
@Module({
|
||||
imports: [DocumentDomainModule],
|
||||
providers: [AgendaDomainInteractor],
|
||||
exports: [AgendaDomainInteractor],
|
||||
})
|
||||
export class AgendaDomainModule {}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { DocumentDomainService } from '~/domain/document/services/document-domain.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { blockchainService } from '~/services';
|
||||
import config from '~/config/config';
|
||||
import { SovietContract } from 'cooptypes';
|
||||
import type { AgendaWithDocumentsDomainInterface } from '../interfaces/agenda-with-documents-domain.interface';
|
||||
import { getActions } from '~/utils/getFetch';
|
||||
import type { VotingAgendaDomainInterface } from '../interfaces/voting-agenda-domain.interface';
|
||||
|
||||
@Injectable()
|
||||
export class AgendaDomainInteractor {
|
||||
constructor(private readonly documentDomainService: DocumentDomainService) {}
|
||||
|
||||
async getAgenda(): Promise<AgendaWithDocumentsDomainInterface[]> {
|
||||
// Шаг 1: Загрузить повестку дня
|
||||
const agenda = await this.loadAgenda(config.coopname);
|
||||
|
||||
// Шаг 2: Построить комплексную повестку (complexAgenda)
|
||||
const complexAgenda: AgendaWithDocumentsDomainInterface[] = [];
|
||||
|
||||
for (const { action, table } of agenda) {
|
||||
// Создание пакета документов для каждого действия
|
||||
const documents = await this.documentDomainService.buildDocumentPackage(action);
|
||||
|
||||
// Проверяем наличие заявления, прежде чем добавлять в комплексную повестку
|
||||
// Делаем только потому что в локальной разработке заявлений после перезапуска блокчейна может и не быть в истории цепочки,
|
||||
// а это ломает отображение на фронте. При нормальные условиях заявление всегда должно быть.
|
||||
if (documents.statement?.document) {
|
||||
complexAgenda.push({
|
||||
table,
|
||||
action,
|
||||
documents,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Шаг 3: Вернуть итоговую комплексную повестку
|
||||
return complexAgenda;
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружает повестку дня (agenda) на основании решений и связанных действий.
|
||||
*/
|
||||
async loadAgenda(coopname: string): Promise<VotingAgendaDomainInterface[]> {
|
||||
//TODO блокчейн-адаптер здесь повесить
|
||||
const api = await blockchainService.getApi();
|
||||
|
||||
// Загружаем таблицу решений
|
||||
const decisions = (await blockchainService.lazyFetch(
|
||||
api,
|
||||
SovietContract.contractName.production as string,
|
||||
coopname,
|
||||
'decisions'
|
||||
)) as SovietContract.Tables.Decisions.IDecision[];
|
||||
|
||||
const agenda: VotingAgendaDomainInterface[] = [];
|
||||
|
||||
for (const decision of decisions) {
|
||||
// Ищем экшен, связанный с конкретным решением
|
||||
const actionResponse = await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
|
||||
filter: JSON.stringify({
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Registry.NewSubmitted.actionName,
|
||||
receiver: process.env.COOPNAME,
|
||||
'data.decision_id': String(decision.id),
|
||||
}),
|
||||
page: 1,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
const action = actionResponse?.results?.[0];
|
||||
if (action) {
|
||||
agenda.push({
|
||||
table: decision,
|
||||
action,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return agenda;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import type { ExtendedBlockchainActionDomainInterface } from './extended-blockchain-action-domain.interface';
|
||||
|
||||
export interface ActDetailDomainInterface {
|
||||
action?: ExtendedBlockchainActionDomainInterface;
|
||||
document?: GeneratedDocumentDomainInterface;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import type { DocumentPackageDomainInterface } from './document-package-domain.interface';
|
||||
import type { VotingAgendaDomainInterface } from './voting-agenda-domain.interface';
|
||||
|
||||
export interface AgendaWithDocumentsDomainInterface extends VotingAgendaDomainInterface {
|
||||
documents: DocumentPackageDomainInterface;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
|
||||
export type BlockchainActionDomainInterface = Cooperative.Blockchain.IAction;
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import type { ExtendedBlockchainActionDomainInterface } from './extended-blockchain-action-domain.interface';
|
||||
|
||||
export interface DecisionDetailDomainInterface {
|
||||
action: ExtendedBlockchainActionDomainInterface;
|
||||
document: GeneratedDocumentDomainInterface;
|
||||
votes_for: ExtendedBlockchainActionDomainInterface[];
|
||||
votes_against: ExtendedBlockchainActionDomainInterface[];
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import type { ActDetailDomainInterface } from './act-detail-domain.interface';
|
||||
import type { DecisionDetailDomainInterface } from './decision-detail-domain.interface';
|
||||
import type { StatementDetailDomainInterface } from './statement-detail-domain.interface';
|
||||
|
||||
export interface DocumentPackageDomainInterface {
|
||||
statement: StatementDetailDomainInterface;
|
||||
decision: DecisionDetailDomainInterface;
|
||||
acts: ActDetailDomainInterface[];
|
||||
links: GeneratedDocumentDomainInterface[];
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import type { DocumentPackageDomainInterface } from './document-package-domain.interface';
|
||||
|
||||
export interface DocumentPackagesResponseDomainInterface {
|
||||
results: DocumentPackageDomainInterface[];
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { BlockchainActionDomainInterface } from './blockchain-action-domain.interface';
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
|
||||
export interface ExtendedBlockchainActionDomainInterface extends BlockchainActionDomainInterface {
|
||||
user?:
|
||||
| Cooperative.Users.IIndividualData
|
||||
| Cooperative.Users.IEntrepreneurData
|
||||
| Cooperative.Users.IOrganizationData
|
||||
| null;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import type { ExtendedBlockchainActionDomainInterface } from './extended-blockchain-action-domain.interface';
|
||||
|
||||
export interface StatementDetailDomainInterface {
|
||||
action: ExtendedBlockchainActionDomainInterface;
|
||||
document: GeneratedDocumentDomainInterface;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { SovietContract } from 'cooptypes';
|
||||
import type { BlockchainActionDomainInterface } from './blockchain-action-domain.interface';
|
||||
|
||||
export interface VotingAgendaDomainInterface {
|
||||
table: SovietContract.Tables.Decisions.IDecision;
|
||||
action: BlockchainActionDomainInterface;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DocumentDomainModule } from '../document/document.module';
|
||||
import { AgreementDomainInteractor } from './interactors/agreement-domain.interactor';
|
||||
|
||||
@Module({
|
||||
imports: [DocumentDomainModule],
|
||||
providers: [AgreementDomainInteractor],
|
||||
exports: [AgreementDomainInteractor],
|
||||
})
|
||||
export class AgreementDomainModule {}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { DocumentDomainService } from '~/domain/document/services/document-domain.service';
|
||||
import { DocumentDomainEntity } from '~/domain/document/entity/document-domain.entity';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AgreementDomainInteractor {
|
||||
constructor(private readonly documentDomainService: DocumentDomainService) {}
|
||||
|
||||
async generateWalletAgreement(
|
||||
data: Cooperative.Registry.WalletAgreement.Action,
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.WalletAgreement.registry_id;
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
|
||||
async generatePrivacyAgreement(
|
||||
data: Cooperative.Registry.PrivacyPolicy.Action,
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.PrivacyPolicy.registry_id;
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
|
||||
async generateSignatureAgreement(
|
||||
data: Cooperative.Registry.RegulationElectronicSignature.Action,
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.RegulationElectronicSignature.registry_id;
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
|
||||
async generateUserAgreement(
|
||||
data: Cooperative.Registry.UserAgreement.Action,
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.UserAgreement.registry_id;
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// domain/appstore/appstore-domain.module.ts
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthDomainInteractor } from './interactors/auth.interactor';
|
||||
import { AccountDomainModule } from '../account/account-domain.module';
|
||||
|
||||
@Module({
|
||||
imports: [AccountDomainModule],
|
||||
providers: [AuthDomainInteractor],
|
||||
exports: [AuthDomainInteractor],
|
||||
})
|
||||
export class AuthDomainModule {}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import type { RegisteredAccountDomainInterface } from '~/domain/account/interfaces/registeted-account.interface';
|
||||
import { AccountDomainService } from '~/domain/account/services/account-domain.service';
|
||||
import { authService, blockchainService, emailService, tokenService, userService } from '~/services';
|
||||
import type { LoginInputDomainInterface } from '../interfaces/login-input-domain.interface';
|
||||
import type { StartResetKeyInputDomainInterface } from '../interfaces/start-reset-key-input.interface';
|
||||
import type { ResetKeyInputDomainInterface } from '../interfaces/reset-key-input.interface';
|
||||
import type { RefreshInputDomainInterface } from '../interfaces/refresh-input.interface';
|
||||
import type { LogoutInputDomainInterface } from '../interfaces/logout-input-domain.interface';
|
||||
import { tokenTypes } from '~/config/tokens';
|
||||
import { Token } from '~/models';
|
||||
import config from '~/config/config';
|
||||
|
||||
@Injectable()
|
||||
export class AuthDomainInteractor {
|
||||
constructor(private readonly accountDomainService: AccountDomainService) {}
|
||||
|
||||
async login(data: LoginInputDomainInterface): Promise<RegisteredAccountDomainInterface> {
|
||||
const user = await authService.loginUserWithSignature(data.email, data.now, data.signature);
|
||||
|
||||
const tokens = await tokenService.generateAuthTokens(user);
|
||||
const account = await this.accountDomainService.getAccount(user.username);
|
||||
|
||||
return {
|
||||
account,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
|
||||
async logout(data: LogoutInputDomainInterface): Promise<void> {
|
||||
const refreshTokenDoc = await Token.findOne({ token: data.refresh_token, type: tokenTypes.REFRESH, blacklisted: false });
|
||||
if (refreshTokenDoc) {
|
||||
await refreshTokenDoc.deleteOne();
|
||||
}
|
||||
|
||||
const accessTokenDoc = await Token.findOne({ token: data.access_token, type: tokenTypes.REFRESH, blacklisted: false });
|
||||
if (accessTokenDoc) {
|
||||
await accessTokenDoc.deleteOne();
|
||||
}
|
||||
}
|
||||
|
||||
async startResetKey(data: StartResetKeyInputDomainInterface): Promise<void> {
|
||||
const resetKeyToken = await tokenService.generateResetKeyToken(data.email);
|
||||
await emailService.sendResetKeyEmail(data.email, resetKeyToken);
|
||||
}
|
||||
|
||||
async resetKey(data: ResetKeyInputDomainInterface): Promise<void> {
|
||||
try {
|
||||
const resetKeyTokenDoc = await tokenService.verifyToken(data.token, [tokenTypes.RESET_KEY, tokenTypes.INVITE]);
|
||||
|
||||
const user = await userService.getUserById(resetKeyTokenDoc.user);
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
await blockchainService.changeKey({
|
||||
coopname: config.coopname,
|
||||
changer: config.coopname,
|
||||
username: user.username,
|
||||
public_key: data.public_key,
|
||||
});
|
||||
|
||||
await userService.updateUserById(user._id, { public_key: data.public_key });
|
||||
|
||||
await Token.deleteMany({ user: user._id, type: tokenTypes.RESET_KEY });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new UnauthorizedException('Возникла ошибка при сбросе ключа');
|
||||
}
|
||||
}
|
||||
|
||||
async refresh(data: RefreshInputDomainInterface): Promise<RegisteredAccountDomainInterface> {
|
||||
try {
|
||||
const refreshTokenDoc = await tokenService.verifyToken(data.refresh_token, tokenTypes.REFRESH);
|
||||
const user = await userService.getUserById(refreshTokenDoc.user);
|
||||
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
await refreshTokenDoc.deleteOne();
|
||||
const tokens = await tokenService.generateAuthTokens(user);
|
||||
|
||||
const account = await this.accountDomainService.getAccount(user.username);
|
||||
|
||||
return {
|
||||
account,
|
||||
tokens,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new UnauthorizedException('Возникла неизвестная ошибка при обновлении');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface LoginInputDomainInterface {
|
||||
email: string;
|
||||
now: string;
|
||||
signature: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface LogoutInputDomainInterface {
|
||||
refresh_token: string;
|
||||
access_token: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface RefreshInputDomainInterface {
|
||||
refresh_token: string;
|
||||
access_token: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ResetKeyInputDomainInterface {
|
||||
public_key: string;
|
||||
token: string;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface StartResetKeyInputDomainInterface {
|
||||
email: string;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// domain/appstore/appstore-domain.module.ts
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BranchDomainInteractor } from './interactors/branch.interactor';
|
||||
import { DocumentDomainModule } from '../document/document.module';
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
export interface QueryResultLegacy<T> {
|
||||
results: T[];
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
totalResults: number;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// domain/appstore/appstore-domain.module.ts
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DecisionDomainInteractor } from './interactors/decision.interactor';
|
||||
import { DocumentDomainModule } from '../document/document.module';
|
||||
|
||||
@Module({
|
||||
imports: [DocumentDomainModule],
|
||||
providers: [DecisionDomainInteractor],
|
||||
exports: [DecisionDomainInteractor],
|
||||
})
|
||||
export class DecisionDomainModule {}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DocumentInteractor } from './interactors/document.interactor';
|
||||
import { DocumentDomainInteractor } from './interactors/document.interactor';
|
||||
import { InfrastructureModule } from '~/infrastructure/infrastructure.module';
|
||||
import { DocumentDomainService } from './services/document-domain.service';
|
||||
|
||||
@Module({
|
||||
imports: [InfrastructureModule],
|
||||
providers: [DocumentInteractor, DocumentDomainService],
|
||||
exports: [DocumentInteractor, DocumentDomainService],
|
||||
providers: [DocumentDomainInteractor, DocumentDomainService],
|
||||
exports: [DocumentDomainInteractor, DocumentDomainService],
|
||||
})
|
||||
export class DocumentDomainModule {}
|
||||
|
||||
@@ -1,17 +1,53 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DOCUMENT_REPOSITORY, DocumentRepository } from '../repository/document.repository';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { GenerateDocumentDomainInterface } from '../interfaces/generate-document-domain.interface';
|
||||
import { DocumentDomainService } from '../services/document-domain.service';
|
||||
import type { DocumentDomainEntity } from '../entity/document-domain.entity';
|
||||
import type { PaginationResultDomainInterface } from '~/domain/common/interfaces/pagination.interface';
|
||||
import type { DocumentPackageDomainInterface } from '~/domain/agenda/interfaces/document-package-domain.interface';
|
||||
import type { GetDocumentsInputDomainInterface } from '../interfaces/get-documents-input-domain.interface';
|
||||
import { toDotNotation } from '~/utils/toDotNotation';
|
||||
import { getActions } from '~/utils/getFetch';
|
||||
import { SovietContract } from 'cooptypes';
|
||||
|
||||
@Injectable()
|
||||
export class DocumentInteractor {
|
||||
constructor(
|
||||
@Inject(DOCUMENT_REPOSITORY) private readonly documentRepository: DocumentRepository,
|
||||
private readonly documentDomainService: DocumentDomainService
|
||||
) {}
|
||||
export class DocumentDomainInteractor {
|
||||
constructor(private readonly documentDomainService: DocumentDomainService) {}
|
||||
|
||||
public async generateDocument(data: GenerateDocumentDomainInterface): Promise<DocumentDomainEntity> {
|
||||
return await this.documentDomainService.generateDocument(data);
|
||||
}
|
||||
|
||||
public async getDocuments(
|
||||
data: GetDocumentsInputDomainInterface
|
||||
): Promise<PaginationResultDomainInterface<DocumentPackageDomainInterface>> {
|
||||
const { type = 'newsubmitted', page = 1, limit = 100, query } = data;
|
||||
|
||||
const actions = await getActions<SovietContract.Actions.Registry.NewResolved.INewResolved>(
|
||||
`${process.env.SIMPLE_EXPLORER_API}/get-actions`,
|
||||
{
|
||||
filter: JSON.stringify({
|
||||
account: SovietContract.contractName.production,
|
||||
name: type,
|
||||
...toDotNotation(query),
|
||||
}),
|
||||
page,
|
||||
limit,
|
||||
}
|
||||
);
|
||||
|
||||
const response: PaginationResultDomainInterface<DocumentPackageDomainInterface> = {
|
||||
items: [],
|
||||
totalCount: actions.results.length,
|
||||
totalPages: 0,
|
||||
currentPage: page,
|
||||
};
|
||||
|
||||
for (const raw_action_document of actions.results) {
|
||||
const documentPackage = await this.documentDomainService.buildDocumentPackage(raw_action_document);
|
||||
|
||||
if (documentPackage.statement.action) response.items.push(documentPackage);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export interface GetDocumentsInputDomainInterface {
|
||||
type?: 'newsubmitted' | 'newresolved';
|
||||
query: Record<string, unknown>;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
@@ -3,6 +3,15 @@ import { DOCUMENT_REPOSITORY, DocumentRepository } from '../repository/document.
|
||||
import { GeneratorInfrastructureService } from '~/infrastructure/generator/generator.service';
|
||||
import type { GenerateDocumentDomainInterface } from '../interfaces/generate-document-domain.interface';
|
||||
import { DocumentDomainEntity } from '../entity/document-domain.entity';
|
||||
import { Cooperative, SovietContract } from 'cooptypes';
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import { User } from '~/models';
|
||||
import { getActions } from '~/utils/getFetch';
|
||||
import type { DocumentPackageDomainInterface } from '~/domain/agenda/interfaces/document-package-domain.interface';
|
||||
import type { ActDetailDomainInterface } from '~/domain/agenda/interfaces/act-detail-domain.interface';
|
||||
import type { DecisionDetailDomainInterface } from '~/domain/agenda/interfaces/decision-detail-domain.interface';
|
||||
import type { ExtendedBlockchainActionDomainInterface } from '~/domain/agenda/interfaces/extended-blockchain-action-domain.interface';
|
||||
import type { StatementDetailDomainInterface } from '~/domain/agenda/interfaces/statement-detail-domain.interface';
|
||||
|
||||
@Injectable()
|
||||
export class DocumentDomainService {
|
||||
@@ -20,4 +29,130 @@ export class DocumentDomainService {
|
||||
if (!document) throw new BadRequestException('Документ не найден');
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает rawAction (блокчейн-действие), выстраивает DocumentPackage,
|
||||
* включающее заявление (statement), решение (decision), акты (acts) и связанные документы (links).
|
||||
*/
|
||||
async buildDocumentPackage(rawAction: Cooperative.Blockchain.IAction): Promise<DocumentPackageDomainInterface> {
|
||||
// Инициализация частей будущего DocumentPackage
|
||||
let statementDetail: StatementDetailDomainInterface | null = null;
|
||||
let decisionDetail: DecisionDetailDomainInterface | null = null;
|
||||
const actDetails: ActDetailDomainInterface[] = [];
|
||||
const links: GeneratedDocumentDomainInterface[] = [];
|
||||
|
||||
// Извлекаем данные (newSubmitted) из rawAction
|
||||
const rawData = rawAction.data as SovietContract.Actions.Registry.NewSubmitted.INewSubmitted;
|
||||
|
||||
// -----------------------------------------------------
|
||||
// ШАГ 1: Подготовка ЗАЯВЛЕНИЯ (Statement)
|
||||
// -----------------------------------------------------
|
||||
{
|
||||
// Основной документ (statement)
|
||||
const mainDocument = await this.getDocumentByHash(rawData.document.hash);
|
||||
|
||||
// Если у документа есть ссылки, подгружаем их
|
||||
if (mainDocument?.meta?.links && Array.isArray(mainDocument.meta.links)) {
|
||||
for (const linkHash of mainDocument.meta.links) {
|
||||
const linkedDoc = await this.getDocumentByHash(linkHash);
|
||||
if (linkedDoc) links.push(linkedDoc);
|
||||
}
|
||||
}
|
||||
|
||||
// Ищем пользователя, оформившего заявление
|
||||
const user = await User.findOne({ username: rawData.username });
|
||||
if (user) {
|
||||
const userData = await user.getPrivateData();
|
||||
|
||||
// Формируем расширенный экшен
|
||||
const extendedAction: Cooperative.Blockchain.IExtendedAction = {
|
||||
...rawAction,
|
||||
user: userData,
|
||||
};
|
||||
|
||||
statementDetail = {
|
||||
action: extendedAction,
|
||||
document: mainDocument,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
// ШАГ 2: Подготовка РЕШЕНИЯ (Decision)
|
||||
// -----------------------------------------------------
|
||||
{
|
||||
// Ищем экшен, соответствующий созданию решения (NewDecision)
|
||||
const decisionActionResponse = await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
|
||||
filter: JSON.stringify({
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
receiver: process.env.COOPNAME,
|
||||
'data.decision_id': String(rawData.decision_id),
|
||||
}),
|
||||
page: 1,
|
||||
limit: 1,
|
||||
});
|
||||
const decisionAction = decisionActionResponse?.results?.[0];
|
||||
|
||||
if (decisionAction) {
|
||||
const user = await User.findOne({ username: decisionAction.data?.username });
|
||||
|
||||
if (user) {
|
||||
const userData = await user.getPrivateData();
|
||||
const extendedDecisionAction: Cooperative.Blockchain.IExtendedAction = {
|
||||
...decisionAction,
|
||||
user: userData ?? null,
|
||||
};
|
||||
|
||||
// Документ, прикреплённый к решению
|
||||
const decisionDocument = await this.getDocumentByHash(decisionAction?.data?.document?.hash);
|
||||
|
||||
if (decisionDocument?.meta?.links && Array.isArray(decisionDocument.meta.links)) {
|
||||
for (const linkHash of decisionDocument.meta.links) {
|
||||
const linkedDoc = await this.getDocumentByHash(linkHash);
|
||||
if (linkedDoc) links.push(linkedDoc);
|
||||
}
|
||||
}
|
||||
|
||||
decisionDetail = {
|
||||
action: extendedDecisionAction,
|
||||
document: decisionDocument,
|
||||
votes_for: [],
|
||||
votes_against: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
// ШАГ 3: Подготовка АКТОВ (Acts)
|
||||
// -----------------------------------------------------
|
||||
// Здесь в примере просто пустой массив,
|
||||
// но если нужно обрабатывать акты, добавьте логику получения
|
||||
// и пушите объекты вида `ActDetailDomainInterface`.
|
||||
// actDetails.push({ action: ..., document: ... });
|
||||
|
||||
// -----------------------------------------------------
|
||||
// ШАГ 4: Возвращаем итоговый объект DocumentPackage
|
||||
// -----------------------------------------------------
|
||||
// Если statementDetail или decisionDetail вдруг оказались null —
|
||||
// можно обработать это или бросить ошибку, в зависимости от бизнес-логики.
|
||||
// Здесь для примера присваиваем пустые объекты:
|
||||
const statement = statementDetail || ({ action: {}, document: {} } as StatementDetailDomainInterface);
|
||||
const decision: DecisionDetailDomainInterface =
|
||||
decisionDetail ||
|
||||
({
|
||||
action: {} as unknown as ExtendedBlockchainActionDomainInterface,
|
||||
document: {} as unknown as GeneratedDocumentDomainInterface,
|
||||
votes_for: [],
|
||||
votes_against: [],
|
||||
} as unknown as DecisionDetailDomainInterface);
|
||||
|
||||
return {
|
||||
statement,
|
||||
decision,
|
||||
acts: actDetails,
|
||||
links,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,18 @@ import { ProviderDomainModule } from './provider/provider.module';
|
||||
import { SystemDomainModule } from './system/system-domain.module';
|
||||
import { BranchDomainModule } from './branch/branch-domain.module';
|
||||
import { DocumentDomainModule } from './document/document.module';
|
||||
import { DecisionDomainModule } from './decision/decision.module';
|
||||
import { FreeDecisionDomainModule } from './free-decision/free-decision.module';
|
||||
import { AgreementDomainModule } from './agreement/agreement-domain.module';
|
||||
import { ParticipantDomainModule } from './participant/participant-domain.module';
|
||||
import { AuthDomainModule } from './auth/auth.module';
|
||||
import { AgendaDomainModule } from './agenda/agenda-domain.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AuthDomainModule,
|
||||
AgendaDomainModule,
|
||||
AccountDomainModule,
|
||||
AgreementDomainModule,
|
||||
ExtensionDomainModule,
|
||||
PaymentDomainModule,
|
||||
PaymentMethodDomainModule,
|
||||
@@ -20,10 +27,14 @@ import { DecisionDomainModule } from './decision/decision.module';
|
||||
SystemDomainModule,
|
||||
BranchDomainModule,
|
||||
DocumentDomainModule,
|
||||
DecisionDomainModule,
|
||||
FreeDecisionDomainModule,
|
||||
ParticipantDomainModule,
|
||||
],
|
||||
exports: [
|
||||
AuthDomainModule,
|
||||
AgendaDomainModule,
|
||||
AccountDomainModule,
|
||||
AgreementDomainModule,
|
||||
ExtensionDomainModule,
|
||||
PaymentDomainModule,
|
||||
PaymentMethodDomainModule,
|
||||
@@ -31,7 +42,8 @@ import { DecisionDomainModule } from './decision/decision.module';
|
||||
SystemDomainModule,
|
||||
BranchDomainModule,
|
||||
DocumentDomainModule,
|
||||
DecisionDomainModule,
|
||||
FreeDecisionDomainModule,
|
||||
ParticipantDomainModule,
|
||||
],
|
||||
})
|
||||
export class DomainModule {}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// domain/appstore/appstore-domain.module.ts
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { FreeDecisionDomainInteractor } from './interactors/free-decision.interactor';
|
||||
import { DocumentDomainModule } from '../document/document.module';
|
||||
|
||||
@Module({
|
||||
imports: [DocumentDomainModule],
|
||||
providers: [FreeDecisionDomainInteractor],
|
||||
exports: [FreeDecisionDomainInteractor],
|
||||
})
|
||||
export class FreeDecisionDomainModule {}
|
||||
+12
-4
@@ -4,7 +4,7 @@ import { DocumentDomainEntity } from '~/domain/document/entity/document-domain.e
|
||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||
import type { PublishProjectFreeDecisionInputDomainInterface } from '../interfaces/publish-project-free-decision.interface';
|
||||
import config from '~/config/config';
|
||||
import { DECISION_BLOCKCHAIN_PORT, DecisionBlockchainPort } from '../interfaces/decision-blockchain.port';
|
||||
import { FREE_DECISION_BLOCKCHAIN_PORT, FreeDecisionBlockchainPort } from '../interfaces/free-decision-blockchain.port';
|
||||
import {
|
||||
PROJECT_FREE_DECISION_REPOSITORY,
|
||||
ProjectFreeDecisionRepository,
|
||||
@@ -12,11 +12,11 @@ import {
|
||||
import { ProjectFreeDecisionDomainEntity } from '~/domain/branch/entities/project-free-decision.entity';
|
||||
|
||||
@Injectable()
|
||||
export class DecisionDomainInteractor {
|
||||
export class FreeDecisionDomainInteractor {
|
||||
constructor(
|
||||
private readonly documentDomainService: DocumentDomainService,
|
||||
@Inject(PROJECT_FREE_DECISION_REPOSITORY) private readonly projectDecisionRepository: ProjectFreeDecisionRepository,
|
||||
@Inject(DECISION_BLOCKCHAIN_PORT) private readonly decisionBlockchainPort: DecisionBlockchainPort
|
||||
@Inject(FREE_DECISION_BLOCKCHAIN_PORT) private readonly FreeDecisionBlockchainPort: FreeDecisionBlockchainPort
|
||||
) {}
|
||||
|
||||
async createProjectOfFreeDecision(data: Cooperative.Document.IProjectData): Promise<ProjectFreeDecisionDomainEntity> {
|
||||
@@ -32,6 +32,14 @@ export class DecisionDomainInteractor {
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
|
||||
async generateFreeDecisionDocument(
|
||||
data: Cooperative.Registry.FreeDecision.Action,
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.FreeDecision.registry_id;
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
|
||||
async publishProjectOfFreeDecision(data: PublishProjectFreeDecisionInputDomainInterface): Promise<boolean> {
|
||||
const document = await this.documentDomainService.getDocumentByHash(data.document.hash);
|
||||
|
||||
@@ -43,7 +51,7 @@ export class DecisionDomainInteractor {
|
||||
if (data.coopname != config.coopname)
|
||||
throw new BadRequestException('Указанное имя аккаунта кооператива не обслуживается здесь');
|
||||
|
||||
await this.decisionBlockchainPort.publichProjectOfFreeDecision({
|
||||
await this.FreeDecisionBlockchainPort.publichProjectOfFreeDecision({
|
||||
coopname: data.coopname,
|
||||
username: data.username,
|
||||
meta: data.meta,
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { SovietContract } from 'cooptypes';
|
||||
import type { TransactResult } from '@wharfkit/session';
|
||||
|
||||
export interface DecisionBlockchainPort {
|
||||
export interface FreeDecisionBlockchainPort {
|
||||
getProjects(coopname: string): Promise<SovietContract.Tables.Decisions.IDecision[]>;
|
||||
getProject(coopname: string, decision_id: string): Promise<SovietContract.Tables.Decisions.IDecision | null>;
|
||||
|
||||
@@ -10,4 +10,4 @@ export interface DecisionBlockchainPort {
|
||||
): Promise<TransactResult>;
|
||||
}
|
||||
|
||||
export const DECISION_BLOCKCHAIN_PORT = Symbol('DecisionBlockchainPort');
|
||||
export const FREE_DECISION_BLOCKCHAIN_PORT = Symbol('FreeDecisionBlockchainPort');
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { DocumentDomainService } from '~/domain/document/services/document-domain.service';
|
||||
import { DocumentDomainEntity } from '~/domain/document/entity/document-domain.entity';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { AddParticipantDomainInterface } from '../interfaces/add-participant-domain.interface';
|
||||
import type { RegisterAccountDomainInterface } from '~/domain/account/interfaces/register-account-input.interface';
|
||||
import { AccountDomainService } from '~/domain/account/services/account-domain.service';
|
||||
import type { AccountDomainEntity } from '~/domain/account/entities/account-domain.entity';
|
||||
import config from '~/config/config';
|
||||
import { emailService, participantService, tokenService } from '~/services';
|
||||
import type { RegisterParticipantDomainInterface } from '../interfaces/register-participant-domain.interface';
|
||||
|
||||
@Injectable()
|
||||
export class ParticipantDomainInteractor {
|
||||
constructor(
|
||||
private readonly documentDomainService: DocumentDomainService,
|
||||
private readonly accountDomainService: AccountDomainService
|
||||
) {}
|
||||
|
||||
async generateParticipantApplication(
|
||||
data: Cooperative.Registry.ParticipantApplication.Action,
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.ParticipantApplication.registry_id;
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
|
||||
async generateParticipantApplicationDecision(
|
||||
data: Cooperative.Registry.DecisionOfParticipantApplication.Action,
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.DecisionOfParticipantApplication.registry_id;
|
||||
return await this.documentDomainService.generateDocument({ data, options });
|
||||
}
|
||||
|
||||
async registerParticipant(data: RegisterParticipantDomainInterface): Promise<AccountDomainEntity> {
|
||||
await participantService.joinCooperative(data);
|
||||
return await this.accountDomainService.getAccount(data.username);
|
||||
}
|
||||
|
||||
async addParticipant(data: AddParticipantDomainInterface): Promise<AccountDomainEntity> {
|
||||
const newAccount: RegisterAccountDomainInterface = {
|
||||
...data,
|
||||
public_key: '',
|
||||
username: data.username,
|
||||
};
|
||||
|
||||
await this.accountDomainService.addProviderAccount(newAccount);
|
||||
|
||||
await this.accountDomainService.addParticipantAccount({
|
||||
registrator: config.coopname,
|
||||
referer: data.referer ? data.referer : '',
|
||||
coopname: config.coopname,
|
||||
meta: '',
|
||||
username: data.username,
|
||||
type: data.type,
|
||||
created_at: data.created_at,
|
||||
initial: data.initial,
|
||||
minimum: data.minimum,
|
||||
spread_initial: data.spread_initial,
|
||||
});
|
||||
|
||||
//TODO move it to hexagon services
|
||||
const token = await tokenService.generateInviteToken(data.email);
|
||||
await emailService.sendInviteEmail(data.email, token);
|
||||
|
||||
return this.accountDomainService.getAccount(data.username);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
import type { RegisterRole } from '~/modules/account/enum/account-role-on-register.enum';
|
||||
import type { AccountType } from '~/modules/account/enum/account-type.enum';
|
||||
|
||||
export interface AddParticipantDomainInterface {
|
||||
email: string;
|
||||
entrepreneur_data?: Omit<Cooperative.Users.IEntrepreneurData, 'username'> & {
|
||||
bank_account: Cooperative.Payments.IBankAccount;
|
||||
};
|
||||
individual_data?: Omit<Cooperative.Users.IIndividualData, 'username'>;
|
||||
organization_data?: Omit<Cooperative.Users.IOrganizationData, 'username'> & {
|
||||
bank_account: Cooperative.Payments.IBankAccount;
|
||||
};
|
||||
public_key?: string;
|
||||
referer?: string;
|
||||
role: RegisterRole;
|
||||
type: AccountType;
|
||||
username: string;
|
||||
created_at: string;
|
||||
initial: string;
|
||||
minimum: string;
|
||||
spread_initial: boolean;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
|
||||
export interface RegisterParticipantDomainInterface {
|
||||
username: string;
|
||||
privacy_agreement: Cooperative.Document.ISignedDocument<Cooperative.Registry.PrivacyPolicy.Action>;
|
||||
signature_agreement: Cooperative.Document.ISignedDocument<Cooperative.Registry.RegulationElectronicSignature.Action>;
|
||||
statement: Cooperative.Document.ISignedDocument<Cooperative.Registry.ParticipantApplication.Action>;
|
||||
user_agreement: Cooperative.Document.ISignedDocument<Cooperative.Registry.UserAgreement.Action>;
|
||||
wallet_agreement: Cooperative.Document.ISignedDocument<Cooperative.Registry.WalletAgreement.Action>;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DocumentDomainModule } from '../document/document.module';
|
||||
import { ParticipantDomainInteractor } from './interactors/participant-domain.interactor';
|
||||
import { AccountDomainModule } from '../account/account-domain.module';
|
||||
|
||||
@Module({
|
||||
imports: [AccountDomainModule, DocumentDomainModule],
|
||||
providers: [ParticipantDomainInteractor],
|
||||
exports: [ParticipantDomainInteractor],
|
||||
})
|
||||
export class ParticipantDomainModule {}
|
||||
@@ -1,13 +1,28 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BadGatewayException, Injectable } from '@nestjs/common';
|
||||
import { BlockchainService } from '../blockchain.service';
|
||||
import { RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import type { BlockchainAccountInterface } from '~/types/shared';
|
||||
import type { AccountBlockchainPort } from '~/domain/account/interfaces/account-blockchain.port';
|
||||
import Vault from '~/models/vault.model';
|
||||
|
||||
@Injectable()
|
||||
export class AccountBlockchainAdapter implements AccountBlockchainPort {
|
||||
constructor(private readonly blockchainService: BlockchainService) {}
|
||||
|
||||
async addParticipantAccount(data: RegistratorContract.Actions.AddUser.IAddUser): Promise<void> {
|
||||
const wif = await Vault.getWif(data.coopname);
|
||||
|
||||
if (!wif) throw new BadGatewayException('Не найден приватный ключ для совершения операции');
|
||||
|
||||
await this.blockchainService.initialize(data.coopname, wif);
|
||||
|
||||
await this.blockchainService.transact({
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.AddUser.actionName,
|
||||
authorization: [{ actor: data.coopname, permission: 'active' }],
|
||||
data,
|
||||
});
|
||||
}
|
||||
getBlockchainAccount(username: string): Promise<BlockchainAccountInterface | null> {
|
||||
return this.blockchainService.getAccount(username);
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,10 +5,10 @@ import { TransactResult } from '@wharfkit/session';
|
||||
import Vault from '~/models/vault.model';
|
||||
import httpStatus from 'http-status';
|
||||
import { HttpApiError } from '~/errors/http-api-error';
|
||||
import type { DecisionBlockchainPort } from '~/domain/decision/interfaces/decision-blockchain.port';
|
||||
import type { FreeDecisionBlockchainPort } from '~/domain/free-decision/interfaces/free-decision-blockchain.port';
|
||||
|
||||
@Injectable()
|
||||
export class DecisionBlockchainAdapter implements DecisionBlockchainPort {
|
||||
export class DecisionBlockchainAdapter implements FreeDecisionBlockchainPort {
|
||||
constructor(private readonly blockchainService: BlockchainService) {}
|
||||
|
||||
async getProjects(coopname: string): Promise<SovietContract.Tables.Decisions.IDecision[]> {
|
||||
@@ -7,8 +7,8 @@ import { SystemBlockchainAdapter } from './adapters/system.adapter';
|
||||
import { SYSTEM_BLOCKCHAIN_PORT } from '~/domain/system/interfaces/system-blockchain.port';
|
||||
import { ACCOUNT_BLOCKCHAIN_PORT } from '~/domain/account/interfaces/account-blockchain.port';
|
||||
import { AccountBlockchainAdapter } from './adapters/account.adapter';
|
||||
import { DECISION_BLOCKCHAIN_PORT } from '~/domain/decision/interfaces/decision-blockchain.port';
|
||||
import { DecisionBlockchainAdapter } from './adapters/decision-blockchain.adapter';
|
||||
import { DecisionBlockchainAdapter } from './adapters/free-decision-blockchain.adapter';
|
||||
import { FREE_DECISION_BLOCKCHAIN_PORT } from '~/domain/free-decision/interfaces/free-decision-blockchain.port';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
@@ -31,7 +31,7 @@ import { DecisionBlockchainAdapter } from './adapters/decision-blockchain.adapte
|
||||
useClass: AccountBlockchainAdapter,
|
||||
},
|
||||
{
|
||||
provide: DECISION_BLOCKCHAIN_PORT,
|
||||
provide: FREE_DECISION_BLOCKCHAIN_PORT,
|
||||
useClass: DecisionBlockchainAdapter,
|
||||
},
|
||||
],
|
||||
@@ -40,7 +40,7 @@ import { DecisionBlockchainAdapter } from './adapters/decision-blockchain.adapte
|
||||
BRANCH_BLOCKCHAIN_PORT,
|
||||
SYSTEM_BLOCKCHAIN_PORT,
|
||||
ACCOUNT_BLOCKCHAIN_PORT,
|
||||
DECISION_BLOCKCHAIN_PORT,
|
||||
FREE_DECISION_BLOCKCHAIN_PORT,
|
||||
],
|
||||
})
|
||||
export class BlockchainModule {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils';
|
||||
import { UnauthorizedException } from '@nestjs/common';
|
||||
import { GraphQLSchema, defaultFieldResolver } from 'graphql';
|
||||
import config from '~/config/config';
|
||||
|
||||
@@ -33,11 +34,10 @@ export function fieldAuthDirectiveTransformer(schema: GraphQLSchema, directiveNa
|
||||
|
||||
// Проверка соответствия ролей
|
||||
const hasAccess = requiredRoles.includes(user.role);
|
||||
if (!hasAccess) {
|
||||
throw new Error(
|
||||
if (!hasAccess)
|
||||
throw new UnauthorizedException(
|
||||
`Недостаточно прав доступа к полю "${info.fieldName}". Требуемые роли: ${requiredRoles.join(', ')}.`
|
||||
);
|
||||
}
|
||||
|
||||
return resolve(source, args, context, info);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ const tokenSchema = new Schema<IToken>(
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: [tokenTypes.REFRESH, tokenTypes.RESET_PASSWORD, tokenTypes.VERIFY_EMAIL, tokenTypes.INVITE],
|
||||
enum: [tokenTypes.REFRESH, tokenTypes.RESET_KEY, tokenTypes.VERIFY_EMAIL, tokenTypes.INVITE],
|
||||
required: true,
|
||||
},
|
||||
expires: {
|
||||
|
||||
@@ -29,7 +29,7 @@ export class AccountDTO {
|
||||
@Field(() => MonoAccountDTO, { description: 'Объект аккаунта в системе учёта провайдера', nullable: true })
|
||||
@ValidateNested()
|
||||
@Type(() => MonoAccountDTO)
|
||||
public readonly mono_account!: MonoAccountDTO | null;
|
||||
public readonly provider_account!: MonoAccountDTO | null;
|
||||
|
||||
@Field(() => ParticipantAccountDTO, { description: 'Объект пайщика кооператива', nullable: true })
|
||||
@ValidateNested()
|
||||
@@ -39,7 +39,7 @@ export class AccountDTO {
|
||||
constructor(entity: AccountDomainEntity) {
|
||||
this.username = entity.username;
|
||||
this.blockchain_account = entity.blockchain_account || null;
|
||||
this.mono_account = entity.mono_account ? new MonoAccountDTO(entity.mono_account) : null;
|
||||
this.provider_account = entity.provider_account ? new MonoAccountDTO(entity.provider_account) : null;
|
||||
this.user_account = entity.user_account ? new UserAccountDTO(entity.user_account) : null;
|
||||
this.participant_account = entity.participant_account ? new ParticipantAccountDTO(entity.participant_account) : null;
|
||||
//TODO cardcoop_account
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { BankAccountInputDTO } from '~/modules/payment-method/dto/bank-account-input.dto';
|
||||
import { Country } from '../enum/country.enum';
|
||||
import { EntrepreneurDetailsInputDTO } from './entrepreneur-details-input.dto';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
@InputType('CreateEntrepreneurDataInput')
|
||||
export class CreateEntrepreneurDataInputDTO {
|
||||
@Field(() => BankAccountInputDTO, { description: 'Банковский счет' })
|
||||
@IsNotEmpty({ message: 'Поле "bank_account" обязательно для заполнения.' })
|
||||
bank_account!: BankAccountInputDTO;
|
||||
|
||||
@Field({ description: 'Дата рождения' })
|
||||
@IsNotEmpty({ message: 'Поле "birthdate" обязательно для заполнения.' })
|
||||
birthdate!: string;
|
||||
|
||||
@Field({ description: 'Город' })
|
||||
@IsNotEmpty({ message: 'Поле "city" обязательно для заполнения.' })
|
||||
city!: string;
|
||||
|
||||
@Field(() => Country, { description: 'Страна' })
|
||||
@IsNotEmpty({ message: 'Поле "country" обязательно для заполнения.' })
|
||||
country!: Country;
|
||||
|
||||
@Field(() => EntrepreneurDetailsInputDTO, { description: 'Детали индивидуального предпринимателя' })
|
||||
@IsNotEmpty({ message: 'Поле "details" обязательно для заполнения.' })
|
||||
details!: EntrepreneurDetailsInputDTO;
|
||||
|
||||
//поле не принимаем - устанавливаем автоматически
|
||||
email!: string;
|
||||
|
||||
@Field({ description: 'Имя' })
|
||||
@IsNotEmpty({ message: 'Поле "first_name" обязательно для заполнения.' })
|
||||
first_name!: string;
|
||||
|
||||
@Field({ description: 'Полный адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "full_address" обязательно для заполнения.' })
|
||||
full_address!: string;
|
||||
|
||||
@Field({ description: 'Фамилия' })
|
||||
@IsNotEmpty({ message: 'Поле "last_name" обязательно для заполнения.' })
|
||||
last_name!: string;
|
||||
|
||||
@Field({ description: 'Отчество' })
|
||||
@IsNotEmpty({ message: 'Поле "middle_name" обязательно для заполнения.' })
|
||||
middle_name!: string;
|
||||
|
||||
@Field({ description: 'Телефон' })
|
||||
@IsNotEmpty({ message: 'Поле "phone" обязательно для заполнения.' })
|
||||
phone!: string;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsOptional, ValidateNested } from 'class-validator';
|
||||
import { PassportInputDTO } from './passport-input.dto';
|
||||
|
||||
@InputType('CreateIndividualDataInput')
|
||||
export class CreateIndividualDataInputDTO {
|
||||
@Field({ description: 'Дата рождения' })
|
||||
@IsNotEmpty({ message: 'Поле "birthdate" обязательно для заполнения.' })
|
||||
birthdate!: string;
|
||||
|
||||
//поле не принимаем - устанавливаем автоматически
|
||||
email!: string;
|
||||
|
||||
@Field({ description: 'Имя' })
|
||||
@IsNotEmpty({ message: 'Поле "first_name" обязательно для заполнения.' })
|
||||
first_name!: string;
|
||||
|
||||
@Field({ description: 'Полный адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "full_address" обязательно для заполнения.' })
|
||||
full_address!: string;
|
||||
|
||||
@Field({ description: 'Фамилия' })
|
||||
@IsNotEmpty({ message: 'Поле "last_name" обязательно для заполнения.' })
|
||||
last_name!: string;
|
||||
|
||||
@Field({ description: 'Отчество' })
|
||||
@IsNotEmpty({ message: 'Поле "middle_name" обязательно для заполнения.' })
|
||||
middle_name!: string;
|
||||
|
||||
@Field(() => PassportInputDTO, { nullable: true, description: 'Данные паспорта' })
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
passport?: PassportInputDTO;
|
||||
|
||||
@Field({ description: 'Телефон' })
|
||||
@IsNotEmpty({ message: 'Поле "phone" обязательно для заполнения.' })
|
||||
phone!: string;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
import { ValidateNested, IsNotEmpty } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { BankAccountInputDTO } from '~/modules/payment-method/dto/bank-account-input.dto';
|
||||
import { OrganizationType } from '../enum/organization-type.enum';
|
||||
import { OrganizationDetailsInputDTO } from './organization-details-input.dto';
|
||||
import { RepresentedByInputDTO } from './represented-by-input.dto';
|
||||
|
||||
@InputType('CreateOrganizationDataInput')
|
||||
export class CreateOrganizationDataInputDTO {
|
||||
@Field(() => BankAccountInputDTO, { description: 'Банковский счет организации' })
|
||||
@ValidateNested()
|
||||
@Type(() => BankAccountInputDTO)
|
||||
@IsNotEmpty({ message: 'Поле "bank_account" обязательно для заполнения.' })
|
||||
bank_account!: BankAccountInputDTO;
|
||||
|
||||
@Field({ description: 'Город' })
|
||||
@IsNotEmpty({ message: 'Поле "city" обязательно для заполнения.' })
|
||||
city!: string;
|
||||
|
||||
@Field({ description: 'Страна' })
|
||||
@IsNotEmpty({ message: 'Поле "country" обязательно для заполнения.' })
|
||||
country!: string;
|
||||
|
||||
@Field(() => OrganizationDetailsInputDTO, { description: 'Детали организации' })
|
||||
@ValidateNested()
|
||||
@Type(() => OrganizationDetailsInputDTO)
|
||||
@IsNotEmpty({ message: 'Поле "details" обязательно для заполнения.' })
|
||||
details!: OrganizationDetailsInputDTO;
|
||||
|
||||
//поле не принимаем - устанавливаем автоматически
|
||||
email!: string;
|
||||
|
||||
@Field({ description: 'Фактический адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "fact_address" обязательно для заполнения.' })
|
||||
fact_address!: string;
|
||||
|
||||
@Field({ description: 'Полный адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "full_address" обязательно для заполнения.' })
|
||||
full_address!: string;
|
||||
|
||||
@Field({ description: 'Полное наименование организации' })
|
||||
@IsNotEmpty({ message: 'Поле "full_name" обязательно для заполнения.' })
|
||||
full_name!: string;
|
||||
|
||||
@Field({ description: 'Телефон' })
|
||||
@IsNotEmpty({ message: 'Поле "phone" обязательно для заполнения.' })
|
||||
phone!: string;
|
||||
|
||||
@Field(() => RepresentedByInputDTO, { description: 'Представитель организации' })
|
||||
@ValidateNested()
|
||||
@Type(() => RepresentedByInputDTO)
|
||||
@IsNotEmpty({ message: 'Поле "represented_by" обязательно для заполнения.' })
|
||||
represented_by!: RepresentedByInputDTO;
|
||||
|
||||
@Field({ description: 'Краткое наименование организации' })
|
||||
@IsNotEmpty({ message: 'Поле "short_name" обязательно для заполнения.' })
|
||||
short_name!: string;
|
||||
|
||||
@Field(() => OrganizationType, { description: 'Тип организации' })
|
||||
@IsNotEmpty({ message: 'Поле "type" обязательно для заполнения.' })
|
||||
type!: OrganizationType;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
@InputType('DeleteAccountInput')
|
||||
export class DeleteAccountInputDTO {
|
||||
@Field({ description: 'Имя аккаунта пользователя' })
|
||||
@IsNotEmpty({ message: 'Поле "username_for_delete" обязательно для заполнения.' })
|
||||
username_for_delete!: string;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
|
||||
@InputType('EntrepreneurDetailsInput')
|
||||
export class EntrepreneurDetailsInputDTO {
|
||||
@Field(() => String, { description: 'ИНН' })
|
||||
inn!: string;
|
||||
|
||||
@Field(() => String, { description: 'ОГРН' })
|
||||
ogrn!: string;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
|
||||
@InputType('GetAccountsInput')
|
||||
export class GetAccountsInputDTO {
|
||||
@Field({ nullable: true })
|
||||
username?: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
role?: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
|
||||
@InputType('OrganizationDetailsInput')
|
||||
export class OrganizationDetailsInputDTO {
|
||||
@Field()
|
||||
inn!: string;
|
||||
|
||||
@Field()
|
||||
kpp!: string;
|
||||
|
||||
@Field()
|
||||
ogrn!: string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
|
||||
@InputType('PassportInput')
|
||||
export class PassportInputDTO {
|
||||
@Field()
|
||||
code!: string;
|
||||
|
||||
@Field()
|
||||
issued_at!: string;
|
||||
|
||||
@Field()
|
||||
issued_by!: string;
|
||||
|
||||
@Field()
|
||||
number!: number;
|
||||
|
||||
@Field()
|
||||
series!: number;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
import { AccountType } from '../enum/account-type.enum';
|
||||
import { RegisterRole } from '../enum/account-role-on-register.enum';
|
||||
import { CreateEntrepreneurDataInputDTO } from './create-entrepreneur-data-input.dto';
|
||||
import { CreateIndividualDataInputDTO } from './create-individual-data-input.dto';
|
||||
import { CreateOrganizationDataInputDTO } from './create-organization-data-input.dto';
|
||||
import { IsNotEmpty, IsOptional, ValidateIf, ValidateNested } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
@InputType('RegisterAccountInput')
|
||||
export class RegisterAccountInputDTO {
|
||||
@Field({ description: 'Электронная почта' })
|
||||
@IsNotEmpty({ message: 'Поле "email" обязательно для заполнения.' })
|
||||
email!: string;
|
||||
|
||||
@Field({ nullable: true, description: 'Имя аккаунта реферера' })
|
||||
@IsOptional()
|
||||
referer?: string;
|
||||
|
||||
@Field(() => RegisterRole, { description: 'Роль пользователя' })
|
||||
@IsNotEmpty({ message: 'Поле "role" обязательно для заполнения.' })
|
||||
role!: RegisterRole;
|
||||
|
||||
@Field(() => AccountType, { description: 'Тип аккаунта' })
|
||||
@IsNotEmpty({ message: 'Поле "type" обязательно для заполнения.' })
|
||||
type!: AccountType;
|
||||
|
||||
@Field({ description: 'Имя пользователя' })
|
||||
@IsNotEmpty({ message: 'Поле "username" обязательно для заполнения.' })
|
||||
username!: string;
|
||||
|
||||
@Field({ nullable: true, description: 'Публичный ключ' })
|
||||
@IsOptional()
|
||||
public_key?: string;
|
||||
|
||||
@Field(() => CreateEntrepreneurDataInputDTO, { nullable: true, description: 'Данные индивидуального предпринимателя' })
|
||||
@ValidateNested()
|
||||
@Type(() => CreateEntrepreneurDataInputDTO)
|
||||
@IsOptional()
|
||||
entrepreneur_data?: CreateEntrepreneurDataInputDTO;
|
||||
|
||||
@Field(() => CreateIndividualDataInputDTO, { nullable: true, description: 'Данные физического лица' })
|
||||
@ValidateNested()
|
||||
@Type(() => CreateIndividualDataInputDTO)
|
||||
@IsOptional()
|
||||
individual_data?: CreateIndividualDataInputDTO;
|
||||
|
||||
@Field(() => CreateOrganizationDataInputDTO, { nullable: true, description: 'Данные организации' })
|
||||
@ValidateNested()
|
||||
@Type(() => CreateOrganizationDataInputDTO)
|
||||
@IsOptional()
|
||||
organization_data?: CreateOrganizationDataInputDTO;
|
||||
|
||||
@ValidateIf((o: RegisterAccountInputDTO) => !o.entrepreneur_data && !o.individual_data && !o.organization_data)
|
||||
@IsNotEmpty({
|
||||
message: 'Необходимо указать хотя бы одно из полей: "entrepreneur_data", "individual_data" или "organization_data".',
|
||||
})
|
||||
validateOneTypePresent!: boolean;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { AccountDTO } from './account.dto';
|
||||
import { TokensDTO } from './tokens.dto';
|
||||
import { ValidateNested } from 'class-validator';
|
||||
import type { RegisteredAccountDomainInterface } from '~/domain/account/interfaces/registeted-account.interface';
|
||||
|
||||
@ObjectType('RegisteredAccount')
|
||||
export class RegisteredAccountDTO {
|
||||
@Field(() => AccountDTO, { description: 'Информация об зарегистрированном аккаунте' })
|
||||
@ValidateNested()
|
||||
account!: AccountDTO;
|
||||
|
||||
@Field(() => TokensDTO, { description: 'Токены доступа и обновления' })
|
||||
@ValidateNested()
|
||||
tokens!: TokensDTO;
|
||||
|
||||
constructor(data: RegisteredAccountDomainInterface) {
|
||||
this.account = new AccountDTO(data.account);
|
||||
this.tokens = data.tokens;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
|
||||
@InputType('RepresentedByInput')
|
||||
export class RepresentedByInputDTO {
|
||||
@Field()
|
||||
based_on!: string;
|
||||
|
||||
@Field()
|
||||
first_name!: string;
|
||||
|
||||
@Field()
|
||||
last_name!: string;
|
||||
|
||||
@Field()
|
||||
middle_name!: string;
|
||||
|
||||
@Field()
|
||||
position!: string;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('Token')
|
||||
export class TokenDTO {
|
||||
@Field({ description: 'Токен доступа' })
|
||||
token!: string;
|
||||
|
||||
@Field({ description: 'Дата истечения токена доступа' })
|
||||
expires!: Date;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { TokenDTO } from './token.dto';
|
||||
|
||||
@ObjectType('Tokens')
|
||||
export class TokensDTO {
|
||||
@Field(() => TokenDTO, { description: 'Токен доступа' })
|
||||
access!: TokenDTO;
|
||||
|
||||
@Field(() => TokenDTO, { description: 'Токен обновления' })
|
||||
refresh!: TokenDTO;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
import { AccountType } from '../enum/account-type.enum';
|
||||
import { RegisterRole } from '../enum/account-role-on-register.enum';
|
||||
import { IsNotEmpty, IsOptional, ValidateIf, ValidateNested } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { UpdateEntrepreneurDataInputDTO } from './update-entrepreneur-data-input.dto';
|
||||
import { UpdateIndividualDataInputDTO } from './update-individual-data-input.dto';
|
||||
import { UpdateOrganizationDataInputDTO } from './update-organization-data-input.dto';
|
||||
|
||||
@InputType('UpdateAccountInput')
|
||||
export class UpdateAccountInputDTO {
|
||||
@Field({ description: 'Электронная почта' })
|
||||
@IsNotEmpty({ message: 'Поле "email" обязательно для заполнения.' })
|
||||
email!: string;
|
||||
|
||||
@Field({ nullable: true, description: 'Имя аккаунта реферера' })
|
||||
@IsOptional()
|
||||
referer?: string;
|
||||
|
||||
@Field(() => RegisterRole, { description: 'Роль пользователя' })
|
||||
@IsNotEmpty({ message: 'Поле "role" обязательно для заполнения.' })
|
||||
role!: RegisterRole;
|
||||
|
||||
@Field(() => AccountType, { description: 'Тип аккаунта' })
|
||||
@IsNotEmpty({ message: 'Поле "type" обязательно для заполнения.' })
|
||||
type!: AccountType;
|
||||
|
||||
@Field({ description: 'Имя пользователя' })
|
||||
@IsNotEmpty({ message: 'Поле "username" обязательно для заполнения.' })
|
||||
username!: string;
|
||||
|
||||
@Field({ nullable: true, description: 'Публичный ключ' })
|
||||
@IsOptional()
|
||||
public_key?: string;
|
||||
|
||||
@Field(() => UpdateEntrepreneurDataInputDTO, { nullable: true, description: 'Данные индивидуального предпринимателя' })
|
||||
@ValidateNested()
|
||||
@Type(() => UpdateEntrepreneurDataInputDTO)
|
||||
@IsOptional()
|
||||
entrepreneur_data?: UpdateEntrepreneurDataInputDTO;
|
||||
|
||||
@Field(() => UpdateIndividualDataInputDTO, { nullable: true, description: 'Данные физического лица' })
|
||||
@ValidateNested()
|
||||
@Type(() => UpdateIndividualDataInputDTO)
|
||||
@IsOptional()
|
||||
individual_data?: UpdateIndividualDataInputDTO;
|
||||
|
||||
@Field(() => UpdateOrganizationDataInputDTO, { nullable: true, description: 'Данные организации' })
|
||||
@ValidateNested()
|
||||
@Type(() => UpdateOrganizationDataInputDTO)
|
||||
@IsOptional()
|
||||
organization_data?: UpdateOrganizationDataInputDTO;
|
||||
|
||||
@ValidateIf((o: UpdateAccountInputDTO) => !o.entrepreneur_data && !o.individual_data && !o.organization_data)
|
||||
@IsNotEmpty({
|
||||
message: 'Необходимо указать хотя бы одно из полей: "entrepreneur_data", "individual_data" или "organization_data".',
|
||||
})
|
||||
validateOneTypePresent!: boolean;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { BankAccountInputDTO } from '~/modules/payment-method/dto/bank-account-input.dto';
|
||||
import { Country } from '../enum/country.enum';
|
||||
import { EntrepreneurDetailsInputDTO } from './entrepreneur-details-input.dto';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
@InputType('UpdateEntrepreneurDataInput')
|
||||
export class UpdateEntrepreneurDataInputDTO {
|
||||
@Field({ description: 'Дата рождения' })
|
||||
@IsNotEmpty({ message: 'Поле "birthdate" обязательно для заполнения.' })
|
||||
birthdate!: string;
|
||||
|
||||
@Field({ description: 'Город' })
|
||||
@IsNotEmpty({ message: 'Поле "city" обязательно для заполнения.' })
|
||||
city!: string;
|
||||
|
||||
@Field(() => Country, { description: 'Страна' })
|
||||
@IsNotEmpty({ message: 'Поле "country" обязательно для заполнения.' })
|
||||
country!: Country;
|
||||
|
||||
@Field(() => EntrepreneurDetailsInputDTO, { description: 'Детали индивидуального предпринимателя' })
|
||||
@IsNotEmpty({ message: 'Поле "details" обязательно для заполнения.' })
|
||||
details!: EntrepreneurDetailsInputDTO;
|
||||
|
||||
//поле не принимаем - устанавливаем автоматически
|
||||
email!: string;
|
||||
|
||||
@Field({ description: 'Имя' })
|
||||
@IsNotEmpty({ message: 'Поле "first_name" обязательно для заполнения.' })
|
||||
first_name!: string;
|
||||
|
||||
@Field({ description: 'Полный адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "full_address" обязательно для заполнения.' })
|
||||
full_address!: string;
|
||||
|
||||
@Field({ description: 'Фамилия' })
|
||||
@IsNotEmpty({ message: 'Поле "last_name" обязательно для заполнения.' })
|
||||
last_name!: string;
|
||||
|
||||
@Field({ description: 'Отчество' })
|
||||
@IsNotEmpty({ message: 'Поле "middle_name" обязательно для заполнения.' })
|
||||
middle_name!: string;
|
||||
|
||||
@Field({ description: 'Телефон' })
|
||||
@IsNotEmpty({ message: 'Поле "phone" обязательно для заполнения.' })
|
||||
phone!: string;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsOptional, ValidateNested } from 'class-validator';
|
||||
import { PassportInputDTO } from './passport-input.dto';
|
||||
|
||||
@InputType('UpdateIndividualDataInput')
|
||||
export class UpdateIndividualDataInputDTO {
|
||||
@Field({ description: 'Дата рождения' })
|
||||
@IsNotEmpty({ message: 'Поле "birthdate" обязательно для заполнения.' })
|
||||
birthdate!: string;
|
||||
|
||||
//поле не принимаем - устанавливаем автоматически
|
||||
email!: string;
|
||||
|
||||
@Field({ description: 'Имя' })
|
||||
@IsNotEmpty({ message: 'Поле "first_name" обязательно для заполнения.' })
|
||||
first_name!: string;
|
||||
|
||||
@Field({ description: 'Полный адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "full_address" обязательно для заполнения.' })
|
||||
full_address!: string;
|
||||
|
||||
@Field({ description: 'Фамилия' })
|
||||
@IsNotEmpty({ message: 'Поле "last_name" обязательно для заполнения.' })
|
||||
last_name!: string;
|
||||
|
||||
@Field({ description: 'Отчество' })
|
||||
@IsNotEmpty({ message: 'Поле "middle_name" обязательно для заполнения.' })
|
||||
middle_name!: string;
|
||||
|
||||
@Field(() => PassportInputDTO, { nullable: true, description: 'Данные паспорта' })
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
passport?: PassportInputDTO;
|
||||
|
||||
@Field({ description: 'Телефон' })
|
||||
@IsNotEmpty({ message: 'Поле "phone" обязательно для заполнения.' })
|
||||
phone!: string;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { InputType, Field } from '@nestjs/graphql';
|
||||
import { ValidateNested, IsNotEmpty } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { OrganizationType } from '../enum/organization-type.enum';
|
||||
import { OrganizationDetailsInputDTO } from './organization-details-input.dto';
|
||||
import { RepresentedByInputDTO } from './represented-by-input.dto';
|
||||
|
||||
@InputType('UpdateOrganizationDataInput')
|
||||
export class UpdateOrganizationDataInputDTO {
|
||||
@Field({ description: 'Город' })
|
||||
@IsNotEmpty({ message: 'Поле "city" обязательно для заполнения.' })
|
||||
city!: string;
|
||||
|
||||
@Field({ description: 'Страна' })
|
||||
@IsNotEmpty({ message: 'Поле "country" обязательно для заполнения.' })
|
||||
country!: string;
|
||||
|
||||
@Field(() => OrganizationDetailsInputDTO, { description: 'Детали организации' })
|
||||
@ValidateNested()
|
||||
@Type(() => OrganizationDetailsInputDTO)
|
||||
@IsNotEmpty({ message: 'Поле "details" обязательно для заполнения.' })
|
||||
details!: OrganizationDetailsInputDTO;
|
||||
|
||||
//поле не принимаем - устанавливаем автоматически
|
||||
email!: string;
|
||||
|
||||
@Field({ description: 'Фактический адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "fact_address" обязательно для заполнения.' })
|
||||
fact_address!: string;
|
||||
|
||||
@Field({ description: 'Полный адрес' })
|
||||
@IsNotEmpty({ message: 'Поле "full_address" обязательно для заполнения.' })
|
||||
full_address!: string;
|
||||
|
||||
@Field({ description: 'Полное наименование организации' })
|
||||
@IsNotEmpty({ message: 'Поле "full_name" обязательно для заполнения.' })
|
||||
full_name!: string;
|
||||
|
||||
@Field({ description: 'Телефон' })
|
||||
@IsNotEmpty({ message: 'Поле "phone" обязательно для заполнения.' })
|
||||
phone!: string;
|
||||
|
||||
@Field(() => RepresentedByInputDTO, { description: 'Представитель организации' })
|
||||
@ValidateNested()
|
||||
@Type(() => RepresentedByInputDTO)
|
||||
@IsNotEmpty({ message: 'Поле "represented_by" обязательно для заполнения.' })
|
||||
represented_by!: RepresentedByInputDTO;
|
||||
|
||||
@Field({ description: 'Краткое наименование организации' })
|
||||
@IsNotEmpty({ message: 'Поле "short_name" обязательно для заполнения.' })
|
||||
short_name!: string;
|
||||
|
||||
@Field(() => OrganizationType, { description: 'Тип организации' })
|
||||
@IsNotEmpty({ message: 'Поле "type" обязательно для заполнения.' })
|
||||
type!: OrganizationType;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
export enum RegisterRole {
|
||||
User = 'user',
|
||||
}
|
||||
registerEnumType(RegisterRole, { name: 'RegisterRole', description: 'Роль пользователя при регистрации' });
|
||||
@@ -0,0 +1,8 @@
|
||||
import { registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
export enum AccountType {
|
||||
Individual = 'individual',
|
||||
Entrepreneur = 'entrepreneur',
|
||||
Organization = 'organization',
|
||||
}
|
||||
registerEnumType(AccountType, { name: 'AccountType', description: 'Тип аккаунта пользователя в системе' });
|
||||
@@ -0,0 +1,6 @@
|
||||
import { registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
export enum Country {
|
||||
Russia = 'Russia',
|
||||
}
|
||||
registerEnumType(Country, { name: 'Country', description: 'Страна регистрации пользователя' });
|
||||
@@ -0,0 +1,11 @@
|
||||
import { registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
export enum OrganizationType {
|
||||
COOP = 'coop',
|
||||
OOO = 'ooo',
|
||||
OAO = 'oao',
|
||||
ZAO = 'zao',
|
||||
PAO = 'pao',
|
||||
AO = 'ao',
|
||||
}
|
||||
registerEnumType(OrganizationType, { name: 'OrganizationType', description: 'Тип юридического лица' });
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Resolver, Query, Args } from '@nestjs/graphql';
|
||||
import { Resolver, Query, Args, Mutation } from '@nestjs/graphql';
|
||||
import { AccountService } from '../services/account.service';
|
||||
import { AccountDTO } from '../dto/account.dto';
|
||||
import { GetAccountInputDTO } from '../dto/get-account-input.dto';
|
||||
@@ -6,6 +6,16 @@ import { UseGuards } from '@nestjs/common';
|
||||
import { AuthRoles } from '~/modules/auth/decorators/auth.decorator';
|
||||
import { GqlJwtAuthGuard } from '~/modules/auth/guards/graphql-jwt-auth.guard';
|
||||
import { RolesGuard } from '~/modules/auth/guards/roles.guard';
|
||||
import { createPaginationResult, PaginationInputDTO } from '~/modules/common/dto/pagination.dto';
|
||||
import { GetAccountsInputDTO } from '../dto/get-accounts-input.dto';
|
||||
import type { PaginationResultDomainInterface } from '~/domain/common/interfaces/pagination.interface';
|
||||
import type { AccountDomainEntity } from '~/domain/account/entities/account-domain.entity';
|
||||
import { RegisterAccountInputDTO } from '../dto/register-account-input.dto';
|
||||
import { RegisteredAccountDTO } from '../dto/registered-account.dto';
|
||||
import { DeleteAccountInputDTO } from '../dto/delete-account-input.dto';
|
||||
import { UpdateAccountInputDTO } from '../dto/update-account-input.dto';
|
||||
|
||||
export const AccountsPaginationResult = createPaginationResult(AccountDTO, 'Accounts');
|
||||
|
||||
@Resolver(() => AccountDTO)
|
||||
export class AccountResolver {
|
||||
@@ -20,4 +30,54 @@ export class AccountResolver {
|
||||
async getAccount(@Args('data', { type: () => GetAccountInputDTO }) input: GetAccountInputDTO): Promise<AccountDTO> {
|
||||
return this.accountService.getAccount(input.username);
|
||||
}
|
||||
|
||||
@Query(() => AccountsPaginationResult, {
|
||||
name: 'getAccounts',
|
||||
description: 'Получить сводную информацию о аккаунтах системы',
|
||||
})
|
||||
async getAccounts(
|
||||
@Args('data', { type: () => GetAccountsInputDTO, nullable: true }) data: GetAccountsInputDTO,
|
||||
@Args('options', { type: () => PaginationInputDTO, nullable: true }) options: PaginationInputDTO
|
||||
): Promise<PaginationResultDomainInterface<AccountDomainEntity>> {
|
||||
return await this.accountService.getAccounts(data, options);
|
||||
}
|
||||
|
||||
@Mutation(() => RegisteredAccountDTO, {
|
||||
name: 'registerAccount',
|
||||
description: 'Зарегистрировать аккаунт пользователя в системе',
|
||||
})
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
async registerAccount(
|
||||
@Args('data', { type: () => RegisterAccountInputDTO })
|
||||
data: RegisterAccountInputDTO
|
||||
): Promise<RegisteredAccountDTO> {
|
||||
return this.accountService.registerAccount(data);
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean, {
|
||||
name: 'deleteAccount',
|
||||
description: 'Удалить аккаунт из системы учёта провайдера',
|
||||
})
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async deleteAccount(
|
||||
@Args('data', { type: () => DeleteAccountInputDTO })
|
||||
data: DeleteAccountInputDTO
|
||||
): Promise<boolean> {
|
||||
await this.accountService.deleteAccount(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Mutation(() => AccountDTO, {
|
||||
name: 'updateAccount',
|
||||
description: 'Обновить аккаунт в системе провайдера',
|
||||
})
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
async updateAccount(
|
||||
@Args('data', { type: () => UpdateAccountInputDTO })
|
||||
data: UpdateAccountInputDTO
|
||||
): Promise<AccountDTO> {
|
||||
return await this.accountService.updateAccount(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AccountDTO } from '../dto/account.dto';
|
||||
import { AccountDomainInteractor } from '~/domain/account/interactors/account.interactor';
|
||||
import type { GetAccountsInputDTO } from '../dto/get-accounts-input.dto';
|
||||
import type { PaginationInputDTO } from '~/modules/common/dto/pagination.dto';
|
||||
import type { AccountDomainEntity } from '~/domain/account/entities/account-domain.entity';
|
||||
import type { PaginationResultDomainInterface } from '~/domain/common/interfaces/pagination.interface';
|
||||
import type { RegisterAccountInputDTO } from '../dto/register-account-input.dto';
|
||||
import { RegisteredAccountDTO } from '../dto/registered-account.dto';
|
||||
import type { DeleteAccountInputDTO } from '../dto/delete-account-input.dto';
|
||||
import type { UpdateAccountInputDTO } from '../dto/update-account-input.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AccountService {
|
||||
constructor(private readonly accountDomainInteractor: AccountDomainInteractor) {}
|
||||
|
||||
public async updateAccount(data: UpdateAccountInputDTO): Promise<AccountDTO> {
|
||||
const result = await this.accountDomainInteractor.updateAccount(data);
|
||||
return new AccountDTO(result);
|
||||
}
|
||||
|
||||
public async deleteAccount(data: DeleteAccountInputDTO): Promise<void> {
|
||||
await this.accountDomainInteractor.deleteAccount(data.username_for_delete);
|
||||
}
|
||||
|
||||
public async getAccount(username: string): Promise<AccountDTO> {
|
||||
const account = await this.accountDomainInteractor.getAccount(username);
|
||||
|
||||
return new AccountDTO(account);
|
||||
}
|
||||
|
||||
public async getAccounts(
|
||||
data: GetAccountsInputDTO,
|
||||
options: PaginationInputDTO
|
||||
): Promise<PaginationResultDomainInterface<AccountDomainEntity>> {
|
||||
return await this.accountDomainInteractor.getAccounts(data, options);
|
||||
}
|
||||
|
||||
public async registerAccount(data: RegisterAccountInputDTO): Promise<RegisteredAccountDTO> {
|
||||
const result = await this.accountDomainInteractor.registerAccount(data);
|
||||
return new RegisteredAccountDTO(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DomainModule } from '~/domain/domain.module';
|
||||
import { AgendaResolver } from './resolvers/agenda.resolver';
|
||||
import { AgendaService } from './services/agenda.service';
|
||||
|
||||
@Module({
|
||||
imports: [DomainModule],
|
||||
controllers: [],
|
||||
providers: [AgendaResolver, AgendaService],
|
||||
exports: [],
|
||||
})
|
||||
export class AgendaModule {}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { GeneratedDocumentDTO } from '~/modules/document/dto/generated-document.dto';
|
||||
import type { ActDetailDomainInterface } from '~/domain/agenda/interfaces/act-detail-domain.interface';
|
||||
import { ExtendedBlockchainActionDTO } from './extended-action.dto';
|
||||
|
||||
@ObjectType('ActDetail')
|
||||
export class ActDetailDTO implements ActDetailDomainInterface {
|
||||
@Field(() => ExtendedBlockchainActionDTO, { nullable: true })
|
||||
action?: ExtendedBlockchainActionDTO;
|
||||
|
||||
@Field(() => GeneratedDocumentDTO, { nullable: true })
|
||||
document?: GeneratedDocumentDTO;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { DocumentPackageDTO } from './document-package.dto';
|
||||
import type { AgendaWithDocumentsDomainInterface } from '~/domain/agenda/interfaces/agenda-with-documents-domain.interface';
|
||||
import { BlockchainActionDTO } from './blockchain-action.dto';
|
||||
import { BlockchainDecisionDTO } from './blockchain-decision.dto';
|
||||
|
||||
@ObjectType('AgendaWithDocuments')
|
||||
export class AgendaWithDocumentsDTO implements AgendaWithDocumentsDomainInterface {
|
||||
@Field(() => BlockchainDecisionDTO, {
|
||||
description: 'Запись в таблице блокчейна о вопросе на голосовании',
|
||||
})
|
||||
table!: BlockchainDecisionDTO;
|
||||
|
||||
@Field(() => BlockchainActionDTO, {
|
||||
description: 'Действие, которое привело к появлению вопроса на голосовании',
|
||||
})
|
||||
action!: BlockchainActionDTO;
|
||||
|
||||
@Field(() => DocumentPackageDTO, {
|
||||
description: 'Пакет документов, включающий разные подсекции',
|
||||
})
|
||||
documents!: DocumentPackageDTO;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { ObjectType, Field, Int } from '@nestjs/graphql';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
/**
|
||||
* Составные части `BlockchainAction`
|
||||
*/
|
||||
@ObjectType('ActionAuthorization')
|
||||
export class ActionAuthorizationDTO {
|
||||
@Field()
|
||||
actor!: string;
|
||||
|
||||
@Field()
|
||||
permission!: string;
|
||||
}
|
||||
|
||||
@ObjectType('AccountRamDelta')
|
||||
export class AccountRamDeltaDTO {
|
||||
@Field()
|
||||
account!: string;
|
||||
|
||||
@Field(() => Int)
|
||||
delta!: number;
|
||||
}
|
||||
|
||||
@ObjectType('ActionReceipt')
|
||||
export class ActionReceiptDTO {
|
||||
@Field()
|
||||
receiver!: string;
|
||||
|
||||
@Field()
|
||||
act_digest!: string;
|
||||
|
||||
@Field()
|
||||
global_sequence!: string;
|
||||
|
||||
@Field()
|
||||
recv_sequence!: string;
|
||||
|
||||
@Field(() => [AuthSequenceDTO])
|
||||
auth_sequence!: AuthSequenceDTO[];
|
||||
|
||||
@Field(() => Int)
|
||||
code_sequence!: number;
|
||||
|
||||
@Field(() => Int)
|
||||
abi_sequence!: number;
|
||||
}
|
||||
|
||||
@ObjectType('AuthSequence')
|
||||
export class AuthSequenceDTO {
|
||||
@Field()
|
||||
account!: string;
|
||||
|
||||
@Field()
|
||||
sequence!: string;
|
||||
}
|
||||
|
||||
@ObjectType('BlockchainAction', {
|
||||
description: 'Объект действия в блокчейне',
|
||||
})
|
||||
export class BlockchainActionDTO {
|
||||
@Field()
|
||||
transaction_id!: string;
|
||||
|
||||
@Field()
|
||||
account!: string;
|
||||
|
||||
@Field(() => Int)
|
||||
block_num!: number;
|
||||
|
||||
@Field()
|
||||
block_id!: string;
|
||||
|
||||
@Field()
|
||||
chain_id!: string;
|
||||
|
||||
@Field()
|
||||
name!: string;
|
||||
|
||||
@Field()
|
||||
receiver!: string;
|
||||
|
||||
@Field(() => [ActionAuthorizationDTO])
|
||||
authorization!: ActionAuthorizationDTO[];
|
||||
|
||||
@Field(() => GraphQLJSON, { description: 'Произвольные данные действия в формате JSON' })
|
||||
data!: any; // Теперь это явно JSON
|
||||
|
||||
@Field(() => Int)
|
||||
action_ordinal!: number;
|
||||
|
||||
@Field()
|
||||
global_sequence!: string;
|
||||
|
||||
@Field(() => [AccountRamDeltaDTO])
|
||||
account_ram_deltas!: AccountRamDeltaDTO[];
|
||||
|
||||
@Field()
|
||||
console!: string;
|
||||
|
||||
@Field(() => ActionReceiptDTO)
|
||||
receipt!: ActionReceiptDTO;
|
||||
|
||||
@Field(() => Int)
|
||||
creator_action_ordinal!: number;
|
||||
|
||||
@Field()
|
||||
context_free!: boolean;
|
||||
|
||||
@Field(() => Int)
|
||||
elapsed!: number;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import type { SovietContract } from 'cooptypes';
|
||||
import { SignedBlockchainDocumentDTO } from '~/modules/document/dto/signed-blockchain-document.dto';
|
||||
|
||||
@ObjectType('BlockchainDecision', {
|
||||
description: 'Запись в таблице блокчейна о процессе принятия решения советом кооператива',
|
||||
})
|
||||
export class BlockchainDecisionDTO implements SovietContract.Tables.Decisions.IDecision {
|
||||
@Field(() => Number)
|
||||
id!: number | string;
|
||||
|
||||
@Field()
|
||||
coopname!: string;
|
||||
|
||||
@Field()
|
||||
username!: string;
|
||||
|
||||
@Field()
|
||||
type!: string;
|
||||
|
||||
@Field(() => Number)
|
||||
batch_id!: number | string;
|
||||
|
||||
@Field(() => SignedBlockchainDocumentDTO)
|
||||
statement!: SignedBlockchainDocumentDTO;
|
||||
|
||||
@Field(() => [String])
|
||||
votes_for!: string[];
|
||||
|
||||
@Field(() => [String])
|
||||
votes_against!: string[];
|
||||
|
||||
@Field()
|
||||
validated!: boolean;
|
||||
|
||||
@Field()
|
||||
approved!: boolean;
|
||||
|
||||
@Field()
|
||||
authorized!: boolean;
|
||||
|
||||
@Field()
|
||||
authorized_by!: string;
|
||||
|
||||
@Field(() => SignedBlockchainDocumentDTO)
|
||||
authorization!: SignedBlockchainDocumentDTO;
|
||||
|
||||
@Field()
|
||||
created_at!: string;
|
||||
|
||||
@Field()
|
||||
expired_at!: string;
|
||||
|
||||
@Field()
|
||||
meta!: string;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { GeneratedDocumentDTO } from '~/modules/document/dto/generated-document.dto';
|
||||
import type { DecisionDetailDomainInterface } from '~/domain/agenda/interfaces/decision-detail-domain.interface';
|
||||
import { ExtendedBlockchainActionDTO } from './extended-action.dto';
|
||||
|
||||
@ObjectType('DecisionDetail', {
|
||||
description: 'Комплексный объект решения совета, включающий в себя информацию о голосовавших членах совета',
|
||||
})
|
||||
export class DecisionDetailDTO implements DecisionDetailDomainInterface {
|
||||
@Field(() => ExtendedBlockchainActionDTO)
|
||||
action!: ExtendedBlockchainActionDTO;
|
||||
|
||||
@Field(() => GeneratedDocumentDTO)
|
||||
document!: GeneratedDocumentDTO;
|
||||
|
||||
@Field(() => [ExtendedBlockchainActionDTO])
|
||||
votes_for!: ExtendedBlockchainActionDTO[];
|
||||
|
||||
@Field(() => [ExtendedBlockchainActionDTO])
|
||||
votes_against!: ExtendedBlockchainActionDTO[];
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { GeneratedDocumentDTO } from '~/modules/document/dto/generated-document.dto';
|
||||
import { StatementDetailDTO } from './statement-detail.dto';
|
||||
import { DecisionDetailDTO } from './decision-detail.dto';
|
||||
import { ActDetailDTO } from './act-detail.dto';
|
||||
import type { DocumentPackageDomainInterface } from '~/domain/agenda/interfaces/document-package-domain.interface';
|
||||
|
||||
@ObjectType('DocumentPackage', {
|
||||
description:
|
||||
'Комплексный объект папки цифрового документа, который включает в себя заявление, решение, акты и связанные документы',
|
||||
})
|
||||
export class DocumentPackageDTO implements DocumentPackageDomainInterface {
|
||||
@Field(() => StatementDetailDTO, {
|
||||
description: 'Объект цифрового документа заявления',
|
||||
})
|
||||
statement!: StatementDetailDTO;
|
||||
|
||||
@Field(() => DecisionDetailDTO, {
|
||||
description: 'Объект цифрового документа решения',
|
||||
})
|
||||
decision!: DecisionDetailDTO;
|
||||
|
||||
@Field(() => [ActDetailDTO], {
|
||||
description: 'Массив объект(ов) актов, относящихся к заявлению',
|
||||
})
|
||||
acts!: ActDetailDTO[];
|
||||
|
||||
@Field(() => [GeneratedDocumentDTO], {
|
||||
description: 'Массив связанных документов, извлечённых из мета-данных',
|
||||
})
|
||||
links!: GeneratedDocumentDTO[];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { IndividualDTO } from '~/modules/common/dto/individual.dto';
|
||||
import { EntrepreneurDTO } from '~/modules/common/dto/entrepreneur.dto';
|
||||
import { OrganizationDTO } from '~/modules/common/dto/organization.dto';
|
||||
import type { ExtendedBlockchainActionDomainInterface } from '~/domain/agenda/interfaces/extended-blockchain-action-domain.interface';
|
||||
import { BlockchainActionDTO } from './blockchain-action.dto';
|
||||
|
||||
@ObjectType('ExtendedBlockchainAction', {
|
||||
description: 'Расширенное действие блокчейна с персональными данными пользователя, совершившего его.',
|
||||
})
|
||||
export class ExtendedBlockchainActionDTO extends BlockchainActionDTO implements ExtendedBlockchainActionDomainInterface {
|
||||
@Field(() => [IndividualDTO, EntrepreneurDTO, OrganizationDTO], {
|
||||
nullable: true,
|
||||
description: 'Доп. данные о пользователе (физ/ИП/организация)',
|
||||
})
|
||||
user?: IndividualDTO | EntrepreneurDTO | OrganizationDTO | null | undefined;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { GeneratedDocumentDTO } from '~/modules/document/dto/generated-document.dto';
|
||||
import type { StatementDetailDomainInterface } from '~/domain/agenda/interfaces/statement-detail-domain.interface';
|
||||
import { ExtendedBlockchainActionDTO } from './extended-action.dto';
|
||||
|
||||
@ObjectType('StatementDetail', {
|
||||
description:
|
||||
'Комплексный объект цифрового документа заявления (или другого ведущего документа для цепочки принятия решений совета)',
|
||||
})
|
||||
export class StatementDetailDTO implements StatementDetailDomainInterface {
|
||||
@Field(() => ExtendedBlockchainActionDTO)
|
||||
action!: ExtendedBlockchainActionDTO;
|
||||
|
||||
@Field(() => GeneratedDocumentDTO)
|
||||
document!: GeneratedDocumentDTO;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
|
||||
import { GqlJwtAuthGuard } from '~/modules/auth/guards/graphql-jwt-auth.guard';
|
||||
import { RolesGuard } from '~/modules/auth/guards/roles.guard';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { AuthRoles } from '~/modules/auth/decorators/auth.decorator';
|
||||
import { AgendaService } from '../services/agenda.service';
|
||||
import { AgendaWithDocumentsDTO } from '../dto/agenda-with-documents.dto';
|
||||
|
||||
@Resolver()
|
||||
export class AgendaResolver {
|
||||
constructor(private readonly agendaService: AgendaService) {}
|
||||
//TODO add votefor / voteagainst
|
||||
|
||||
@Query(() => [AgendaWithDocumentsDTO], {
|
||||
name: 'getAgenda',
|
||||
description: 'Получить список вопросов совета кооператива для голосования',
|
||||
})
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async getAgenda(): Promise<AgendaWithDocumentsDTO[]> {
|
||||
return this.agendaService.getAgenda();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { type AgendaWithDocumentsDTO } from '../dto/agenda-with-documents.dto';
|
||||
import { AgendaDomainInteractor } from '~/domain/agenda/interactors/agenda-domain.interactor';
|
||||
|
||||
@Injectable()
|
||||
export class AgendaService {
|
||||
constructor(private readonly agendaDomainInteractor: AgendaDomainInteractor) {}
|
||||
|
||||
public async getAgenda(): Promise<AgendaWithDocumentsDTO[]> {
|
||||
const agenda = await this.agendaDomainInteractor.getAgenda();
|
||||
return agenda;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DomainModule } from '~/domain/domain.module';
|
||||
import { AgreementResolver } from './resolvers/agreement.resolver';
|
||||
import { AgreementService } from './services/agreement.service';
|
||||
|
||||
@Module({
|
||||
imports: [DomainModule],
|
||||
controllers: [],
|
||||
providers: [AgreementResolver, AgreementService],
|
||||
exports: [],
|
||||
})
|
||||
export class AgreementModule {}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Resolver, Mutation, Args } from '@nestjs/graphql';
|
||||
import { GqlJwtAuthGuard } from '~/modules/auth/guards/graphql-jwt-auth.guard';
|
||||
import { RolesGuard } from '~/modules/auth/guards/roles.guard';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { AuthRoles } from '~/modules/auth/decorators/auth.decorator';
|
||||
import { GenerateDocumentOptionsInputDTO } from '~/modules/document/dto/generate-document-options-input.dto';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
import { GeneratedDocumentDTO } from '~/modules/document/dto/generated-document.dto';
|
||||
import { GenerateDocumentInputDTO } from '~/modules/document/dto/generate-document-input.dto';
|
||||
import { AgreementService } from '../services/agreement.service';
|
||||
|
||||
@Resolver()
|
||||
export class AgreementResolver {
|
||||
constructor(private readonly agreementService: AgreementService) {}
|
||||
|
||||
@Mutation(() => GeneratedDocumentDTO, {
|
||||
name: 'generateWalletAgreement',
|
||||
description: 'Сгенерировать документ соглашения о целевой потребительской программе "Цифровой Кошелёк"',
|
||||
})
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async generateWalletAgreement(
|
||||
@Args('data', { type: () => GenerateDocumentInputDTO })
|
||||
data: GenerateDocumentInputDTO,
|
||||
@Args('options', { type: () => GenerateDocumentOptionsInputDTO, nullable: true })
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return this.agreementService.generateWalletAgreement(data, options);
|
||||
}
|
||||
|
||||
@Mutation(() => GeneratedDocumentDTO, {
|
||||
name: 'generatePrivacyAgreement',
|
||||
description: 'Сгенерировать документ соглашения с политикой конфиденциальности',
|
||||
})
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async generatePrivacyAgreement(
|
||||
@Args('data', { type: () => GenerateDocumentInputDTO })
|
||||
data: GenerateDocumentInputDTO,
|
||||
@Args('options', { type: () => GenerateDocumentOptionsInputDTO, nullable: true })
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return this.agreementService.generatePrivacyAgreement(data, options);
|
||||
}
|
||||
|
||||
@Mutation(() => GeneratedDocumentDTO, {
|
||||
name: 'generateSignatureAgreement',
|
||||
description: 'Сгенерировать документ соглашения о целевой потребительской программе "Цифровой Кошелёк"',
|
||||
})
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async generateSignatureAgreement(
|
||||
@Args('data', { type: () => GenerateDocumentInputDTO })
|
||||
data: GenerateDocumentInputDTO,
|
||||
@Args('options', { type: () => GenerateDocumentOptionsInputDTO, nullable: true })
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return this.agreementService.generateSignatureAgreement(data, options);
|
||||
}
|
||||
|
||||
@Mutation(() => GeneratedDocumentDTO, {
|
||||
name: 'generateUserAgreement',
|
||||
description: 'Сгенерировать документ пользовательского соглашения',
|
||||
})
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async generateUserAgreement(
|
||||
@Args('data', { type: () => GenerateDocumentInputDTO })
|
||||
data: GenerateDocumentInputDTO,
|
||||
@Args('options', { type: () => GenerateDocumentOptionsInputDTO, nullable: true })
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return this.agreementService.generateUserAgreement(data, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { GenerateDocumentOptionsInputDTO } from '~/modules/document/dto/generate-document-options-input.dto';
|
||||
import { GeneratedDocumentDTO } from '~/modules/document/dto/generated-document.dto';
|
||||
import { GenerateDocumentInputDTO } from '~/modules/document/dto/generate-document-input.dto';
|
||||
import { AgreementDomainInteractor } from '~/domain/agreement/interactors/agreement-domain.interactor';
|
||||
|
||||
@Injectable()
|
||||
export class AgreementService {
|
||||
constructor(private readonly agreementDomainInteractor: AgreementDomainInteractor) {}
|
||||
|
||||
public async generateWalletAgreement(
|
||||
data: GenerateDocumentInputDTO,
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
const document = await this.agreementDomainInteractor.generateWalletAgreement(data, options);
|
||||
return document as GeneratedDocumentDTO;
|
||||
}
|
||||
|
||||
public async generatePrivacyAgreement(
|
||||
data: GenerateDocumentInputDTO,
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
const document = await this.agreementDomainInteractor.generatePrivacyAgreement(data, options);
|
||||
return document as GeneratedDocumentDTO;
|
||||
}
|
||||
|
||||
public async generateSignatureAgreement(
|
||||
data: GenerateDocumentInputDTO,
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
const document = await this.agreementDomainInteractor.generateSignatureAgreement(data, options);
|
||||
return document as GeneratedDocumentDTO;
|
||||
}
|
||||
|
||||
public async generateUserAgreement(
|
||||
data: GenerateDocumentInputDTO,
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
const document = await this.agreementDomainInteractor.generateUserAgreement(data, options);
|
||||
return document as GeneratedDocumentDTO;
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,16 @@ import { AppStoreModule } from './appstore/appstore-app.module';
|
||||
import { QueueModule } from './queue/queue-app.module';
|
||||
import { DocumentModule } from './document/document.module';
|
||||
import { RedisAppModule } from './redis/redis-app.module';
|
||||
import { DecisionModule } from './decision/decision.module';
|
||||
import { DecisionModule } from './free-decision/decision.module';
|
||||
import { AgreementModule } from './agreement/agreement.module';
|
||||
import { ParticipantModule } from './participant/participant.module';
|
||||
import { AgendaModule } from './agenda/agenda.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AccountModule,
|
||||
AgreementModule,
|
||||
AgendaModule,
|
||||
AppStoreModule,
|
||||
AuthModule,
|
||||
BranchModule,
|
||||
@@ -25,9 +30,12 @@ import { DecisionModule } from './decision/decision.module';
|
||||
SystemModule,
|
||||
DocumentModule,
|
||||
DecisionModule,
|
||||
ParticipantModule,
|
||||
],
|
||||
exports: [
|
||||
AccountModule,
|
||||
AgendaModule,
|
||||
AgreementModule,
|
||||
AppStoreModule,
|
||||
AuthModule,
|
||||
BranchModule,
|
||||
@@ -38,6 +46,7 @@ import { DecisionModule } from './decision/decision.module';
|
||||
SystemModule,
|
||||
DocumentModule,
|
||||
DecisionModule,
|
||||
ParticipantModule,
|
||||
],
|
||||
})
|
||||
export class ApplicationModule {}
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { JwtAuthStrategy } from './strategies/jwt.strategy';
|
||||
import { DomainModule } from '~/domain/domain.module';
|
||||
import { AuthResolver } from './resolvers/auth.resolver';
|
||||
import { AuthService } from './services/auth.service';
|
||||
|
||||
@Module({
|
||||
imports: [PassportModule],
|
||||
providers: [JwtAuthStrategy],
|
||||
imports: [PassportModule, DomainModule],
|
||||
providers: [JwtAuthStrategy, AuthResolver, AuthService],
|
||||
exports: [PassportModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
@InputType('LoginInput')
|
||||
export class LoginInputDTO {
|
||||
@Field({ description: 'Электронная почта' })
|
||||
@IsNotEmpty({ message: 'Поле "email" обязательно для заполнения.' })
|
||||
email!: string;
|
||||
|
||||
@Field({ description: 'Метка времени в строковом формате ISO' })
|
||||
@IsNotEmpty({ message: 'Поле "now" обязательно для заполнения.' })
|
||||
now!: string;
|
||||
|
||||
@Field({ description: 'Цифровая подпись метки времени' })
|
||||
@IsNotEmpty({ message: 'Поле "signature" обязательно для заполнения.' })
|
||||
signature!: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
@InputType('LogoutInput')
|
||||
export class LogoutInputDTO {
|
||||
@Field({ description: 'Токен обновления' })
|
||||
@IsNotEmpty({ message: 'Поле "access_token" обязательно для заполнения.' })
|
||||
access_token!: string;
|
||||
|
||||
@Field({ description: 'Токен доступа' })
|
||||
@IsNotEmpty({ message: 'Поле "refresh_token" обязательно для заполнения.' })
|
||||
refresh_token!: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
@InputType('RefreshInput')
|
||||
export class RefreshInputDTO {
|
||||
@Field({ description: 'Токен обновления' })
|
||||
@IsNotEmpty({ message: 'Поле "refresh_token" обязательно для заполнения.' })
|
||||
refresh_token!: string;
|
||||
|
||||
@Field({ description: 'Токен доступа' })
|
||||
@IsNotEmpty({ message: 'Поле "access_token" обязательно для заполнения.' })
|
||||
access_token!: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
@InputType('ResetKeyInput')
|
||||
export class ResetKeyInputDTO {
|
||||
@Field({ description: 'Публичный ключ для замены' })
|
||||
@IsNotEmpty({ message: 'Поле "public_key" обязательно для заполнения.' })
|
||||
public_key!: string;
|
||||
|
||||
@Field({ description: 'Токен авторизации для замены ключа, полученный по email' })
|
||||
@IsNotEmpty({ message: 'Поле "token" обязательно для заполнения.' })
|
||||
token!: string;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user