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

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',
};