fix vpn servers

This commit is contained in:
kandrusyak
2022-12-05 19:45:45 +03:00
parent 3e8a0e2233
commit 9c331789b3
8 changed files with 514 additions and 186 deletions

18
package-lock.json generated
View File

@@ -26,6 +26,7 @@
"qrcode.react": "^3.1.0",
"querystring-es3": "^0.2.1",
"react": "^17.0.2",
"react-country-flag": "^3.0.2",
"react-dom": "^17.0.2",
"react-flatpickr": "^3.10.7",
"react-loader-spinner": "^5.2.0",
@@ -6829,6 +6830,17 @@
"node": ">=0.10.0"
}
},
"node_modules/react-country-flag": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-3.0.2.tgz",
"integrity": "sha512-JPaz+q3QD0/nZtHBKj5x3O7r/SgSG9kxbymdaIU0RqlDAcorJIe4KV0DFhWIdKh69q5cPVkIVERcMYGZdvXgAA==",
"engines": {
"node": ">=12"
},
"peerDependencies": {
"react": ">=16"
}
},
"node_modules/react-dom": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
@@ -13837,6 +13849,12 @@
"object-assign": "^4.1.1"
}
},
"react-country-flag": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-3.0.2.tgz",
"integrity": "sha512-JPaz+q3QD0/nZtHBKj5x3O7r/SgSG9kxbymdaIU0RqlDAcorJIe4KV0DFhWIdKh69q5cPVkIVERcMYGZdvXgAA==",
"requires": {}
},
"react-dom": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",

View File

@@ -25,6 +25,7 @@
"qrcode.react": "^3.1.0",
"querystring-es3": "^0.2.1",
"react": "^17.0.2",
"react-country-flag": "^3.0.2",
"react-dom": "^17.0.2",
"react-flatpickr": "^3.10.7",
"react-loader-spinner": "^5.2.0",

View File

