@coopenomics/desktop 2025.4.29 → 2025.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/alias-resolver.js +35 -0
  2. package/package.json +13 -7
  3. package/quasar.config.cjs +24 -4
  4. package/src/app/providers/router.ts +4 -3
  5. package/src/boot/sentry.ts +14 -13
  6. package/src/entities/Session/model/store.ts +3 -3
  7. package/src/env.d.ts +2 -0
  8. package/src/features/Request/CreateParentOffer/ui/CreateParentOfferForm/CreateParentOfferForm.vue +2 -1
  9. package/src/features/Request/CreateParentOffer/ui/ImageUploaderWithPreview/ImageUploaderWithPreview.vue +2 -1
  10. package/src/features/Union/AddCooperative/model/index.ts +5 -4
  11. package/src/features/Union/AddCooperative/ui/AddCooperativeForm.vue +2 -4
  12. package/src/features/User/LoginUser/model/index.ts +0 -4
  13. package/src/features/User/Logout/model/index.ts +0 -1
  14. package/src/features/Wallet/DepositToWallet/ui/DepositButton/DepositButton.vue +4 -2
  15. package/src/features/Wallet/WithdrawFromWallet/ui/withdrawButton.vue +4 -2
  16. package/src/pages/Cooperative/ChangeRegisterPayments/ui/ChangeRegisterPayments.vue +7 -5
  17. package/src/pages/ExtensionStore/ExtensionPage/ExtensionPage.vue +22 -8
  18. package/src/pages/Registrator/SignUp/EmailInput.vue +3 -1
  19. package/src/pages/Registrator/SignUp/Welcome.vue +3 -1
  20. package/src/shared/api/axios.ts +5 -4
  21. package/src/shared/api/client.ts +4 -3
  22. package/src/shared/api/eosio.ts +2 -1
  23. package/src/shared/api/indexDB.ts +3 -0
  24. package/src/shared/api/utils.ts +3 -1
  25. package/src/shared/config/Environment.ts +68 -0
  26. package/src/shared/config/index.ts +2 -0
  27. package/src/shared/lib/proxy/dicebear-collection.cjs +21 -0
  28. package/src/shared/lib/proxy/dicebear-core.cjs +20 -0
  29. package/src/shared/lib/proxy/email-regex.cjs +19 -0
  30. package/src/shared/ui/ClientOnly/ClientOnly.vue +15 -0
  31. package/src/shared/ui/ClientOnly/index.ts +1 -0
  32. package/src/shared/ui/ZodForm/ZodForm.vue +2 -2
  33. package/src/widgets/Header/CommonHeader/MainHeader.vue +2 -1
  34. package/src/widgets/Marketplace/SupplyOrderRequestCard/ui/Base/Base.vue +2 -1
  35. package/src-ssr/middlewares/injectEnv.ts +49 -0
@@ -0,0 +1,35 @@
1
+ // Перенаправление импортов для ESM модулей в SSR
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ // Список модулей, которые нужно перенаправить
6
+ const modulesToResolve = {
7
+ '@dicebear/core': path.resolve(__dirname, 'src/shared/lib/proxy/dicebear-core.cjs'),
8
+ '@dicebear/collection': path.resolve(__dirname, 'src/shared/lib/proxy/dicebear-collection.cjs'),
9
+ 'email-regex': path.resolve(__dirname, 'src/shared/lib/proxy/email-regex.cjs')
10
+ };
11
+
12
+ // Проверяем, существуют ли прокси-файлы
13
+ Object.entries(modulesToResolve).forEach(([moduleName, proxyPath]) => {
14
+ if (!fs.existsSync(proxyPath)) {
15
+ console.error(`Прокси-файл для ${moduleName} не найден по пути ${proxyPath}`);
16
+ process.exit(1);
17
+ }
18
+ });
19
+
20
+ // Регистрируем перехватчик для require
21
+ require.extensions['.js'] = function(module, filename) {
22
+ const originalCompile = module._compile;
23
+
24
+ module._compile = function(content, filename) {
25
+ // Заменяем импорты проблемных модулей на наши прокси
26
+ Object.keys(modulesToResolve).forEach(moduleName => {
27
+ const regex = new RegExp(`require\\(['"]${moduleName}['"]\\)`, 'g');
28
+ content = content.replace(regex, `require('${modulesToResolve[moduleName]}')`);
29
+ });
30
+
31
+ return originalCompile.call(this, content, filename);
32
+ };
33
+
34
+ module._compile(fs.readFileSync(filename, 'utf8'), filename);
35
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coopenomics/desktop",
3
- "version": "2025.4.29",
3
+ "version": "2025.5.4",
4
4
  "description": "A Desktop Project",
5
5
  "productName": "Desktop App",
6
6
  "author": "Alex Ant <dacom.dark.sun@gmail.com>",
@@ -16,13 +16,16 @@
16
16
  "test": "echo \"No test specified\" && exit 0",
17
17
  "dev": "concurrently -n 'DESKTOP' -c 'bgRed.white' \"quasar dev\"",
18
18
  "devnet": "quasar dev",
19
+ "ssr": "quasar dev --mode ssr",
19
20
  "build:lib": "vite build",
20
- "build": "quasar build",
21
- "prepublishOnly": "npm run build:lib"
21
+ "build:spa": "quasar build",
22
+ "build": "quasar build --mode ssr",
23
+ "prepublishOnly": "npm run build:lib",
24
+ "start": "node -r ./alias-resolver.js dist/ssr/index.js"
22
25
  },
