This commit is contained in:
Alex Ant
2024-09-03 20:11:17 +05:00
parent 29e972dbf2
commit 0688467302
24 changed files with 279 additions and 262 deletions
+3
View File
@@ -0,0 +1,3 @@
node_modules
dist
+10
View File
@@ -0,0 +1,10 @@
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install -g pnpm lerna
RUN pnpm install
RUN lerna run build
@@ -95,7 +95,7 @@ async function getCooperative(coopname) {
}
async function registerBlockchainAccount(user: IUser, orderData: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
const eos = await getInstance(process.env.SERVICE_WIF);
const newaccount: RegistratorContract.Actions.CreateAccount.ICreateAccount = {
registrator: process.env.COOPNAME as string,
@@ -209,11 +209,11 @@ async function createBoard(data: SovietContract.Actions.Boards.CreateBoard.ICrea
}
async function createOrder(data) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
const eos = await getInstance(process.env.SERVICE_WIF);
const actions = [
{
account: process.env.GATEWAY_CONTRACT as string,
account: GatewayContract.contractName.production,
name: 'deposit',
authorization: [
{
@@ -241,7 +241,7 @@ async function createOrder(data) {
}
async function completeOrder(data: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
const eos = await getInstance(process.env.SERVICE_WIF);
const actions = [
{
@@ -269,11 +269,11 @@ async function completeOrder(data: GatewayContract.Actions.CompleteDeposit.IComp
}
async function failOrder(data) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
const eos = await getInstance(process.env.SERVICE_WIF);
const actions = [
{
account: process.env.GATEWAY_CONTRACT as string,
account: GatewayContract.contractName.production,
name: 'dpfail',
authorization: [
{
@@ -355,7 +355,7 @@ export async function changeKey(data: RegistratorContract.Actions.ChangeKey.ICha
async function getSoviet(coopname) {
const api = await getApi();
const soviet = (await lazyFetch(api, process.env.SOVIET_CONTRACT, coopname, 'boards'))[0];
const soviet = (await lazyFetch(api, SovietContract.contractName.production, coopname, 'boards'))[0];
return soviet;
}
@@ -363,7 +363,7 @@ async function getSoviet(coopname) {
async function fetchAllParticipants() {
const api = await getApi();
const participants = await lazyFetch(api, process.env.SOVIET_CONTRACT, process.env.COOPNAME, 'participants');
const participants = await lazyFetch(api, SovietContract.contractName.production, process.env.COOPNAME, 'participants');
return participants;
}
@@ -11,7 +11,7 @@ export const loadAgenda = async (coopname: string): Promise<Cooperative.Document
const decisions = (await blockchainService.lazyFetch(
api,
process.env.SOVIET_CONTRACT as string,
SovietContract.contractName.production as string,
coopname,
'decisions'
)) as SovietContract.Tables.Decisions.IDecision[];
@@ -22,7 +22,7 @@ export const loadAgenda = async (coopname: string): Promise<Cooperative.Document
const action = (
await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
filter: JSON.stringify({
account: process.env.SOVIET_CONTRACT,
account: SovietContract.contractName.production,
name: SovietContract.Actions.Registry.NewSubmitted.actionName,
receiver: process.env.COOPNAME,
'data.decision_id': String(table.id),
@@ -40,7 +40,7 @@ export const loadAgenda = async (coopname: string): Promise<Cooperative.Document
export const loadStaff = async (coopname) => {
const api = await blockchainService.getApi();
const staff = await blockchainService.lazyFetch(api, process.env.SOVIET_CONTRACT, coopname, 'staff');
const staff = await blockchainService.lazyFetch(api, SovietContract.contractName.production, coopname, 'staff');
for (const staf of staff) {
const user = await userService.getUserByUsername(staf.username);
@@ -51,7 +51,7 @@ export async function buildComplexDocument(
const decision_action = (
await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
filter: JSON.stringify({
account: process.env.SOVIET_CONTRACT,
account: SovietContract.contractName.production,
name: SovietContract.Actions.Registry.NewDecision.actionName,
receiver: process.env.COOPNAME,
'data.decision_id': String(raw_document.decision_id),
@@ -101,7 +101,7 @@ export const queryDocuments = async (
`${process.env.SIMPLE_EXPLORER_API}/get-actions`,
{
filter: JSON.stringify({
account: process.env.SOVIET_CONTRACT,
account: SovietContract.contractName.production,
name: SovietContract.Actions.Registry.NewResolved.actionName,
receiver: process.env.COOPNAME,
...filter,
@@ -42,7 +42,7 @@ export class JoinCoopTemplateFactory extends DocFactory {
signature: string
}
const data_service = new DataService<SignatureData>(this.storage, 'SignatureData')
const data_service = new DataService<SignatureData>(this.storage, 'signatures')
// пропуск сохранения необходим при вступлении для того, чтобы подготовить документ только для отображения без сохранения
if (!options.skip_save) {
@@ -46,7 +46,7 @@ export class Cooperative {
constructor(storage: MongoDBConnector) {
this.db = storage
this.cooperative = null
this.data_service = new DataService<CooperativeData>(storage, 'CooperativeData')
this.data_service = new DataService<CooperativeData>(storage, 'cooperatives')
}
async getOne(username: string, block_num?: number): Promise<CooperativeData> {
@@ -24,7 +24,7 @@ export class Entrepreneur {
constructor(storage: MongoDBConnector, data?: ExternalEntrepreneurData) {
this.db = storage
this.entrepreneur = data
this.data_service = new DataService<InternalEntrepreneurData>(storage, 'EntrepreneurData')
this.data_service = new DataService<InternalEntrepreneurData>(storage, 'entrepreneurs')
}
validate(): ValidateResult {
@@ -16,7 +16,7 @@ export class Individual {
constructor(storage: MongoDBConnector, data?: ExternalIndividualData) {
this.individual = data
this.data_service = new DataService<InternalIndividualData>(storage, 'IndividualData')
this.data_service = new DataService<InternalIndividualData>(storage, 'individuals')
}
validate(): ValidateResult {
@@ -24,7 +24,7 @@ export class Organization {
constructor(storage: MongoDBConnector, data?: ExternalOrganizationData) {
this.db = storage
this.organization = data
this.data_service = new DataService<InternalOrganizationData>(storage, 'OrgData')
this.data_service = new DataService<InternalOrganizationData>(storage, 'organizations')
}
validate(): ValidateResult {
@@ -18,7 +18,7 @@ export class PaymentMethod {
constructor(storage: MongoDBConnector, data?: PaymentData) {
this.db = storage
this.paymentMethod = data
this.data_service = new DataService(storage, 'PaymentData')
this.data_service = new DataService(storage, 'paymentMethods')
}
validate(): ValidateResult {
+70 -90
View File
@@ -9,111 +9,91 @@ import {
} from 'vue-router';
import { routes } from 'src/app/providers/routes';
import { useSessionStore } from 'src/entities/Session';
import {
COOPNAME,
} from 'src/shared/config';
import { COOPNAME } from 'src/shared/config';
import { IUserAccountData, useCurrentUserStore } from 'src/entities/User';
import { useDesktopStore } from '../../entities/Desktop/model';
import { useCardStore } from './card/store';
export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// Helper function to determine router history mode
function getHistoryMode() {
if (process.env.SERVER) return createMemoryHistory;
return process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory;
}
export default route(async function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: process.env.VUE_ROUTER_MODE === 'history'
? createWebHistory
: createWebHashHistory;
// Helper function to initialize stores
async function initializeStores() {
const desktops = useDesktopStore();
const { initWallet } = useCardStore();
const desktops = useDesktopStore()
await desktops.healthCheck()
const { initWallet } = useCardStore()
await initWallet()
const currentUser = useCurrentUserStore();
const session = useSessionStore();
console.log('on load desktops', currentUser)
await desktops.healthCheck();
await desktops.loadDesktops()
await desktops.setActiveDesktop(desktops.defaultDesktopHash)
desktops.currentDesktop?.routes.forEach(route => {
routes[0].children?.push(route as RouteRecordRaw)
})
await initWallet();
return { desktops, initWallet };
}
const router = createRouter({
history: createHistory(process.env.VUE_ROUTER_BASE),
routes,
scrollBehavior(
to: RouteLocationNormalizedGeneric,
from: RouteLocationNormalizedGeneric,
savedPosition: any
) {
if (savedPosition) {
return savedPosition;
} else {
return { top: 0 };
}
},
// Function to set up router instance
function setupRouter(desktops) {
desktops.currentDesktop?.routes.forEach(route => {
routes[0].children?.push(route as RouteRecordRaw);
});
return createRouter({
history: getHistoryMode()(process.env.VUE_ROUTER_BASE),
routes,
scrollBehavior(to, from, savedPosition) {
return savedPosition || { top: 0 };
},
});
}
router.beforeEach(
async (
to: RouteLocationNormalizedGeneric,
from: RouteLocationNormalizedGeneric,
next: any
) => {
console.log('on router before')
desktops.healthCheck()
/** проверяем установлено ли приложение и не переход ли это на страницу установки */
if (desktops.health?.status === 'install' && to.name !== 'install') {
/** переадресуем на страницу установки */
next({name: 'install', params: { coopname: COOPNAME }})
return
} else if (to.name == 'index') {
/** Если переходим на Главную, то определяем её на основе рабочего стола и имеющейся авторизации */
if (session.isAuth){
next({ name: desktops.currentDesktop?.authorizedHome, params: { coopname: COOPNAME } });
return
} else {
next({
name: desktops.currentDesktop?.nonAuthorizedHome,
params: { coopname: COOPNAME },
});
return;
}
}
if (hasAccess(to, currentUser.userAccount)) {
/** Если переход на любую другую страницу, то проверяем права доступа к ней на основе роли и продолжаем загрузку */
next(); // Продолжить переход, если доступ разрешен
return
} else {
next({ name: 'permissionDenied' }); // Перенаправить на страницу с сообщением о недостатке прав доступа
return
}
}
);
function hasAccess(
to: RouteLocationNormalizedGeneric,
userAccount: IUserAccountData | null
) {
if (!to.meta?.roles || to.meta?.roles.length === 0) {
return true; // Доступ разрешен, если массив ролей пуст
}
if (userAccount && to.meta?.roles.includes(userAccount.role)) {
return true; // Доступ разрешен, если роль пользователя входит в массив разрешенных ролей
}
return false; // В остальных случаях доступ запрещен
// Function to handle access logic
function hasAccess(to: RouteLocationNormalizedGeneric, userAccount: IUserAccountData | null) {
if (!to.meta?.roles || to.meta?.roles.length === 0) {
return true; // Access is allowed if no roles are defined
}
return userAccount && to.meta?.roles.includes(userAccount.role); // Check user roles against route roles
}
// Function to handle router navigation guards
function setupNavigationGuards(router, desktops, session, currentUser) {
router.beforeEach(async (to, from, next) => {
await desktops.healthCheck();
if (desktops.health?.status === 'install' && to.name !== 'install') {
next({ name: 'install', params: { coopname: COOPNAME } });
return;
}
if (to.name === 'index') {
const homePage = session.isAuth && currentUser.isRegistrationComplete
? desktops.currentDesktop?.authorizedHome
: desktops.currentDesktop?.nonAuthorizedHome;
next({ name: homePage, params: { coopname: COOPNAME } });
return;
}
if (hasAccess(to, currentUser.userAccount)) {
next();
} else {
next({ name: 'permissionDenied' });
}
});
}
// Main route export
export default route(async function () {
const { desktops } = await initializeStores();
const router = setupRouter(desktops);
const currentUser = useCurrentUserStore();
const session = useSessionStore();
setupNavigationGuards(router, desktops, session, currentUser);
return router;
});
@@ -14,6 +14,7 @@ const baseRoutes = [
{
path: '/',
component: layout,
name: 'base',
children: [
{
path: '',
@@ -18,9 +18,9 @@ div.menu-container
q-icon(:name="isDark ? 'brightness_7' : 'brightness_3'").q-pt-xs.btn-icon
span.btn-font {{ isDark ? 'светлая' : 'тёмная' }}
q-btn(flat class="cursor-pointer btn-menu" @click="logout")
q-icon( color="red" name="logout").q-pt-xs.btn-icon
div.btn-font Выход
//- q-btn(flat class="cursor-pointer btn-menu" @click="logout")
//- q-icon( color="red" name="logout").q-pt-xs.btn-icon
//- div.btn-font Выход
</template>
@@ -28,11 +28,11 @@ export const manifest = {
'name': 'UserDesktop',
'hash': 'hash1',
'authorizedHome': 'user-identity',
'nonAuthorizedHome': 'signin',
'nonAuthorizedHome': 'signup',
'routes': [
{
meta: {
title: 'Пайщик',
title: 'Карта',
icon: 'fa-solid fa-id-card',
roles: [],
},
@@ -1,5 +1,4 @@
<template lang="pug">
q-tabs(
v-if="routes"
dense
@@ -12,7 +11,7 @@ q-tabs(
active-bg-color="teal"
active-color="white"
indicator-color="secondary"
)
).second-menu
q-route-tab(
v-for="route in routes"
@@ -26,7 +25,6 @@ q-tabs(
</template>
<script lang="ts" setup>
import { useCurrentUserStore } from 'src/entities/User';
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
@@ -50,3 +48,10 @@ init()
watch(route, () => init())
</script>
<style>
.second-menu .q-tab__label {
font-size: 12px !important;
}
</style>
@@ -1,25 +1,116 @@
import { defineStore } from 'pinia';
import { reactive } from 'vue';
import { IGeneratedAccount } from 'src/shared/lib/types/user';
import { IUserData } from 'src/shared/lib/types/user/IUserData';
import { IUserData, type ICreateUserData } from 'src/shared/lib/types/user/IUserData';
const namespace = 'registrator';
function clearObjectValues(obj: any): any {
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
clearObjectValues(obj[key]); // рекурсивный вызов
} else if (typeof obj[key] === 'string' && obj[key] !== '') {
obj[key] = '';
} else if (typeof obj[key] === 'boolean') {
obj[key] = false;
}
}
return obj;
}
// Начальное состояние для account
const initialAccountState: IGeneratedAccount = {
username: '',
private_key: '',
public_key: ''
};
// Начальное состояние для userData
const initialUserDataState: ICreateUserData = {
type: 'individual',
individual_data: {
first_name: '',
last_name: '',
middle_name: '',
birthdate: '',
full_address: '',
phone: '',
email: '',
},
organization_data: {
type: 'coop',
is_cooperative: false,
short_name: '',
full_name: '',
represented_by: {
first_name: '',
last_name: '',
middle_name: '',
position: '',
based_on: '',
},
country: 'Russia',
city: '',
full_address: '',
phone: '',
email: '',
details: {
inn: '',
ogrn: '',
},
bank_account: {
currency: 'RUB',
card_number: undefined,
bank_name: '',
account_number: '',
details: {
bik: '',
corr: '',
kpp: '',
},
},
},
entrepreneur_data: {
first_name: '',
last_name: '',
middle_name: '',
birthdate: '',
phone: '',
email: '',
country: 'Russia',
city: '',
full_address: '',
details: {
inn: '',
ogrn: '',
},
bank_account: {
currency: 'RUB',
card_number: undefined,
bank_name: '',
account_number: '',
details: {
bik: '',
corr: '',
kpp: '',
},
},
},
};
// Начальное состояние для statement
const initialStatementState = {
hash: '',
meta: {},
public_key: '',
signature: '',
};
// Начальное состояние для payment
const initialPaymentState = {
provider: 'yookassa',
details: {
token: '',
url: '',
},
};
// Начальное состояние для agreements
const initialAgreementsState = {
condidential: false,
digital_signature: false,
wallet: false,
ustav: false,
user: false,
self_paid: false,
};
export const useRegistratorStore = defineStore(
namespace,
@@ -28,104 +119,17 @@ export const useRegistratorStore = defineStore(
step: 1,
role: 'user',
email: '',
account: { username: '', private_key: '', public_key: '' } as IGeneratedAccount,
userData: {
type: 'individual',
individual_data: {
first_name: '',
last_name: '',
middle_name: '',
birthdate: '',
full_address: '',
phone: '',
email: '',
},
organization_data: {
type: 'coop', // или другой тип, если нужно
is_cooperative: false,
short_name: '',
full_name: '',
represented_by: {
first_name: '',
last_name: '',
middle_name: '',
position: '',
based_on: '',
},
country: 'Russia', // или 'Other'
city: '',
full_address: '',
phone: '',
email: '',
details: {
inn: '',
ogrn: '',
},
bank_account: {
currency: 'RUB', // или 'Other'
card_number: undefined,
bank_name: '',
account_number: '',
details: {
bik: '',
corr: '',
kpp: '',
},
},
},
entrepreneur_data: {
first_name: '',
last_name: '',
middle_name: '',
birthdate: '',
phone: '',
email: '',
country: 'Russia', // или 'Other'
city: '',
full_address: '',
details: {
inn: '',
ogrn: '',
},
bank_account: {
currency: 'RUB', // или 'Other'
card_number: undefined,
bank_name: '',
account_number: '',
details: {
bik: '',
corr: '',
kpp: '',
},
},
},
} as IUserData,
account: structuredClone(initialAccountState),
userData: structuredClone(initialUserDataState),
signature: '',
inLoading: false,
agreements: {
condidential: false,
digital_signature: false,
wallet: false,
ustav: false,
user: false,
self_paid: false,
},
statement: {
hash: '',
meta: {},
public_key: '',
signature: '',
},
payment: {
provider: 'yookassa',
details: {
token: '',
url: '',
},
},
agreements: structuredClone(initialAgreementsState),
statement: structuredClone(initialStatementState),
payment: structuredClone(initialPaymentState),
is_paid: false,
});
// Функция для очистки состояния addUserState
const clearAddUserState = () => reactive({
spread_initial: false,
created_at: '',
@@ -133,31 +137,26 @@ export const useRegistratorStore = defineStore(
minimum: 0,
org_initial: 0,
org_minimum: 0
})
});
const addUserState = clearAddUserState()
const addUserState = clearAddUserState();
// Функция для сброса данных пользователя
const clearUserData = () => {
state.step = 1
state.email = ''
state.account = clearObjectValues(state.account);
state.userData = clearObjectValues(state.userData);
state.payment.provider = 'yookassa'
state.is_paid = false
state.statement = {
hash: '',
meta: {},
public_key: '',
signature: '',
}
}
state.step = 1;
state.email = '';
state.account = structuredClone(initialAccountState);
state.userData = structuredClone(initialUserDataState);
state.payment = structuredClone(initialPaymentState);
state.is_paid = false;
state.statement = structuredClone(initialStatementState);
};
return {
state,
addUserState,
clearUserData,
clearAddUserState
clearAddUserState,
};
},
{
@@ -5,12 +5,7 @@ const walletStore = useWalletStore()
<template lang="pug">
div
q-input(readonly standout label="Паевый счёт" v-model="walletStore.wallet.available")
//- span.text-grey Паевый счёт
//- p.text-h3 {{ walletStore.wallet.available }}
q-input(readonly standout label="Членский счёт" v-model="walletStore.wallet.blocked")
q-input(readonly standout label="Паевый счёт" v-model="walletStore.wallet.available" style="font-size: 18px;")
q-input(readonly standout label="Членский счёт" v-model="walletStore.wallet.blocked" style="font-size: 18px;")
</template>
@@ -1,6 +1,11 @@
<template lang="pug">
div
q-btn(@click="showDialog=true" outline) добавить метод
q-badge(flat rounded color="grey").q-ml-sm
q-icon(name="far fa-question")
q-tooltip Используйте, чтобы добавить метод возврата паевого взноса. Каким именно методом вам вернуть паевый взнос вы сможете выбрать при создании заявления на возврат.
q-dialog(v-model="showDialog" @hide="clear")
ModalBase(:title='"Добавить метод платежа"' )
Form(:handler-submit="handlerSubmit" :is-submitting="isSubmitting" :button-cancel-txt="'Отменить'" :button-submit-txt="'Продолжить'" @cancel="clear").q-pa-sm
@@ -2,10 +2,11 @@
div.row.justify-center.q-mt-lg
div.col-md-6.col-xs-12
LogoutCard
ExitCard.q-mt-lg
//- ExitCard.q-mt-lg
</template>
<script lang="ts" setup>
import { LogoutCard } from 'src/widgets/User/LogoutCard';
import { ExitCard } from 'src/widgets/Cooperative/Participants/ExitCard';
// import { ExitCard } from 'src/widgets/Cooperative/Participants/ExitCard';
</script>
@@ -10,3 +10,17 @@ export interface IUserData {
individual_data?: IIndividualData;
organization_data?: IOrganizationData;
}
type OmitUsername<T> = Omit<T, 'username'>;
export type ICreateEntrepreneurData = OmitUsername<Cooperative.Users.IEntrepreneurData>;
export type ICreateIndividualData = OmitUsername<Cooperative.Users.IIndividualData>;
export type ICreateOrganizationData = OmitUsername<Cooperative.Users.IOrganizationData>;
export interface ICreateUserData {
type: 'individual' | 'entrepreneur' | 'organization';
entrepreneur_data?: ICreateEntrepreneurData;
individual_data?: ICreateIndividualData;
organization_data?: ICreateOrganizationData;
}
@@ -22,10 +22,11 @@ div
Welcome(v-model:step='store.step')
q-btn(@click="out" dense size="sm" flat) начать с начала
</template>
<script lang="ts" setup>
import { watch, onMounted, onBeforeUnmount } from 'vue'
import { watch, onMounted, onBeforeUnmount, computed } from 'vue'
import EmailInput from './EmailInput.vue'
import GenerateAccount from './GenerateAccount.vue'
import SetUserData from './SetUserData.vue'
@@ -41,10 +42,11 @@ const currentUser = useCurrentUserStore()
import { useRegistratorStore } from 'src/entities/Registrator'
import { useLogoutUser } from 'src/features/Registrator/Logout'
import { useSessionStore } from 'src/entities/Session'
const { state, clearUserData } = useRegistratorStore()
const session = useSessionStore()
const store = state
const username = computed(() => session.username)
onMounted(() => {
if (!currentUser.isRegistrationComplete) {
@@ -87,7 +89,7 @@ watch(
() => [store.step, store.email, store.account, store.userData],
() => {
if (store.step >= 4) {
currentUser.loadProfile(store.account.username, COOPNAME)
currentUser.loadProfile(username.value, COOPNAME)
}
}
)
@@ -7,7 +7,7 @@ q-card(flat bordered).q-pa-md
div(v-if="method.method_type ==='sbp' && isSBPData(method.data)")
div.flex.justify-between
q-badge
q-badge(color="accent")
span {{ method.method_id }}
span(v-if="method.method_type ==='sbp'").q-pl-xs СБП
DeletePaymentButton(:size="'xs'" :username="username" :method_id="method.method_id")
@@ -15,7 +15,7 @@ q-card(flat bordered).q-pa-md
div(v-if="method.method_type ==='bank_transfer' && isBankTransferData(method.data)")
div.flex.justify-between
q-badge
q-badge(color="accent")
span {{ method.method_id }}
span(v-if="method.method_type ==='bank_transfer'").q-pl-xs Банковский перевод
DeletePaymentButton(:size="'xs'" :username="username" :method_id="method.method_id")
@@ -1,13 +1,15 @@
<template lang="pug">
q-card(flat bordered).q-pa-md
div.row
div.col-md-6.col-xs-12.q-gutter-sm
div.flex.q-pa-sm
DepositButton.q-ma-sm
WithdrawButton.q-ma-sm
div.row.justify-center
div.col-md-6.col-xs-12.q-gutter-sm
div.row.q-pa-sm.justify-center
div.flex
DepositButton.q-ma-sm
WithdrawButton.q-ma-sm
WalletBalance
WalletProgramsList
WalletProgramsList
</template>