@@ -32,6 +32,10 @@ const App = observer(() => {
document.querySelector('html').style.scrollBehavior = '';
}, [location.pathname]); // triggered on route change
if (!store.nodeInfo.networkIdentifier) {
return <LoadingOverlay />;
}
return (
<>
{store.loading && <LoadingOverlay />}

View File

@@ -6,33 +6,45 @@ import { QRCodeSVG } from 'qrcode.react';
import VPNServeIcon from '../../images/vpn_server_icon.png';
import { Filters } from '../../components/Filters';
import { observer } from 'mobx-react-lite';
import ReactCountryFlag from 'react-country-flag';
const filters = {
'View All': '',
Active: 'Active',
Offline: 'Offline',
'View All': 'all',
Active: true,
Offline: false,
};
function VPNServers() {
const VPNServers = observer(() => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [selectedServer, setSelectedServer] = useState(0);
const [currentFilter, setCurrentFilter] = useState('View All');
const servers = useMemo(() => {
if (filters[currentFilter] === 'all') return store.vpnServers;
return store.vpnServers.filter((e) => e.state === filters[currentFilter]);
}, [store.vpnServers, currentFilter]);
const qrContent = useMemo(
() => `
[Interface]
PrivateKey = ${store.vpnPrivateKey}
Address = ${store.vpnServers?.[selectedServer]?.address}
DNS = ${store.vpnServers?.[selectedServer]?.dns}
Address = ${servers?.[selectedServer]?.address}
DNS = ${servers?.[selectedServer]?.dns}
[Peer]
PublicKey = ${store.vpnServers?.[selectedServer]?.publicKey}
PublicKey = ${servers?.[selectedServer]?.serverPublickKey}
AllowedIPs = 0.0.0.0/0
Endpoint = ${store.vpnServers?.[selectedServer]?.endpoint}
Endpoint = ${servers?.[selectedServer]?.endpoint}
PersistentKeepalive = 25
`,
[store.vpnServers, store.vpnPrivateKey]
[store.vpnServers, store.vpnPrivateKey, selectedServer]
);
useEffect(() => {
setSelectedServer(0);
}, [currentFilter]);
const downloadTxtFile = () => {
const element = document.createElement('a');
const file = new Blob([qrContent], { type: 'text/plain' });
@@ -59,7 +71,7 @@ Endpoint = ${store.vpnServers?.[selectedServer]?.endpoint}
<main>
<div className="lg:relative lg:flex">
{/* Content */}
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl">
{/* Page header */}
<div className="sm:flex sm:justify-between sm:items-center mb-5">
{/* Left: Title */}
@@ -79,7 +91,7 @@ Endpoint = ${store.vpnServers?.[selectedServer]?.endpoint}
{/* Credit cards */}
<div className="space-y-2">
{store.vpnServers?.map((e, i) => (
{servers?.map((e, i) => (
<label
className="relative block cursor-pointer text-left w-full"
onClick={() => setSelectedServer(i)}
@@ -96,28 +108,35 @@ Endpoint = ${store.vpnServers?.[selectedServer]?.endpoint}
<div className="grid grid-cols-12 items-center gap-x-2">
{/* Card */}
<div className="col-span-6 order-1 sm:order-none sm:col-span-3 flex items-center space-x-4 lg:sidebar-expanded:col-span-6 xl:sidebar-expanded:col-span-3">
<img
src={VPNServeIcon}
width="36"
height="36"
alt="VPN Country"
<ReactCountryFlag
countryCode={e.country}
svg
style={{ width: '36px', height: 'auto' }}
/>
<div>
<div className="text-sm font-medium text-slate-800">
{e.endpoint}
</div>
<div className="text-xs">{e.publicKey}</div>
<div className="text-xs">{e.serverPublickKey}</div>
</div>
</div>
{/* Card limits */}
<div className="col-span-6 order-1 sm:order-none sm:col-span-4 text-right sm:text-center lg:sidebar-expanded:col-span-6 xl:sidebar-expanded:col-span-6">
<div className="text-sm">4673,00 Mb / 473,00 Mb</div>
<div className="text-sm">
{e.transferSum} / 50.00 GB
</div>
</div>
{/* Card status */}
<div className="col-span-6 order-2 sm:order-none sm:col-span-2 text-right lg:sidebar-expanded:hidden xl:sidebar-expanded:block">
<div className="text-xs inline-flex font-medium bg-emerald-100 text-emerald-600 rounded-full text-center px-2.5 py-1">
Active
</div>
{store.vpnServers?.[selectedServer]?.state ? (
<div className="text-xs inline-flex font-medium bg-emerald-100 text-emerald-600 rounded-full text-center px-2.5 py-1">
Active
</div>
) : (
<div className="text-xs inline-flex font-medium bg-amber-100 text-amber-600 rounded-full text-center px-2.5 py-1">
Offline
</div>
)}
</div>
</div>
</div>
@@ -131,96 +150,111 @@ Endpoint = ${store.vpnServers?.[selectedServer]?.endpoint}
</div>
{/* Sidebar */}
<div>
<div className="lg:sticky lg:top-16 bg-slate-50 lg:overflow-x-hidden lg:overflow-y-auto no-scrollbar lg:shrink-0 border-t lg:border-t-0 lg:border-l border-slate-200 lg:w-[390px] lg:h-[calc(100vh-64px)]">
<div className="py-8 px-4 lg:px-8 h-full">
<div className="max-w-sm mx-auto lg:max-w-none flex flex-col justify-between h-full">
<div>
<div className="text-slate-800 font-semibold text-center mb-6">
Physical Metal Card
</div>
{/* Credit Card */}
<div className="flex justify-center">
<QRCodeSVG
value={qrContent}
size={200}
includeMargin
level="L"
/>
</div>
{/* Details */}
<div className="mt-6">
<div className="text-sm font-semibold text-slate-800 mb-1">
Details
{servers?.length > 0 && (
<div>
<div className="lg:sticky lg:top-16 bg-slate-50 lg:overflow-x-hidden lg:overflow-y-auto no-scrollbar lg:shrink-0 border-t lg:border-t-0 lg:border-l border-slate-200 lg:w-[390px] lg:h-[calc(100vh-64px)]">
<div className="py-8 px-4 lg:px-8 h-full">
<div className="max-w-sm mx-auto lg:max-w-none flex flex-col justify-between h-full">
<div>
<div className="text-slate-800 font-semibold text-center mb-6">
Physical Metal Card
</div>
<ul>
<li className="flex items-center justify-between py-3 border-b border-slate-200">
<div className="text-sm">Server address</div>
<div className="text-sm font-medium text-slate-800 ml-2">
{store.vpnServers?.[selectedServer]?.endpoint}
</div>
</li>
<li className="flex items-center justify-between py-3 border-b border-slate-200">
<div className="text-sm">Status</div>
<div className="flex items-center whitespace-nowrap">
<div className="w-2 h-2 rounded-full bg-emerald-500 mr-2" />
<div className="text-sm font-medium text-slate-800">
Active
{/* Credit Card */}
<div className="flex justify-center">
<QRCodeSVG
value={qrContent}
size={200}
includeMargin
level="L"
/>
</div>
{/* Details */}
<div className="mt-6">
<div className="text-sm font-semibold text-slate-800 mb-1">
Details
</div>
<ul>
<li className="flex items-center justify-between py-3 border-b border-slate-200">
<div className="text-sm">Server address</div>
<div className="text-sm font-medium text-slate-800 ml-2">
{servers?.[selectedServer]?.endpoint}
</div>
</li>
<li className="flex items-center justify-between py-3 border-b border-slate-200">
<div className="text-sm">Status</div>
<div className="flex items-center whitespace-nowrap">
{servers?.[selectedServer]?.state ? (
<>
<div className="w-2 h-2 rounded-full bg-emerald-500 mr-2" />
<div className="text-sm font-medium text-slate-800">
Active
</div>
</>
) : (
<>
<div className="w-2 h-2 rounded-full mr-2 bg-amber-500" />
<div className="text-sm font-medium text-slate-800">
Offline
</div>
</>
)}
</div>
</li>
</ul>
</div>
{/* Payment Limits */}
<div className="mt-6">
<div className="text-sm font-semibold text-slate-800 mb-4">
Transfer
</div>
<div className="pb-4 border-b border-slate-200">
<div className="flex justify-between text-sm mb-2">
<div>Spent This Month</div>
<div className="italic">
{servers?.[selectedServer]?.transferSum}{' '}
<span className="text-slate-400">/</span> 50.00
GB
</div>
</div>
</li>
</ul>
</div>
{/* Payment Limits */}
<div className="mt-6">
<div className="text-sm font-semibold text-slate-800 mb-4">
Transfer
</div>
<div className="pb-4 border-b border-slate-200">
<div className="flex justify-between text-sm mb-2">
<div>Spent This Month</div>
<div className="italic">
4673,00 Mb{' '}
<span className="text-slate-400">/</span> 46730,00
Mb
<div className="relative w-full h-2 bg-slate-300">
<div
className="absolute inset-0 bg-indigo-500"
aria-hidden="true"
style={{
width: `${servers?.[selectedServer]?.trafficUsed}%`,
}}
/>
</div>
</div>
<div className="relative w-full h-2 bg-slate-300">
<div
className="absolute inset-0 bg-indigo-500"
aria-hidden="true"
style={{ width: '10%' }}
/>
</div>
</div>
</div>
</div>
<button
className="btn w-full border-slate-200 hover:border-slate-300 text-slate-600"
onClick={downloadTxtFile}
>
<svg
className="w-4 h-4 fill-current text-slate-400 shrink-0 rotate-180"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
<button
className="btn w-full border-slate-200 hover:border-slate-300 text-slate-600"
onClick={downloadTxtFile}
>
<path d="M8 4c-.3 0-.5.1-.7.3L1.6 10 3 11.4l4-4V16h2V7.4l4 4 1.4-1.4-5.7-5.7C8.5 4.1 8.3 4 8 4ZM1 2h14V0H1v2Z" />
</svg>
<span className="ml-2">Download</span>
</button>
<svg
className="w-4 h-4 fill-current text-slate-400 shrink-0 rotate-180"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M8 4c-.3 0-.5.1-.7.3L1.6 10 3 11.4l4-4V16h2V7.4l4 4 1.4-1.4-5.7-5.7C8.5 4.1 8.3 4 8 4ZM1 2h14V0H1v2Z" />
</svg>
<span className="ml-2">Download</span>
</button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
</main>
</div>
</div>
);
}
});
export default VPNServers;

View File

@@ -3,27 +3,30 @@ import {
makeAutoObservable,
onBecomeObserved,
onBecomeUnobserved,
} from "mobx";
import { cryptography } from "@liskhq/lisk-client";
import { passphrase } from "@liskhq/lisk-client";
import { getNodeInfo } from "../api/node";
import { fetchWrapper } from "../shared/fetchWrapper";
import { labelMap } from "../shared/labelMap";
import { statusMap } from "../shared/statusMap";
import { generateSvgAvatar } from "../images/GenerateOnboardingSvg/GenerateSvg";
import { decryptedData } from "../utils/decryptedData";
} from 'mobx';
import { cryptography } from '@liskhq/lisk-client';
import { passphrase } from '@liskhq/lisk-client';
import { getNodeInfo } from '../api/node';
import { fetchWrapper } from '../shared/fetchWrapper';
import { labelMap } from '../shared/labelMap';
import { statusMap } from '../shared/statusMap';
import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
import { decryptedData } from '../utils/decryptedData';
import {
bytesToMbytes,
encryptAccountData,
encryptSharedData,
formatBytes,
generateTransaction,
hashAccountData,
} from "../utils/Utils";
import sodium from "sodium-universal";
} from '../utils/Utils';
import sodium from 'sodium-universal';
import { countryCodes } from '../utils/countryCodes';
class Store {
_accountData = [];
_passPhrase = "";
_passPhrase = '';
_nodeInfo = {};
@@ -63,18 +66,18 @@ class Store {
() => this.address,
() => this.fetchAccountInfo()
);
onBecomeObserved(this, "decryptedAccountData", () =>
onBecomeObserved(this, 'decryptedAccountData', () =>
this.fetchNewAccountData()
);
onBecomeUnobserved(this, "decryptedAccountData", () =>
onBecomeUnobserved(this, 'decryptedAccountData', () =>
this.unobservedAccountData()
);
onBecomeObserved(this, "sharedData", () => this.fetchKeysArray());
onBecomeUnobserved(this, "sharedData", () => this.unobservedSharedData());
onBecomeObserved(this, "transactionsInfo", () =>
onBecomeObserved(this, 'sharedData', () => this.fetchKeysArray());
onBecomeUnobserved(this, 'sharedData', () => this.unobservedSharedData());
onBecomeObserved(this, 'transactionsInfo', () =>
this.fetchTransactionsInfo()
);
onBecomeUnobserved(this, "transactionsInfo", () =>
onBecomeUnobserved(this, 'transactionsInfo', () =>
this.unobservedTransactionsInfo()
);
@@ -83,7 +86,7 @@ class Store {
fetchVPNServers() {
fetchWrapper
.getAuth("vpn/servers", {
.getAuth('vpn/servers', {
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID,
})
@@ -97,17 +100,12 @@ class Store {
}
fetchNewAccountData() {
getNodeInfo()
.then((info) => {
this.fetchNodeInfoSuccess(info);
fetchWrapper
.getAuth("data/private", {
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID,
})
.then((res) => this.accountDataFetchChange(res))
.catch((err) => this.throwError(err));
fetchWrapper
.getAuth('data/private', {
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID,
})
.then((res) => this.accountDataFetchChange(res))
.catch((err) => this.throwError(err));
}
@@ -123,7 +121,7 @@ class Store {
.then((info) => {
this.fetchNodeInfoSuccess(info);
fetchWrapper
.getAuth("data/shared/", {
.getAuth('data/shared/', {
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID,
})
@@ -144,13 +142,13 @@ class Store {
fetchTempTransactions() {
fetchWrapper
.get("node/transactions")
.get('node/transactions')
.then((res) => this.fetchTempTransactionsSuccess(res))
.catch((err) => this.throwError(err));
}
fetchPassPhrase() {
this._passPhrase = sessionStorage.getItem("passPhrase") || "";
this._passPhrase = sessionStorage.getItem('passPhrase') || '';
}
fetchSharedData() {
@@ -173,7 +171,7 @@ class Store {
if (data.length > 0) {
fetchWrapper
.postAuth(
"data/shared",
'data/shared',
{
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID,
@@ -190,7 +188,7 @@ class Store {
pushAccountData(data = this.accountData) {
fetchWrapper
.postAuth(
"data/private",
'data/private',
{
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID,
@@ -221,7 +219,7 @@ class Store {
if (changed.length !== 0) signedTx = builder.update(changed);
if (signedTx) {
fetchWrapper
.post("transactions", {}, signedTx)
.post('transactions', {}, signedTx)
.then(() => this.fetchTempTransactions())
.catch((err) => this.throwError(err));
this.accountInfo.sequence.nonce++;
@@ -240,7 +238,7 @@ class Store {
if (signedTx) {
fetchWrapper
.post("transactions", {}, signedTx)
.post('transactions', {}, signedTx)
.then(() => this.fetchTempTransactions())
.catch((err) => this.throwError(err));
this.accountInfo.sequence.nonce++;
@@ -262,7 +260,7 @@ class Store {
if (signedTx) {
fetchWrapper
.post("transactions", {}, signedTx)
.post('transactions', {}, signedTx)
.then(() => this.fetchTempTransactions())
.catch((err) => this.throwError(err));
this.accountInfo.sequence.nonce++;
@@ -271,12 +269,12 @@ class Store {
generatePassPhrase() {
this._passPhrase = passphrase.Mnemonic.generateMnemonic();
sessionStorage.setItem("passPhrase", this._passPhrase);
sessionStorage.setItem('passPhrase', this._passPhrase);
}
savePastPassPhrase(phrase) {
this._passPhrase = phrase;
sessionStorage.setItem("passPhrase", this._passPhrase);
sessionStorage.setItem('passPhrase', this._passPhrase);
}
unobservedTransactionsInfo() {
@@ -398,12 +396,12 @@ class Store {
}
get firstName() {
return this.decryptedAccountData.find((item) => item.key === "firstname")
return this.decryptedAccountData.find((item) => item.key === 'firstname')
?.value;
}
get lastName() {
return this.decryptedAccountData.find((item) => item.key === "secondname")
return this.decryptedAccountData.find((item) => item.key === 'secondname')
?.value;
}
@@ -444,27 +442,27 @@ class Store {
let x25519_sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES);
sodium.crypto_sign_ed25519_sk_to_curve25519(
x25519_sk,
Buffer.from(this.privateKey, "hex")
Buffer.from(this.privateKey, 'hex')
);
return x25519_sk.toString("Base64");
return x25519_sk.toString('Base64');
}
get pubKey() {
return this.addressAndPubKey.publicKey.toString("hex");
return this.addressAndPubKey.publicKey.toString('hex');
}
get address() {
return this.addressAndPubKey.address.toString("hex");
return this.addressAndPubKey.address.toString('hex');
}
get tokenKey() {
const stringToSign =
this.nodeInfo.networkIdentifier + this.nodeInfo.lastBlockID;
if (!stringToSign) return "";
if (!stringToSign) return '';
const sign = cryptography
.signDataWithPassphrase(Buffer.from(stringToSign, "hex"), this.passPhrase)
.toString("hex");
return this.pubKey + ":" + sign;
.signDataWithPassphrase(Buffer.from(stringToSign, 'hex'), this.passPhrase)
.toString('hex');
return this.pubKey + ':' + sign;
}
get accountIdentity() {
@@ -598,7 +596,19 @@ class Store {
}
get vpnServers() {
return this._vpnServers;
return this._vpnServers?.map((server) => ({
...server,
transferSum: formatBytes(
Number(server.transferTx || 0) + Number(server.transferRx || 0)
),
trafficUsed: Math.floor(
((Number(server.transferTx || 0) + Number(server.transferRx || 0)) /
(50 * 1024 * 1024 * 1024)) *
100
),
state: server.state === '1',
country: countryCodes[server.country] || 'US',
}));
}
get canVPN() {
@@ -611,13 +621,13 @@ class Store {
let amount = this.processedVotes[address];
if (amount) return "Pending";
if (amount) return 'Pending';
amount = this.accountSentVotes[address];
if (amount) return "Unvote";
if (amount) return 'Unvote';
return "Vote";
return 'Vote';
};
return this._delegates.data.map((delegate) => ({

View File

@@ -139,6 +139,18 @@ export const hashValue = (value = '', seed = '') =>
Buffer.concat([Buffer.from(seed, 'utf8'), cryptography.hash(value, 'utf8')])
);
export const formatBytes = (bytes, decimals = 2) => {
if (!+bytes) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
};
export const generateTransaction = (
nonce = BigInt(0),
senderPublicKey = '',

252
src/utils/countryCodes.js Normal file
View File

@@ -0,0 +1,252 @@
export const countryCodes = {
BGD: 'BD',
BEL: 'BE',
BFA: 'BF',
BGR: 'BG',
BIH: 'BA',
BRB: 'BB',
WLF: 'WF',
BLM: 'BL',
BMU: 'BM',
BRN: 'BN',
BOL: 'BO',
BHR: 'BH',
BDI: 'BI',
BEN: 'BJ',
BTN: 'BT',
JAM: 'JM',
BVT: 'BV',
BWA: 'BW',
WSM: 'WS',
BES: 'BQ',
BRA: 'BR',
BHS: 'BS',
JEY: 'JE',
BLR: 'BY',
BLZ: 'BZ',
RUS: 'RU',
RWA: 'RW',
SRB: 'RS',
TLS: 'TL',
REU: 'RE',
TKM: 'TM',
TJK: 'TJ',
ROU: 'RO',
TKL: 'TK',
GNB: 'GW',
GUM: 'GU',
GTM: 'GT',
SGS: 'GS',
GRC: 'GR',
GNQ: 'GQ',
GLP: 'GP',
JPN: 'JP',
GUY: 'GY',
GGY: 'GG',
GUF: 'GF',
GEO: 'GE',
GRD: 'GD',
GBR: 'GB',
GAB: 'GA',
SLV: 'SV',
GIN: 'GN',
GMB: 'GM',
GRL: 'GL',
GIB: 'GI',
GHA: 'GH',
OMN: 'OM',
TUN: 'TN',
JOR: 'JO',
HRV: 'HR',
HTI: 'HT',
HUN: 'HU',
HKG: 'HK',
HND: 'HN',
HMD: 'HM',
VEN: 'VE',
PRI: 'PR',
PSE: 'PS',
PLW: 'PW',
PRT: 'PT',
SJM: 'SJ',
PRY: 'PY',
IRQ: 'IQ',
PAN: 'PA',
PYF: 'PF',
PNG: 'PG',
PER: 'PE',
PAK: 'PK',
PHL: 'PH',
PCN: 'PN',
POL: 'PL',
SPM: 'PM',
ZMB: 'ZM',
ESH: 'EH',
EST: 'EE',
EGY: 'EG',
ZAF: 'ZA',
ECU: 'EC',
ITA: 'IT',
VNM: 'VN',
SLB: 'SB',
ETH: 'ET',
SOM: 'SO',
ZWE: 'ZW',
SAU: 'SA',
ESP: 'ES',
ERI: 'ER',
MNE: 'ME',
MDA: 'MD',
MDG: 'MG',
MAF: 'MF',
MAR: 'MA',
MCO: 'MC',
UZB: 'UZ',
MMR: 'MM',
MLI: 'ML',
MAC: 'MO',
MNG: 'MN',
MHL: 'MH',
MKD: 'MK',
MUS: 'MU',
MLT: 'MT',
MWI: 'MW',
MDV: 'MV',
MTQ: 'MQ',
MNP: 'MP',
MSR: 'MS',
MRT: 'MR',
IMN: 'IM',
UGA: 'UG',
TZA: 'TZ',
MYS: 'MY',
MEX: 'MX',
ISR: 'IL',
FRA: 'FR',
IOT: 'IO',
SHN: 'SH',
FIN: 'FI',
FJI: 'FJ',
FLK: 'FK',
FSM: 'FM',
FRO: 'FO',
NIC: 'NI',
NLD: 'NL',
NOR: 'NO',
NAM: 'NA',
VUT: 'VU',
NCL: 'NC',
NER: 'NE',
NFK: 'NF',
NGA: 'NG',
NZL: 'NZ',
NPL: 'NP',
NRU: 'NR',
NIU: 'NU',
COK: 'CK',
XKX: 'XK',
CIV: 'CI',
CHE: 'CH',
COL: 'CO',
CHN: 'CN',
CMR: 'CM',
CHL: 'CL',
CCK: 'CC',
CAN: 'CA',
COG: 'CG',
CAF: 'CF',
COD: 'CD',
CZE: 'CZ',
CYP: 'CY',
CXR: 'CX',
CRI: 'CR',
CUW: 'CW',
CPV: 'CV',
CUB: 'CU',
SWZ: 'SZ',
SYR: 'SY',
SXM: 'SX',
KGZ: 'KG',
KEN: 'KE',
SSD: 'SS',
SUR: 'SR',
KIR: 'KI',
KHM: 'KH',
KNA: 'KN',
COM: 'KM',
STP: 'ST',
SVK: 'SK',
KOR: 'KR',
SVN: 'SI',
PRK: 'KP',
KWT: 'KW',
SEN: 'SN',
SMR: 'SM',
SLE: 'SL',
SYC: 'SC',
KAZ: 'KZ',
CYM: 'KY',
SGP: 'SG',
SWE: 'SE',
SDN: 'SD',
DOM: 'DO',
DMA: 'DM',
DJI: 'DJ',
DNK: 'DK',
VGB: 'VG',
DEU: 'DE',
YEM: 'YE',
DZA: 'DZ',
USA: 'US',
URY: 'UY',
MYT: 'YT',
UMI: 'UM',
LBN: 'LB',
LCA: 'LC',
LAO: 'LA',
TUV: 'TV',
TWN: 'TW',
TTO: 'TT',
TUR: 'TR',
LKA: 'LK',
LIE: 'LI',
LVA: 'LV',
TON: 'TO',
LTU: 'LT',
LUX: 'LU',
LBR: 'LR',
LSO: 'LS',
THA: 'TH',
ATF: 'TF',
TGO: 'TG',
TCD: 'TD',
TCA: 'TC',
LBY: 'LY',
VAT: 'VA',
VCT: 'VC',
ARE: 'AE',
AND: 'AD',
ATG: 'AG',
AFG: 'AF',
AIA: 'AI',
VIR: 'VI',
ISL: 'IS',
IRN: 'IR',
ARM: 'AM',
ALB: 'AL',
AGO: 'AO',
ATA: 'AQ',
ASM: 'AS',
ARG: 'AR',
AUS: 'AU',
AUT: 'AT',
ABW: 'AW',
IND: 'IN',
ALA: 'AX',
AZE: 'AZ',
IRL: 'IE',
IDN: 'ID',
UKR: 'UA',
QAT: 'QA',
MOZ: 'MZ',
};

View File

@@ -1,19 +1,19 @@
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const plugins = [
new HtmlWebpackPlugin({
favicon: "./public/favicon.ico",
template: "./public/index.html",
favicon: './public/favicon.ico',
template: './public/index.html',
}),
new webpack.ProvidePlugin({
Buffer: ["buffer", "Buffer"],
Buffer: ['buffer', 'Buffer'],
}),
new webpack.ProvidePlugin({
process: "process/browser",
process: 'process/browser',
}),
require("tailwindcss"),
require('tailwindcss'),
new webpack.ContextReplacementPlugin(/sodium/, (data) => {
delete data.dependencies[0].critical;
return data;
@@ -21,86 +21,83 @@ const plugins = [
];
module.exports = (env, argv) => ({
entry: "./src/main.jsx",
mode: argv.mode || "development",
devtool: argv.mode === "production" ? false : "eval-source-map",
entry: './src/main.jsx',
mode: argv.mode || 'development',
devtool: argv.mode === 'production' ? false : 'eval-source-map',
plugins,
output: {
clean: true,
path: path.resolve(__dirname, "build"),
assetModuleFilename: "assets/[fullhash:12][ext]",
filename: "main.[fullhash:8].js",
path: path.resolve(__dirname, 'build'),
assetModuleFilename: 'assets/[fullhash:12][ext]',
filename: 'main.[fullhash:8].js',
},
devServer: {
open: true,
static: {
directory: path.join(__dirname, "public"),
directory: path.join(__dirname, 'public'),
},
compress: true,
port: "auto",
port: 'auto',
hot: true,
client: {
progress: true,
},
proxy: {
"/api/**": {
target: "https://tn-alpha.idntty.org:8080/",
'/api/**': {
target: 'https://tn-alpha.idntty.org:8080/',
secure: false,
changeOrigin: true,
},
},
},
ignoreWarnings: [() => false],
module: {
rules: [
{ test: /\.(html)$/, use: ["html-loader"] },
{ test: /\.(html)$/, use: ['html-loader'] },
{
test: /\.m?jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
loader: 'babel-loader',
},
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader",
loader: 'style-loader',
},
{
loader: "css-loader",
loader: 'css-loader',
},
{
loader: "postcss-loader",
loader: 'postcss-loader',
},
{
loader: "sass-loader",
loader: 'sass-loader',
},
],
},
{
test: /\.(png|svg|jpg|gif)$/,
loader: "file-loader",
loader: 'file-loader',
options: {
outputPath: "assets",
outputPath: 'assets',
},
},
],
},
resolve: {
extensions: [".js", ".jsx"],
extensions: ['.js', '.jsx'],
fallback: {
path: require.resolve("path-browserify"),
os: require.resolve("os-browserify/browser"),
querystring: require.resolve("querystring-es3"),
crypto: require.resolve("crypto-browserify"),
util: require.resolve("util/"),
stream: require.resolve("stream-browserify"),
url: require.resolve("url/"),
buffer: require.resolve("buffer/"),
path: require.resolve('path-browserify'),
os: require.resolve('os-browserify/browser'),
querystring: require.resolve('querystring-es3'),
crypto: require.resolve('crypto-browserify'),
util: require.resolve('util/'),
stream: require.resolve('stream-browserify'),
url: require.resolve('url/'),
buffer: require.resolve('buffer/'),
fs: false,
},
},