23
26
  "dependencies": {
24
- "@coopenomics/controller": "2025.4.29",
25
- "@coopenomics/sdk": "2025.4.29",
27
+ "@coopenomics/controller": "2025.5.4",
28
+ "@coopenomics/sdk": "2025.5.3",
26
29
  "@dicebear/collection": "^9.0.1",
27
30
  "@dicebear/core": "^9.0.1",
28
31
  "@fortawesome/fontawesome-svg-core": "^6.5.2",
@@ -46,13 +49,15 @@
46
49
  "@wharfkit/wallet-plugin-privatekey": "^1.1.0",
47
50
  "axios": "^1.2.1",
48
51
  "compression": "^1.7.4",
49
- "cooptypes": "2025.4.29",
52
+ "cooptypes": "2025.5.3",
50
53
  "dompurify": "^3.1.7",
51
54
  "dotenv": "^16.4.5",
52
55
  "email-regex": "^5.0.0",
53
56
  "eosjs": "^22.1.0",
54
57
  "express": "^4.21.0",
58
+ "graphql-ws": "^5.16.0",
55
59
  "idb": "^8.0.0",
60
+ "isomorphic-ws": "^5.0.0",
56
61
  "lodash": "^4.17.21",
57
62
  "moment-with-locales-es6": "^1.0.1",
58
63
  "pinia": "^2.0.11",
@@ -60,6 +65,7 @@
60
65
  "pug": "^3.0.3",
61
66
  "qrcode": "^1.5.4",
62
67
  "quasar": "^2.16.0",
68
+ "serialize-javascript": "^6.0.2",
63
69
  "swiper": "^11.1.7",
64
70
  "vite": "2.9.16",
65
71
  "vue": "^3.4.18",
@@ -95,5 +101,5 @@
95
101
  "npm": ">= 6.13.4",
96
102
  "yarn": ">= 1.21.1"
97
103
  },
98
- "gitHead": "397670473ae57d53b0b48cd1c3bc6e2d531015fa"
104
+ "gitHead": "8d7c91933210969a574f111dd24e8270142e6717"
99
105
  }
package/quasar.config.cjs CHANGED
@@ -7,11 +7,22 @@
7
7
  // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
8
8
 
9
9
  // const config = require('./src/shared/config/Env.ts');
10
- require('dotenv').config();
10
+
11
11
  const { configure } = require('quasar/wrappers');
12
12
  const path = require('path');
13
13
 
14
- module.exports = configure(function (/* ctx */) {
14
+
15
+ module.exports = configure(function (ctx) {
16
+
17
+ const isSSR = ctx.mode.ssr;
18
+ const isDev = ctx.dev;
19
+
20
+ // Загружаем переменные окружения всегда в режиме разработки
21
+ // или только для клиентской части в продакшн
22
+ const env = (isDev || !isSSR) ? require('dotenv').config().parsed : {
23
+ CLIENT: process.env.CLIENT,
24
+ SERVER: process.env.SERVER,
25
+ };
15
26
 
16
27
  return {
17
28
  htmlVariables: {
@@ -72,7 +83,8 @@ module.exports = configure(function (/* ctx */) {
72
83
 
73
84
  // publicPath: '/',
74
85
  // analyze: true,
75
- env: require('dotenv').config().parsed,
86
+ // env: require('dotenv').config().parsed,
87
+ env,
76
88
  // rawDefine: {}
77
89
  // ignorePublicFolder: true,
78
90
  // minify: false,
@@ -111,6 +123,10 @@ module.exports = configure(function (/* ctx */) {
111
123
  { server: false },
112
124
  ],
113
125
  ],
126
+
127
+ optimizeDeps: {
128
+ include: ['@dicebear/core', '@dicebear/collection']
129
+ },
114
130
  },
115
131
 
116
132
  // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
@@ -120,7 +136,7 @@ module.exports = configure(function (/* ctx */) {
120
136
  open: false, // opens browser window automatically
121
137
  port: 3005,
122
138
  hmr:{
123
- clientPort: 3005,
139
+ // clientPort: 3005,
124
140
  // overlay: false
125
141
  }
126
142
  },
@@ -183,8 +199,12 @@ module.exports = configure(function (/* ctx */) {
183
199
  // (gets superseded if process.env.PORT is specified at runtime)
184
200
 
185
201
  middlewares: [
202
+ 'injectEnv', // middleware для инъекции process.env в window.__ENV__
186
203
  'render', // keep this as last one
187
204
  ],
205
+
206
+ // Не обрабатывать эти модули для SSR
207
+ noExternal: ['@dicebear/core', '@dicebear/collection']
188
208
  },
189
209
 
190
210
  // https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
@@ -7,17 +7,18 @@ import {
7
7
  createWebHistory,
8
8
  } from 'vue-router';
9
9
  import { routes } from 'src/app/providers/routes';
10
+ import { env } from 'src/shared/config';
10
11
 
11
12
  // Helper function to determine router history mode
12
13
  function getHistoryMode() {
13
- if (process.env.SERVER) return createMemoryHistory;
14
- return process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory;
14
+ if (env.SERVER) return createMemoryHistory;
15
+ return env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory;
15
16
  }
16
17
 
17
18
  // Main route export
18
19
  export default route(function () {
19
20
  const Router = createRouter({
20
- history: getHistoryMode()(process.env.VUE_ROUTER_BASE),
21
+ history: getHistoryMode()(env.VUE_ROUTER_BASE),
21
22
  routes, // Базовые маршруты
22
23
  scrollBehavior(to, from, savedPosition) {
23
24
  return savedPosition || { top: 0 };
@@ -1,25 +1,26 @@
1
1
  import { boot } from 'quasar/wrappers';
2
2
  import { App } from 'vue';
3
3
  import { Router } from 'vue-router';
4
- import Tracker from '@openreplay/tracker';
5
- import trackerAssist from '@openreplay/tracker-assist'
6
- import { useSessionStore } from 'src/entities/Session';
4
+ import { env } from 'src/shared/config';
5
+ // import Tracker from '@openreplay/tracker';
6
+ // import trackerAssist from '@openreplay/tracker-assist'
7
+ // import { useSessionStore } from 'src/entities/Session';
7
8
  export default boot(
8
9
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
10
  ({ app, router }: { app: App<Element>; router: Router }) => {
10
- if ((process.env.NODE_ENV as string) === 'production') {
11
- const session = useSessionStore()
11
+ if ((env.NODE_ENV as string) === 'production') {
12
+ // const session = useSessionStore()
12
13
 
13
- const tracker = new Tracker({
14
- projectKey: 'mgaCVSShnDNbPRFDZehd',
15
- });
14
+ // const tracker = new Tracker({
15
+ // projectKey: 'mgaCVSShnDNbPRFDZehd',
16
+ // });
16
17
 
17
- if (session.username)
18
- tracker.setUserID(session.username)
18
+ // if (session.username)
19
+ // tracker.setUserID(session.username)
19
20
 
20
- // .start() returns a promise
21
- tracker.start().then(sessionData => console.log(sessionData) ).catch(e => console.error('on tracker: ', e) )
22
- tracker.use(trackerAssist({}));
21
+ // // .start() returns a promise
22
+ // tracker.start().then(sessionData => console.log(sessionData) ).catch(e => console.error('on tracker: ', e) )
23
+ // tracker.use(trackerAssist({}));
23
24
  }
24
25
  }
25
26
  );
@@ -6,7 +6,7 @@ import { WalletPluginPrivateKey } from '@wharfkit/wallet-plugin-privatekey';
6
6
  import { FailAlert, readBlockchain } from 'src/shared/api';
7
7
  import { PrivateKey, Serializer } from '@wharfkit/antelope';
8
8
  import { GetInfoResult } from 'eosjs/dist/eosjs-rpc-interfaces';
9
-
9
+ import { env } from 'src/shared/config';
10
10
  interface ISessionStore {
11
11
  isAuth: Ref<boolean>;
12
12
  username: ComputedRef<string>;
@@ -67,8 +67,8 @@ export const useSessionStore = defineStore('session', (): ISessionStore => {
67
67
  actor: globalStore.username,
68
68
  permission: 'active',
69
69
  chain: {
70
- id: process.env.CHAIN_ID as string,
71
- url: process.env.CHAIN_URL as string,
70
+ id: env.CHAIN_ID as string,
71
+ url: env.CHAIN_URL as string,
72
72
  },
73
73
  walletPlugin: new WalletPluginPrivateKey(
74
74
  globalStore.wif as PrivateKey
package/src/env.d.ts CHANGED
@@ -6,6 +6,7 @@ declare namespace NodeJS {
6
6
  VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
7
7
  VUE_ROUTER_BASE: string | undefined;
8
8
  }
9
+
9
10
  }
10
11
 
11
12
  declare global {
@@ -13,6 +14,7 @@ declare global {
13
14
  YooMoneyCheckoutWidget: any;
14
15
  chatwootSDK: any;
15
16
  }
17
+
16
18
  }
17
19
 
18
20
  import 'vue-router';
@@ -6,6 +6,7 @@ import { computed, ref } from 'vue'
6
6
  import { Form } from 'src/shared/ui/Form'
7
7
  import { useCooperativeStore } from 'src/entities/Cooperative'
8
8
  import { ImageUploaderWithPreview } from '../ImageUploaderWithPreview'
9
+ import { env } from 'src/shared/config'
9
10
 
10
11
  const props = defineProps({
11
12
  username: {
@@ -48,7 +49,7 @@ const handlerSubmit = async () => {
48
49
  coopname: props.coopname,
49
50
  program_id: formData.value.program_id,
50
51
  units: formData.value.units,
51
- unit_cost: parseFloat(unit_cost.toString()).toFixed(4) + ' ' + process.env.CURRENCY,
52
+ unit_cost: parseFloat(unit_cost.toString()).toFixed(4) + ' ' + env.CURRENCY,
52
53
  product_lifecycle_secs: 86400 * formData.value.product_lifecycle_days,
53
54
  data: {
54
55
  title: formData.value.title,
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import { ref, computed } from 'vue'
3
3
  import { ImagesPreview } from '../ImagesPreview'
4
+ import { env } from 'src/shared/config'
4
5
 
5
6
  const emit = defineEmits(['updateImages'])
6
7
 
@@ -38,7 +39,7 @@ const triggerUploader = () => {
38
39
  uploaderRef.value.pickFiles()
39
40
  }
40
41
 
41
- const upload_url = computed(() => process.env.UPLOAD_URL)
42
+ const upload_url = computed(() => env.UPLOAD_URL)
42
43
  </script>
43
44
 
44
45
  <template lang="pug">
@@ -2,6 +2,7 @@ import { TransactResult } from '@wharfkit/session';
2
2
  import { RegistratorContract } from 'cooptypes';
3
3
  import { useSessionStore } from 'src/entities/Session';
4
4
  import { useGlobalStore } from 'src/shared/store';
5
+ import { env } from 'src/shared/config';
5
6
 
6
7
  export function useAddCooperative() {
7
8
  async function addCooperative(
@@ -9,10 +10,10 @@ export function useAddCooperative() {
9
10
  ): Promise<TransactResult | undefined> {
10
11
  const session = useSessionStore();
11
12
 
12
- data.params.initial = `${parseFloat(data.params.initial).toFixed(4)} ${process.env.CURRENCY}`
13
- data.params.minimum = `${parseFloat(data.params.minimum).toFixed(4)} ${process.env.CURRENCY}`
14
- data.params.org_initial = `${parseFloat(data.params.org_initial).toFixed(4)} ${process.env.CURRENCY}`
15
- data.params.org_minimum = `${parseFloat(data.params.org_minimum).toFixed(4)} ${process.env.CURRENCY}`
13
+ data.params.initial = `${parseFloat(data.params.initial).toFixed(4)} ${env.CURRENCY}`
14
+ data.params.minimum = `${parseFloat(data.params.minimum).toFixed(4)} ${env.CURRENCY}`
15
+ data.params.org_initial = `${parseFloat(data.params.org_initial).toFixed(4)} ${env.CURRENCY}`
16
+ data.params.org_minimum = `${parseFloat(data.params.org_minimum).toFixed(4)} ${env.CURRENCY}`
16
17
 
17
18
  const result = await useGlobalStore().transact({
18
19
  account: RegistratorContract.contractName.production,
@@ -29,11 +29,12 @@ import { useSessionStore } from 'src/entities/Session';
29
29
  import { RegistratorContract } from 'cooptypes';
30
30
  import { FailAlert, SuccessAlert } from 'src/shared/api';
31
31
  import type { IObjectedDocument } from 'src/shared/lib/types/document';
32
+ import { env } from 'src/shared/config';
32
33
 
33
34
  const emit = defineEmits(['finish'])
34
35
 
35
36
  const {addCooperative} = useAddCooperative()
36
- const currency = computed(() => process.env.CURRENCY)
37
+ const currency = computed(() => env.CURRENCY)
37
38
  const isSubmitting = ref(false)
38
39
 
39
40
  const props = defineProps({
@@ -82,9 +83,6 @@ const addNow = async () => {
82
83
  } catch(e: any){
83
84
  FailAlert(e.message)
84
85
  }
85
-
86
-
87
-
88
86
  }
89
87
 
90
88
  </script>
@@ -14,7 +14,6 @@ export function useLoginUser() {
14
14
  async function login(email: string, wif: string): Promise<void> {
15
15
  const auth = await api.loginUser(email, wif);
16
16
  const { tokens, account } = await client.login(email, wif);
17
-
18
17
  // Создаём объект tokens с правильными типами
19
18
  const adaptedTokens: ITokens = {
20
19
  access: {
@@ -36,7 +35,6 @@ export function useLoginUser() {
36
35
 
37
36
  const { run } = useInitWalletProcess()
38
37
  await run() //запускаем фоновое обновление кошелька - заменить на подписку потом
39
-
40
38
  if (!currentUser.isRegistrationComplete){
41
39
  const {state, steps} = useRegistratorStore()
42
40
  state.userData.type = currentUser.userAccount?.type as 'individual' | 'entrepreneur' | 'organization'
@@ -48,7 +46,6 @@ export function useLoginUser() {
48
46
  organization: state.userData.organization_data,
49
47
  entrepreneur: state.userData.entrepreneur_data
50
48
  };
51
-
52
49
  const data = dataMap[state.userData.type];
53
50
  if (data) {
54
51
  for (const key of Object.keys(privateData)) {
@@ -74,7 +71,6 @@ export function useLoginUser() {
74
71
  else if (currentUser.userAccount?.status === 'registered')
75
72
  state.step = steps.Welcome
76
73
  }
77
-
78
74
  }
79
75
 
80
76
  return {
@@ -8,7 +8,6 @@ export function useLogoutUser() {
8
8
  async function logout(): Promise<void> {
9
9
  const global = useGlobalStore()
10
10
  //TODO: "начать с начала" при регистрации бажит на это - да и в целом пора бы маршруты срезать эти
11
- // if (global.tokens?.refresh.token) await api.logoutUser(global.tokens?.refresh.token)
12
11
  await useRegistratorStore().clearUserData()
13
12
 
14
13
  await global.logout()
@@ -32,6 +32,8 @@ import { useSessionStore } from 'src/entities/Session'
32
32
  import type { IPaymentOrder } from 'src/shared/lib/types/payments'
33
33
  import { formatAssetToReadable } from 'src/shared/lib/utils/formatAssetToReadable'
34
34
  import { useSystemStore } from 'src/entities/System/model';
35
+ import { env } from 'src/shared/config';
36
+
35
37
  const { info } = useSystemStore()
36
38
 
37
39
  const { createDeposit, loadUserWallet } = useWalletStore()
@@ -54,7 +56,7 @@ const handlerSubmit = async (): Promise<void> => {
54
56
  isSubmitting.value = true
55
57
  try {
56
58
  paymentOrder.value = (await createDeposit({
57
- quantity: `${parseFloat(quantity.value.toString()).toFixed(4)} ${process.env.CURRENCY}`
59
+ quantity: `${parseFloat(quantity.value.toString()).toFixed(4)} ${env.CURRENCY}`
58
60
  })) as IPaymentOrder
59
61
  isSubmitting.value = false
60
62
  } catch (e: any) {
@@ -64,7 +66,7 @@ const handlerSubmit = async (): Promise<void> => {
64
66
  }
65
67
  }
66
68
 
67
- const currency = computed(() => process.env.CURRENCY)
69
+ const currency = computed(() => env.CURRENCY)
68
70
 
69
71
  const paymentFail = (): void => {
70
72
  clear()
@@ -18,7 +18,9 @@ q-btn(@click="showDialog = true" color="primary")
18
18
  import { ref, computed } from 'vue';
19
19
  import { ModalBase } from 'src/shared/ui/ModalBase';
20
20
  import { Form } from 'src/shared/ui/Form';
21
- const currency = computed(() => process.env.CURRENCY)
21
+ import { env } from 'src/shared/config';
22
+
23
+ const currency = computed(() => env.CURRENCY)
22
24
  const showDialog = ref(false)
23
25
  const quantity = ref(1000)
24
26
  const isSubmitting = ref(false)
@@ -33,7 +35,7 @@ const handlerSubmit = async (): Promise<void> => {
33
35
  isSubmitting.value = true
34
36
  try {
35
37
  // paymentOrder.value = (await createDeposit({
36
- // quantity: `${parseFloat(quantity.value.toString()).toFixed(4)} ${process.env.CURRENCY}`,
38
+ // quantity: `${parseFloat(quantity.value.toString()).toFixed(4)} ${env.CURRENCY}`,
37
39
  // provider: 'yookassa',
38
40
  // })) as IPaymentOrder
39
41
  isSubmitting.value = false
@@ -7,8 +7,10 @@ import { FailAlert, SuccessAlert } from 'src/shared/api';
7
7
  import { formatToAsset } from 'src/shared/lib/utils/formatToAsset';
8
8
  import { ref, watch, computed } from 'vue';
9
9
  import { useSystemStore } from 'src/entities/System/model';
10
+ import { env } from 'src/shared/config';
11
+
10
12
  const { info } = useSystemStore()
11
- const currency = computed(() => process.env.CURRENCY)
13
+ const currency = computed(() => env.CURRENCY)
12
14
  const coop = useCooperativeStore()
13
15
  coop.loadPublicCooperativeData(info.coopname)
14
16
  coop.loadPrivateCooperativeData()
@@ -28,10 +30,10 @@ const save = async () => {
28
30
  await updateCoop({
29
31
  coopname: info.coopname,
30
32
  username: session.username,
31
- initial: formatToAsset(localCoop.value.initial, process.env.CURRENCY as string),
32
- minimum: formatToAsset(localCoop.value.minimum, process.env.CURRENCY as string),
33
- org_initial: formatToAsset(localCoop.value.org_initial, process.env.CURRENCY as string),
34
- org_minimum: formatToAsset(localCoop.value.org_minimum, process.env.CURRENCY as string),
33
+ initial: formatToAsset(localCoop.value.initial, env.CURRENCY as string),
34
+ minimum: formatToAsset(localCoop.value.minimum, env.CURRENCY as string),
35
+ org_initial: formatToAsset(localCoop.value.org_initial, env.CURRENCY as string),
36
+ org_minimum: formatToAsset(localCoop.value.org_minimum, env.CURRENCY as string),
35
37
  announce: coop.publicCooperativeData?.announce,
36
38
  description: coop.publicCooperativeData?.description
37
39
  })
@@ -33,7 +33,7 @@ div(v-if="extension").row
33
33
  span.q-ml-xs удалить
34
34
 
35
35
  div.col-md-9.col-sm-8.col-xs-12.q-pa-md
36
- div(v-if="isMain")
36
+ div(v-if="isMain").info-card
37
37
  div
38
38
  span.text-h1 {{extension.title}}
39
39
  //- q-chip(square dense size="sm" color="green" outline v-if="extension.is_installed && extension.enabled").q-ml-sm установлено
@@ -42,29 +42,43 @@ div(v-if="extension").row
42
42
  div
43
43
  q-chip(outline v-for="tag in extension.tags" v-bind:key="tag" dense size="sm") {{tag}}
44
44
 
45
- vue-markdown(:source="extension.readme").description.q-mt-md
46
- div(v-if="(isSettings || isInstall) && extension.schema")
45
+ ClientOnly
46
+ template(#default)
47
+ vue-markdown(:source="extension.readme").description.q-mt-md
48
+ div(v-if="(isSettings || isInstall) && extension.schema").info-card
47
49
  q-form(ref="myFormRef")
48
50
  div(v-if="isEmpty && !isInstall")
49
51
  div.q-pa-md
50
52
  p.text-h6 Нет настроек
51
53
  span Расширение не предоставило настроек для изменения.
52
- div(v-if="!isEmpty")
53
- vue-markdown(:source="extension.instructions").description.q-mt-md
54
- ZodForm(:schema="extension.schema" v-model="data")
54
+ div(v-if="!isEmpty && !isInstall")
55
+
56
+ //- vue-markdown(:source="extension.instructions").description.q-mt-md
57
+ ClientOnly
58
+ template(#default)
59
+ vue-markdown(v-if="extension.instructions && isInstall" :source="extension.instructions").description.q-mt-md
60
+ div
61
+ p.text-h5 Настройки
62
+ ZodForm(:schema="extension.schema" v-model="data").q-mt-lg
55
63
 
56
64
  </template>
57
65
  <script lang="ts" setup>
58
66
  import { useExtensionStore } from 'src/entities/Extension/model';
59
- import { computed, onMounted, ref, watch } from 'vue';
67
+ import { computed, onMounted, ref, watch, defineAsyncComponent } from 'vue';
60
68
  import { useRoute, useRouter } from 'vue-router';
61
69
  import { ZodForm } from 'src/shared/ui/ZodForm';
62
- import VueMarkdown from 'vue-markdown-render'
70
+ // import VueMarkdown from 'vue-markdown-render'
63
71
  import { extractGraphQLErrorMessages, FailAlert, SuccessAlert } from 'src/shared/api';
64
72
  import { useUpdateExtension } from 'src/features/Extension/UpdateExtension';
65
73
  import { useUninstallExtension } from 'src/features/Extension/UninstallExtension';
66
74
  import { useInstallExtension } from 'src/features/Extension/InstallExtension';
67
75
  import { useDesktopStore } from 'src/entities/Desktop/model';
76
+ import { ClientOnly } from 'src/shared/ui/ClientOnly';
77
+
78
+ // Клиентский компонент для markdown, загружаемый только на клиенте
79
+ const VueMarkdown = defineAsyncComponent(() =>
80
+ import('vue-markdown-render').then(mod => mod.default)
81
+ );
68
82
 
69
83
  const route = useRoute();
70
84
  const router = useRouter();
@@ -28,12 +28,14 @@ import { ref, computed, watch } from 'vue'
28
28
  import { useCreateUser } from 'src/features/User/CreateUser'
29
29
  import { debounce } from 'quasar'
30
30
  import { useRegistratorStore } from 'src/entities/Registrator'
31
+ import { env } from 'src/shared/config'
32
+
31
33
  const store = useRegistratorStore()
32
34
 
33
35
  const api = useCreateUser()
34
36
 
35
37
  watch(() => store.state.email, () => email.value = store.state.email)
36
- const coopTitle = computed(() => process.env.COOP_SHORT_NAME)
38
+ const coopTitle = computed(() => env.COOP_SHORT_NAME)
37
39
  const email = ref(store.state.email)
38
40
 
39
41
  const inLoading = ref(false)
@@ -20,9 +20,11 @@ div
20
20
  import { computed } from 'vue'
21
21
  import { useRouter } from 'vue-router';
22
22
  import { useRegistratorStore } from 'src/entities/Registrator';
23
+ import { env } from 'src/shared/config';
24
+
23
25
  const router = useRouter()
24
26
  const store = useRegistratorStore()
25
- const coopTitle = computed(() => process.env.COOP_SHORT_NAME)
27
+ const coopTitle = computed(() => env.COOP_SHORT_NAME)
26
28
 
27
29
  const next = () => {
28
30
  router.push({ name: 'index' })
@@ -1,5 +1,6 @@
1
1
  import axios from 'axios';
2
2
  import { useGlobalStore } from '../store';
3
+ import { env } from 'src/shared/config';
3
4
 
4
5
  export async function sendGET(
5
6
  url: string,
@@ -13,7 +14,7 @@ export async function sendGET(
13
14
  // if (!tokens || !tokens.access || !tokens.access.token)
14
15
  // throw new Error('Ошибка авторизации: токен доступа не найден');
15
16
 
16
- const response = await axios.get(process.env.BACKEND_URL + url, {
17
+ const response = await axios.get(env.BACKEND_URL + url, {
17
18
  params,
18
19
  headers: {
19
20
  Authorization: `Bearer ${tokens?.access?.token}`,
@@ -21,7 +22,7 @@ export async function sendGET(
21
22
  });
22
23
  return response.data;
23
24
  } else {
24
- const response = await axios.get(process.env.BACKEND_URL + url, {
25
+ const response = await axios.get(env.BACKEND_URL + url, {
25
26
  params,
26
27
  });
27
28
  return response.data;
@@ -49,14 +50,14 @@ export async function sendPOST(
49
50
  // if (!tokens || !tokens.access || !tokens.access.token)
50
51
  // throw new Error('Ошибка авторизации: токен доступа не найден');
51
52
 
52
- const response = await axios.post(process.env.BACKEND_URL + url, data, {
53
+ const response = await axios.post(env.BACKEND_URL + url, data, {
53
54
  headers: {
54
55
  Authorization: `Bearer ${tokens?.access?.token}`,
55
56
  },
56
57
  });
57
58
  return response.data;
58
59
  } else {
59
- const response = await axios.post(process.env.BACKEND_URL + url, data);
60
+ const response = await axios.post(env.BACKEND_URL + url, data);
60
61
  return response.data;
61
62
  }
62
63
  } catch (e: any) {
@@ -1,12 +1,13 @@
1
1
  import { Client } from '@coopenomics/sdk'
2
+ import { env } from 'src/shared/config'
2
3
 
3
4
  // Создаем и экспортируем экземпляр API-клиента
4
5
  export const client = Client.create({
5
- api_url: process.env.BACKEND_URL + '/v1/graphql',
6
+ api_url: env.BACKEND_URL + '/v1/graphql',
6
7
  headers: {
7
8
  'Content-Type': 'application/json',
8
9
  },
9
- chain_url: process.env.CHAIN_URL as string,
10
- chain_id: process.env.CHAIN_ID as string,
10
+ chain_url: env.CHAIN_URL as string,
11
+ chain_id: env.CHAIN_ID as string,
11
12
  })
12
13
 
@@ -3,9 +3,10 @@ import { APIClient, PrivateKey } from '@wharfkit/antelope';
3
3
  import { ContractKit } from '@wharfkit/contract';
4
4
  import { useGlobalStore } from '../store';
5
5
  import { Account } from '@wharfkit/account'
6
+ import { env } from 'src/shared/config';
6
7
 
7
8
  export const readBlockchain = new APIClient({
8
- url: process.env.CHAIN_URL,
9
+ url: env.CHAIN_URL,
9
10
  });
10
11
 
11
12
  export const contractKit = new ContractKit({
@@ -1,6 +1,8 @@
1
1
  import { openDB } from 'idb';
2
+ import { env } from '../config';
2
3
 
3
4
  async function openIndexedDB(dbName: string, storeName: string) {
5
+
4
6
  if (!process.env.CLIENT) {
5
7
  return;
6
8
  }
@@ -21,6 +23,7 @@ export async function getFromIndexedDB(dbName: string, storeName: string, key: s
21
23
  return;
22
24
  }
23
25
 
26
+
24
27
  const db = await openIndexedDB(dbName, storeName);
25
28
  if (db) {
26
29
  return db.get(storeName, key);
@@ -1,7 +1,9 @@
1
+ import { env } from 'src/shared/config';
2
+
1
3
  export function constructImageSrc(name: string): string {
2
4
  if (name.startsWith('https://')) {
3
5
  return name;
4
6
  } else {
5
- return process.env.STORAGE_URL + name;
7
+ return env.STORAGE_URL + name;
6
8
  }
7
9
  }
@@ -0,0 +1,68 @@
1
+ // Типы для переменных окружения
2
+ export interface EnvVars {
3
+ NODE_ENV?: string;
4
+ BACKEND_URL?: string;
5
+ CHAIN_URL?: string;
6
+ CHAIN_ID?: string;
7
+ CURRENCY?: string;
8
+ COOP_SHORT_NAME?: string;
9
+ SITE_DESCRIPTION?: string;
10
+ SITE_IMAGE?: string;
11
+ STORAGE_URL?: string;
12
+ UPLOAD_URL?: string;
13
+ CLIENT?: boolean;
14
+ SERVER?: boolean;
15
+ VUE_ROUTER_MODE?: string;
16
+ VUE_ROUTER_BASE?: string;
17
+ [key: string]: string | boolean | undefined;
18
+ }
19
+
20
+ // Расширяем глобальный Window чтобы TypeScript понимал window.__ENV__
21
+ declare global {
22
+ interface Window {
23
+ __ENV__?: EnvVars;
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Определение переменных окружения для разных сред
29
+ * - На сервере (SSR): берем напрямую из process.env
30
+ * - На клиенте (SSR): используем window.__ENV__, инжектированные с сервера
31
+ * - В SPA режиме: переменные заменяются при сборке
32
+ */
33
+ function getEnv(): EnvVars {
34
+ // SSR сервер или SPA сборка
35
+
36
+ if (typeof process !== 'undefined' && process.env) {
37
+ // Для SSR сервера - берем реальные переменные
38
+ // Для SPA - эти значения заменятся при сборке
39
+ return {
40
+ NODE_ENV: process.env.NODE_ENV,
41
+ BACKEND_URL: process.env.BACKEND_URL,
42
+ CHAIN_URL: process.env.CHAIN_URL,
43
+ CHAIN_ID: process.env.CHAIN_ID,
44
+ CURRENCY: process.env.CURRENCY,
45
+ COOP_SHORT_NAME: process.env.COOP_SHORT_NAME,
46
+ SITE_DESCRIPTION: process.env.SITE_DESCRIPTION,
47
+ SITE_IMAGE: process.env.SITE_IMAGE,
48
+ STORAGE_URL: process.env.STORAGE_URL,
49
+ UPLOAD_URL: process.env.UPLOAD_URL,
50
+ CLIENT: process.env.CLIENT as unknown as boolean,
51
+ SERVER: process.env.SERVER as unknown as boolean,
52
+ VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE,
53
+ VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE
54
+ };
55
+ }
56
+
57
+ // SSR клиент - берем из window.__ENV__
58
+ if (typeof window !== 'undefined' && window.__ENV__) {
59
+ return window.__ENV__;
60
+ }
61
+
62
+ // Запасной вариант, если ничего не сработало
63
+ console.warn('Не удалось получить переменные окружения!');
64
+ return {};
65
+ }
66
+
67
+ // Экспортируем переменные окружения
68
+ export const env: EnvVars = getEnv();
@@ -3,3 +3,5 @@ export * from './TablesList';
3
3
  export * from './LimitsList';
4
4
  export * from './SecondaryIndexesNumbersList';
5
5
  export * from './SecondaryIndexesTypesList';
6
+ export * from './ActionsList';
7
+ export * from './Environment';
@@ -0,0 +1,21 @@
1
+ // Прокси-файл для @dicebear/collection
2
+ let cachedModule = null;
3
+
4
+ async function loadModule() {
5
+ if (!cachedModule) {
6
+ cachedModule = await import('@dicebear/collection');
7
+ }
8
+ return cachedModule;
9
+ }
10
+
11
+ // Экспортируем пустой объект для CommonJS
12
+ // В SSR мы можем проверять наличие функций перед их использованием
13
+ module.exports = {
14
+ loadModule,
15
+ // Добавляем заглушки для основных стилей
16
+ thumbs: {},
17
+ lorelei: {},
18
+ bottts: {},
19
+ avataaars: {},
20
+ // Другие стили можно добавить при необходимости
21
+ };
@@ -0,0 +1,20 @@
1
+ // Прокси-файл для @dicebear/core
2
+ let cachedModule = null;
3
+
4
+ async function loadModule() {
5
+ if (!cachedModule) {
6
+ cachedModule = await import('@dicebear/core');
7
+ }
8
+ return cachedModule;
9
+ }
10
+
11
+ // Экспортируем пустой объект для CommonJS
12
+ // В SSR мы можем проверять наличие функций перед их использованием
13
+ module.exports = {
14
+ loadModule,
15
+ // Добавляем заглушки для основных функций
16
+ createAvatar: async function(...args) {
17
+ const module = await loadModule();
18
+ return module.createAvatar(...args);
19
+ }
20
+ };
@@ -0,0 +1,19 @@
1
+ // CJS прокси для email-regex
2
+ const originalModule = require('email-regex');
3
+
4
+ // Преобразуем default export из ESM в CJS
5
+ module.exports = function emailRegex(options = {}) {
6
+ // Проверяем, является ли оригинальный модуль функцией или объектом с default
7
+ if (typeof originalModule === 'function') {
8
+ return originalModule(options);
9
+ } else if (originalModule && typeof originalModule.default === 'function') {
10
+ return originalModule.default(options);
11
+ } else {
12
+ // Запасная реализация, если что-то пошло не так
13
+ const exact = options && options.exact;
14
+ const pattern = exact
15
+ ? /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
16
+ : /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/g;
17
+ return pattern;
18
+ }
19
+ };
@@ -0,0 +1,15 @@
1
+ <template lang="pug">
2
+ slot(v-if="isMounted")
3
+ template(v-else)
4
+ slot(name="fallback")
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { ref, onMounted } from 'vue'
9
+
10
+ const isMounted = ref(false)
11
+
12
+ onMounted(() => {
13
+ isMounted.value = true
14
+ })
15
+ </script>
@@ -0,0 +1 @@
1
+ export { default as ClientOnly } from './ClientOnly.vue';
@@ -1,12 +1,12 @@
1
1
  <template lang="pug">
2
2
  div
3
- q-card(flat v-for="(property, propertyName) in schema.properties" :key="propertyName")
3
+ div(flat v-for="(property, propertyName) in schema.properties" :key="propertyName")
4
4
  div(v-if="isVisible(property)")
5
5
  component(
6
6
  standout="bg-teal text-white"
7
7
  :is="getComponentType(property)"
8
8
  v-bind="getComponentProps(property, propertyName)"
9
- ).q-mt-md
9
+ ).q-mt-lg
10
10
  // Слот для prepend, если указано
11
11
  template(v-slot:prepend v-if="property.description?.prepend")
12
12
  span {{ property.description.prepend }}
@@ -41,6 +41,7 @@ import { useWindowSize } from 'src/shared/hooks'
41
41
  import { SettingsDropdown } from 'src/widgets/Header/SettingsDropdown'
42
42
  import { BackButton } from 'src/widgets/Header/BackButton'
43
43
  import './HeaderStyles.scss'
44
+ // import { env } from 'src/shared/config'
44
45
 
45
46
  const router = useRouter()
46
47
  const route = useRoute()
@@ -50,7 +51,7 @@ const { isMobile } = useWindowSize()
50
51
  const emit = defineEmits(['toggle-left-drawer'])
51
52
 
52
53
  // Получаем информацию для навигации назад
53
- // const coopTitle = computed(() => process.env.COOP_SHORT_NAME)
54
+ // const coopTitle = computed(() => env.COOP_SHORT_NAME)
54
55
 
55
56
  const isDark = computed(() => $q.dark.isActive)
56
57
  const headerClass = computed(() =>
@@ -11,6 +11,7 @@ import {
11
11
  SeventhStep,
12
12
  EightStep,
13
13
  } from '../Steps'
14
+ import { env } from 'src/shared/config'
14
15
 
15
16
  //TODO изменить на обязательный импорт с типом
16
17
  const props = defineProps<{
@@ -53,7 +54,7 @@ const src = computed(() => {
53
54
  if (preview && preview.startsWith('https://')) {
54
55
  return preview // Превью уже содержит HTTPS, оставляем его как есть
55
56
  } else {
56
- return process.env.STORAGE_URL + preview // Иначе, добавляем config.storageUrl
57
+ return env.STORAGE_URL + preview // Иначе, добавляем config.storageUrl
57
58
  }
58
59
  })
59
60
  </script>
@@ -0,0 +1,49 @@
1
+ import { ssrMiddleware } from 'quasar/wrappers';
2
+ import { EnvVars } from '../../src/shared/config/Environment';
3
+
4
+ /**
5
+ * SSR middleware для инъекции переменных окружения в браузер
6
+ * Создает window.__ENV__ со всеми переменными, которые должны быть доступны на клиенте
7
+ */
8
+ export default ssrMiddleware(({ app }) => {
9
+ // Регистрируем middleware для всех запросов
10
+ app.use((req, res, next) => {
11
+ // Получаем переменные из process.env, которые нужны клиенту
12
+ const envForClient: EnvVars = {
13
+ NODE_ENV: process.env.NODE_ENV,
14
+ BACKEND_URL: process.env.BACKEND_URL,
15
+ CHAIN_URL: process.env.CHAIN_URL,
16
+ CHAIN_ID: process.env.CHAIN_ID,
17
+ CURRENCY: process.env.CURRENCY,
18
+ COOP_SHORT_NAME: process.env.COOP_SHORT_NAME,
19
+ SITE_DESCRIPTION: process.env.SITE_DESCRIPTION,
20
+ SITE_IMAGE: process.env.SITE_IMAGE,
21
+ STORAGE_URL: process.env.STORAGE_URL,
22
+ UPLOAD_URL: process.env.UPLOAD_URL,
23
+ VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE,
24
+ VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE
25
+ };
26
+
27
+ // Создаем скрипт, который добавит переменные в window.__ENV__
28
+ const script = `
29
+ <script>
30
+ window.__ENV__ = ${JSON.stringify(envForClient)};
31
+ console.log('SSR: Переменные окружения загружены');
32
+ </script>
33
+ `;
34
+
35
+ // Оригинальный метод отправки HTML
36
+ const originalSend = res.send;
37
+
38
+ // Переопределяем метод, чтобы вставить наш скрипт перед закрывающим тегом </head>
39
+ res.send = function (html) {
40
+ if (typeof html === 'string') {
41
+ html = html.replace('</head>', `${script}</head>`);
42
+ }
43
+ return originalSend.call(this, html);
44
+ };
45
+
46
+ // Продолжаем обработку запроса
47
+ next();
48
+ });
49
+ });