This commit is contained in:
chaosBreaking
2019-08-31 14:59:13 +08:00
commit dc5423d356
65 changed files with 3813 additions and 0 deletions

41
src/utils/account.js Normal file
View File

@@ -0,0 +1,41 @@
'use strict';
const tools = require('./crypto');
const getPath = uid => {
const pre = Date.now() + '';
let seed = pre + uid;
if (seed.length < 19) seed += (Date.now() + '').slice(seed.length - 19);
const frag1 = seed.slice(0, 6);
const frag2 = seed.slice(6, 12);
const frag3 = seed.slice(-8);
const append = `/${frag1}'/${frag2}/${frag3}`;
return {
BTC: `m/44'/0'${append}`,
ETH: `m/44'/60'${append}`
};
};
module.exports = {
createAccount (type = 'BTC') {
const keypair = tools.randomKeypair();
return {
type,
...keypair,
address: tools.pubkey2address(keypair.pubkey, {
coin: type
})
};
},
deriveNewAccount (root, option) {
const { coin, seed } = option;
if (!coin || !seed) throw new Error('Invalid Params');
const path = getPath(seed)[coin];
const keypair = tools.secword2keypair(root, { coin, path });
return {
pubkey: keypair.pubkey,
address: tools.pubkey2address(keypair.pubkey, { coin }),
path
};
}
};

323
src/utils/crypto.js Normal file
View File

@@ -0,0 +1,323 @@
const crypto = require('crypto');
const nacl = require('tweetnacl');
const bs58check = require('bs58check');
const { keccak256 } = require('js-sha3');
const Secword = require('bitcore-mnemonic'); // https://bitcore.io/api/mnemonic/ https://github.com/bitpay/bitcore-mnemonic
// const bip39 = require('bip39') // https://github.com/bitcoinjs/bip39 // 有更多语言,但不方便选择语言,也不能使用 pass
// const HDKey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
// 全部以hex为默认输入输出格式方便人的阅读以及方便函数之间统一接口
const my = {};
my.HASHER = 'sha256'; // 默认的哈希算法。could be md5, sha1, sha256, sha512, ripemd160。 可用 Crypto.getHashes/Ciphers/Curves() 查看支持的种类。
my.HASHER_LIST = crypto.getHashes();
my.CIPHER = 'aes-256-cfb'; // 默认的加解密算法
my.CIPHER_LIST = crypto.getCiphers();
my.CURVE = 'secp256k1'; // 默认的ECDH曲线用于把私钥转成公钥。
my.CURVE_LIST = ['secp256k1']; // crypto.getCurves() 引入到浏览器里后出错,不支持 getCurves.
my.OUTPUT = 'hex'; // 默认的哈希或加密的输入格式
my.OUTPUT_LIST = ['hex', 'latin1', 'base64']; // or 'buf' to Buffer explicitly
my.INPUT = 'utf8'; // 默认的加密方法的明文格式。utf8 能够兼容 latin1, ascii 的情形
my.INPUT_LIST = ['utf8', 'ascii', 'latin1']; // ignored for Buffer/TypedArray/DataView
my.COIN = 'BTC'; // 默认的币种
my.COIN_LIST = ['TIC', 'BTC', 'ETH'];
my.CHAINNET = 'mainnet'; // 默认的链网
module.exports = {
hash: function (data, option) { // data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
if (typeof(data) !== 'boolean' && data !== Infinity) {
option = option || {};
if (typeof (data) !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView))
data = JSON.stringify(data);
if (option.salt && typeof (option.salt) === 'string')
data = data + this.hash(option.salt);
let hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER; // 默认为 sha256.
let inputEncoding = my.INPUT_LIST.indexOf(option.input) >= 0 ? option.input : my.INPUT; // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
let outputEncoding = (option.output === 'buf') ? undefined : (my.OUTPUT_LIST.indexOf(option.output) >= 0 ? option.output : my.OUTPUT); // option.output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64'
return crypto.createHash(hasher).update(data, inputEncoding).digest(outputEncoding);
}
return null;
}
,
isHashable: function (data, option) {
option = option || {};
if (option.strict) {
return data && typeof (data) !== 'boolean' && data !== Infinity; // 允许大多数数据,除了空值、布尔值、无限数
}
return typeof (data) !== 'undefined'; // 允许一切数据,除非 undefined
}
,
isHash: function (hash, option) {
option = option || {};
option.hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER;
switch (option.hasher) {
case 'sha256': return /^[a-fA-F0-9]{64}$/.test(hash);
case 'md5': return /^[a-fA-F0-9]{32}$/.test(hash);
case 'ripemd160': case 'sha1': return /^[a-fA-F0-9]{40}$/.test(hash);
case 'sha512': return /^[a-fA-F0-9]{128}$/.test(hash);
}
return false;
}
,
encrypt: function (data, pwd, option) {
if (this.isHashable(data) && typeof (pwd) === 'string') {
option = option || {};
let inputEncoding = my.INPUT_LIST.indexOf(option.input) >= 0 ? option.input : my.INPUT; // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
let outputEncoding = (option.output === 'buf') ? undefined : (my.OUTPUT_LIST.indexOf(option.output) >= 0 ? option.output : my.OUTPUT); // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
let cipher = crypto.createCipher(
my.CIPHER_LIST.indexOf(option.cipher) >= 0 ? option.cipher : my.CIPHER,
this.hash(pwd));
if (typeof (data) !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView))
data = JSON.stringify(data);
let encrypted = cipher.update(data, inputEncoding, outputEncoding);
encrypted += cipher.final(outputEncoding); // 但是 Buffer + Buffer 还是会变成string
return encrypted;
}
return null;
}
,
decrypt: function (data, pwd, option) { // data 应当是 encrypt 输出的数据类型
if (data && (typeof (data) === 'string' || data instanceof Buffer) && typeof (pwd) === 'string') {
option = option || {};
let inputEncoding = my.OUTPUT_LIST.indexOf(option.input) >= 0 ? option.input : my.OUTPUT; // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
let outputEncoding = (option.output === 'buf') ? undefined : (my.INPUT_LIST.indexOf(option.output) >= 0 ? option.output : my.INPUT); // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly
let decipher = crypto.createDecipher(
my.CIPHER_LIST.indexOf(option.cipher) >= 0 ? option.cipher : my.CIPHER,
this.hash(pwd));
let decrypted = decipher.update(data, inputEncoding, outputEncoding);
decrypted += decipher.final(outputEncoding); // 但是 Buffer + Buffer 还是会变成string
if (option.format === 'json') { // 如果用户输入错误密码deciper也能返回结果。为了判断是否正确结果对应当是 json 格式的原文做解析来验证。
try {
JSON.parse(decrypted);
} catch (exception) {
return null;
}
}
return decrypted;
}
return null;
}
,
sign: function (data, seckey, option) { // data can be string or buffer or object, results are the same
if (this.isHashable(data) && this.isSeckey(seckey)) {
option = option || {};
// 使用nacl的签名算法。注意nacl.sign需要的seckey是64字节=512位而比特币/以太坊的seckey是32字节。因此本方法只能用于 TIC 币的 keypair。
option.output = 'buf'; // 哈希必须输出为 buffer
var hashBuf = this.hash(data, option);
var signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'));
return Buffer.from(signature).toString('hex'); // 返回128个hex字符64字节
// 方案2尚未彻底实现。
// let hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
// let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
// let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT)
// let signer=crypto.createSign(hasher)
// return signer.update(data, inputEncoding).sign(seckey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式因此这样写是不能用的。
}
return null;
}
,
isSignature: function (signature) {
return /^[a-fA-F0-9]{128}$/.test(signature);
}
,
verify: function (data, signature, pubkey, option) { // data could be anything, but converts to string or remains be Buffer/TypedArray/DataView
if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey)) {
option = option || {};
option.output = 'buf'; // 哈希必须输出为 buffer
var bufHash = this.hash(data, option);
var bufSignature = Buffer.from(signature, 'hex');
var bufPubkey = Buffer.from(pubkey, 'hex');
var res = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey);
return res;
}
return null;
}
,
pass2keypair: function (pass, option) { // 如果使用其他机制例如密码、随机数不使用secword也可生成keypair
if (this.isHashable(pass)) {
option = option || {};
option.hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER;
var hashBuf = crypto.createHash(option.hasher).update(pass).digest();
var keypair = nacl.sign.keyPair.fromSeed(hashBuf);
return {
hash: hashBuf.toString('hex'),
pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型
seckey: Buffer.from(keypair.secretKey).toString('hex')
};
}
return null;
}
,
secword2keypair: function (secword, option = {coin: my.COIN, path: 'master'}) { // option.coin 币种option.passphase 密码默认为空option.path==='master' 生成 HD master key不定义则默认为相应币种的第一对公私钥。
if (Secword.isValid(secword)) {
const { coin = my.COIN, path = 'master', pass = '' } = option;
// 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。
// let hdmaster=HDKey.fromMasterSeed(new Buffer(this.secword2seed(secword, option.pass), 'hex')) // 和 new Secword(secword).toHDPrivateKey 求出的公私钥一样!
let hdmaster = new Secword(secword).toHDPrivateKey(pass); // 和 ethers.HDNode.fromMnemonic(secword)的公私钥一样。而 ethers.HDNode.fromMnemonic(secword).derivePath("m/44'/60'/0'/0/0")的公私钥===ethers.Wallet.fromMnemonic(secword [,"m/44'/60'/0'/0/0"])
let key = hdmaster;
if (path === 'master') {
key = hdmaster;
} else if (!path) {
switch (coin) {
case 'BTC': key = hdmaster.derive("m/44'/0'/0'/0/0"); break;
case 'ETH': key = hdmaster.derive("m/44'/60'/0'/0/0"); break;
default: key = hdmaster.derive("m/44'/99'/0'/0/0"); break;
}
} else { // 指定了路径 option.path例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
key = hdmaster.derive(path);
}
return {
coin,
path,
seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果
pubkey: key.publicKey.toString('hex')
};
}
return null;
}
,
seckey2pubkey: function (seckey, option = {}) {
if (this.isSeckey(seckey) && seckey.length === 64) { // 只能用于32字节的私钥BTC, ETH)。也就是不能用于 TIC 的私钥。
const {curve = my.CURVE, compress = 'compressed'} = option;
return new crypto.ECDH(curve).setPrivateKey(seckey, 'hex').getPublicKey('hex', compress).toString('hex'); // ecdh.getPublicKey(不加参数) 默认为 'uncompressed'
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。
// 或者 require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'),compress).toString('hex')
// 或者 require('bitcore-lib').PublicKey.fromPrivateKey(new Btc.PrivateKey(seckey)).toString('hex')
// 注意Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
}
return null;
}
,
secword2account: function (secword, option) { // account 比 keypair 多了 address 字段。
option = option || {};
option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN;
let kp = this.secword2keypair(secword, option);
if (kp) {
kp.address = this.pubkey2address(kp.pubkey, option);
return kp;
}
return null;
}
,
secword2address: function (secword, option) {
option = option || {};
option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN;
let kp = this.secword2keypair(secword, option);
if (kp) {
return this.pubkey2address(kp.pubkey, option);
}
return null;
}
,
isSecword: function (secword) {
return Secword.isValid(secword);
}
,
isSeckey: function (seckey) {
// 比特币、以太坊的私钥64 hex
// nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey);
}
,
isPubkey: function (pubkey) {
// 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
// 以太坊的公钥:'02|03' + 64 hex
// nacl.sign 的公钥64 hex
return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey); // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
}
,
isAddress: function (address) {
return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address); // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) {
}
,
pubkey2address: function (pubkey, option = {}) { // pubkey 应当是string类型
if (this.isPubkey(pubkey)) {
const { coin = 'BTC', netType = 'mainnet', curve = my.CURVE } = option;
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest();
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex');
let prefix;
if (coin === 'BTC') {
switch (netType) {
case 'mainnet': prefix = '00'; break; // 1
case 'testnet': prefix = '6f'; break; // m or n
case 'p2sh': prefix = '05'; break; // 3
default: prefix = '00';
}
return bs58check.encode(Buffer.from(prefix + h160, 'hex')); // wallet import format
} else if (coin === 'ETH') { // 目前不支持 ETH或其他币种 地址转换因为这会大量增加前端打包的js。
const uncompressedPubkey = crypto.ECDH.convertKey(pubkey, curve, 'hex', 'buffer', 'uncompressed');
return '0x' + keccak256(uncompressedPubkey.slice(1)).slice(24);
}
}
return null;
}
,
secword2seed: function (secword, pass) { // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样是64字节的种子。
if (Secword.isValid(secword)) { // bip39.validateMnemonic(secword)) {
return new Secword(secword).toSeed(pass).toString('hex'); // 结果一致于 bip39.mnemonicToSeedHex(secword) 或 ethers.HDNode.mnemonic2Seed(secword)
}
return null;
}
,
randomSecword: function (lang) { // Object.keys(Secword.Words) => [ 'CHINESE', 'ENGLISH', 'FRENCH', 'ITALIAN', 'JAPANESE', 'SPANISH' ]
lang = (lang && Secword.Words.hasOwnProperty(lang.toUpperCase())) ? lang.toUpperCase() : 'ENGLISH';
return new Secword(Secword.Words[lang]).phrase;
}
,
randomSeckey: function () {
return Buffer.from(nacl.box.keyPair().secretKey).toString('hex'); // 32字节
}
,
randomKeypair: function () {
// 此函数有错!!
let kp = nacl.box.keyPair();
const seckey = Buffer.from(kp.secretKey).toString('hex');
const pubkey = this.seckey2pubkey(seckey);
return {
seckey, pubkey
};
}
,
randomString: function (length = 6, alphabet) { // 长度为 length字母表为 alphabet 的随机字符串
alphabet = alphabet || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@";
var text = '';
for (var i = 0; i < length; i++) {
text += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
}
return text;
}
,
randomNumber: function (option) { // 长度为 option.length 的随机数字,或者 (option.min||0) <= num < option.max
option = option || {};
let num = 0;
if (option.length > 0) {
num = parseInt(Math.random() * Math.pow(10, option.length));
let l = new String(num).length;
while (l < option.length) {
num = '0' + num; // 注意,这时返回的是字符串!
l++;
}
} else if (option.max > 0) {
option.min = (option.min >= 0) ? option.min : 0;
num = parseInt(Math.random() * (option.max - option.min)) + option.min;
} else { // 如果 option 为空
num = Math.random();
}
return num;
}
,
rsaSign: function (string2Sign, prikey, signType) {
signType = signType || 'RSA-SHA1'; // could be RSA-SHA256, RSA-SHA1 or more
let signer = crypto.createSign(signType);
return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'));
}
,
rsaVerify: function (string2Verify, sign, pubkey, signType) {
signType = signType || 'RSA-SHA1'; // could be RSA-SHA256, RSA-SHA1 or more
let verifier = crypto.createVerify(signType);
return verifier.update(string2Verify).verify(pubkey, sign, 'base64');
}
};

38
src/utils/index.js Normal file
View File

@@ -0,0 +1,38 @@
/* eslint-disable no-useless-escape */
const PHONE_VALID_REG = /^\+[0-9]+-[0-9]+\b/;
const URL_PROTOCOL_REG = /^\w+(?=\:\/\/)/;
const URL_HOST_REG = /(?<=\:\/\/)\S+(?=\:)|(?<=\:\/\/)\S+(?=\b)/;
const URL_PORT_REG = /(?<=\:)\d+/;
const getFuncProps = fn => {
const reg = /^(async function|function){1}\s*[^\(]*\(\s*([^\)]*)\)/m;
return Object.toString.call(fn).match(reg)[2].replace(/ /g, '').split(',');
};
const isPhoneNumber = n => {
return PHONE_VALID_REG.test(n);
};
const parseURL = (url, type) => {
return type === 'protocol' ? getProtocol(url) :
type === 'host' ? getHost(url) :
type === 'port' ? getPort(url) : '';
};
const getProtocol = url => {
const res = URL_PROTOCOL_REG.exec(url);
return Object.prototype.toString.call(res) === '[object Array]' ? res[0] : null;
};
const getHost = url => {
const res = URL_HOST_REG.exec(url);
return Object.prototype.toString.call(res) === '[object Array]' ? res[0] : null;
};
const getPort = url => {
const res = URL_PORT_REG.exec(url);
return Object.prototype.toString.call(res) === '[object Array]' ? res[0] : null;
};
module.exports = {
getFuncProps,
isPhoneNumber,
parseURL,
getPort,
getHost,
getProtocol
};

30
src/utils/jwt.js Normal file
View File

@@ -0,0 +1,30 @@
const jwt = require('jsonwebtoken');
module.exports = {
createToken: (data = {}, JWT_SECRET, config = {}) => {
if (typeof config !== 'object') {
config = {};
}
if (!config.maxAge || typeof config.maxAge !== 'number') {
config.maxAge = 3600;
}
let token = jwt.sign({ data }, JWT_SECRET, {
expiresIn: config.maxAge,
algorithm: 'HS256'
});
return token;
},
verifyToken: async (token = '', JWT_SECRET) => {
return await new Promise(resolve => {
if (!token) return null;
jwt.verify(token, JWT_SECRET, (err, res) => {
if (err) {
resolve(null);
}
resolve(res);
});
});
}
};

262
src/utils/locale.js Executable file
View File

@@ -0,0 +1,262 @@
module.exports = {
LangSet: [
{ code: 'zh_CN', name: '中文(简体)' },
{ code: 'en_US', name: 'English' },
{ code: 'jp_JP', name: '日本語' },
{ code: 'kr_KR', name: '한국어' },
{ code: 'ru_RU', name: 'русский' },
{ code: 'es_ES', name: 'Español' }
],
NationSet: [
// {pode:"N004", iso2:"AF", name_en:"Afghanistan", name_zh_CN:"阿富汗", name_native:"‫افغانستان‬‎", itc:"+93-"},
// {pode:"N008", iso2:"AL", name_en:"Albania", name_zh_CN:"阿尔巴尼亚", name_native:"Shqipëri", itc:"+355-"},
// {pode:"N010", iso2:"AQ", name_en:"Antarctica", name_zh_CN:"南极洲", itc:"+672-"},
// {pode:"N012", iso2:"DZ", name_en:"Algeria", name_zh_CN:"阿尔及利亚", name_native:"‫الجزائر‬‎", itc:"+213-"},
// {pode:"N016", iso2:"AS", name_en:"American Samoa", name_zh_CN:"美属萨摩亚", itc:"+1-"},
// {pode:"N020", iso2:"AD", name_en:"Andorra", name_zh_CN:"安道尔", itc:"+376-"},
// {pode:"N024", iso2:"AO", name_en:"Angola", name_zh_CN:"安哥拉", itc:"+244-"},
// {pode:"N028", iso2:"AG", name_en:"Antigua and Barbuda", name_zh_CN:"安提瓜和巴布达", itc:"+1-"},
// {pode:"N031", iso2:"AZ", name_en:"Azerbaijan", name_zh_CN:"阿塞拜疆", name_native:"Azərbaycan", itc:"+994-"},
// {pode:"N032", iso2:"AR", name_en:"Argentina", name_zh_CN:"阿根廷", itc:"+54-"},
// {pode:"N036", iso2:"AU", name_en:"Australia", name_zh_CN:"澳大利亚", itc:"+61-"},
// {pode:"N040", iso2:"AT", name_en:"Austria", name_zh_CN:"奥地利", name_native:"Österreich", itc:"+43-"},
// {pode:"N044", iso2:"BS", name_en:"Bahamas", name_zh_CN:"巴哈马", itc:"+1-"},
// {pode:"N048", iso2:"BH", name_en:"Bahrain", name_zh_CN:"巴林", name_native:"‫البحرين‬‎", itc:"+973-"},
// {pode:"N050", iso2:"BD", name_en:"Bangladesh", name_zh_CN:"孟加拉", name_native:"বাংলাদেশ", itc:"+880-"},
// {pode:"N051", iso2:"AM", name_en:"Armenia", name_zh_CN:"亚美尼亚", name_native:"Հայաստան", itc:"+374-"},
// {pode:"N052", iso2:"BB", name_en:"Barbados", name_zh_CN:"巴巴多斯", itc:"+1-"},
// {pode:"N056", iso2:"BE", name_en:"Belgium", name_zh_CN:"比利时", name_native:"België", itc:"+32-"},
// {pode:"N060", iso2:"BM", name_en:"Bermuda", name_zh_CN:"百慕大", itc:"+1-"},
// {pode:"N064", iso2:"BT", name_en:"Bhutan", name_zh_CN:"不丹", name_native:"འབྲུག", itc:"+975-"},
// {pode:"N068", iso2:"BO", name_en:"Bolivia", name_zh_CN:"玻利维亚", itc:"+591-"},
// {pode:"N070", iso2:"BA", name_en:"Bosnia and Herzegovina", name_zh_CN:"波黑", name_native:"Босна и Херцеговина", itc:"+387-"},
// {pode:"N072", iso2:"BW", name_en:"Botswana", name_zh_CN:"博茨瓦纳", itc:"+267-"},
// {pode:"N074", iso2:"BV", name_en:"Bouvet Island", name_zh_CN:"布韦岛", itc:"+47-"},
// {pode:"N076", iso2:"BR", name_en:"Brazil", name_zh_CN:"巴西", name_native:"Brasil", itc:"+55-"},
// {pode:"N084", iso2:"BZ", name_en:"Belize", name_zh_CN:"伯利兹", itc:"+501-"},
// {pode:"N086", iso2:"IO", name_en:"British Indian Ocean Territory", name_zh_CN:"英属印度洋领地", itc:"+246-"},
// {pode:"N090", iso2:"SB", name_en:"Solomon Islands", name_zh_CN:"所罗门群岛", itc:"+677-"},
// {pode:"N092", iso2:"VG", name_en:"British Virgin Islands", name_zh_CN:"英属维尔京群岛", itc:"+1-"},
// {pode:"N096", iso2:"BN", name_en:"Brunei", name_zh_CN:"文莱", itc:"+673-"},
// {pode:"N100", iso2:"BG", name_en:"Bulgaria", name_zh_CN:"保加利亚", name_native:"България", itc:"+359-"},
// {pode:"N104", iso2:"MM", name_en:"Myanmar (Burma)", name_zh_CN:"缅甸", name_native:"မြန်မာ", itc:"+95-"},
// {pode:"N108", iso2:"BI", name_en:"Burundi", name_zh_CN:"布隆迪", name_native:"Uburundi", itc:"+257-"},
// {pode:"N112", iso2:"BY", name_en:"Belarus", name_zh_CN:"白俄罗斯", name_native:"Беларусь", itc:"+375-"},
// {pode:"N116", iso2:"KH", name_en:"Cambodia", name_zh_CN:"柬埔寨", name_native:"កម្ពុជា", itc:"+855-"},
// {pode:"N120", iso2:"CM", name_en:"Cameroon", name_zh_CN:"喀麦隆", name_native:"Cameroun", itc:"+237-"},
// {pode:"N124", iso2:"CA", name_en:"Canada", name_zh_CN:"加拿大", itc:"+1-"},
// {pode:"N132", iso2:"CV", name_en:"Cape Verde", name_zh_CN:"佛得角", name_native:"Kabu Verdi", itc:"+238-"},
// {pode:"N136", iso2:"KY", name_en:"Cayman Islands", name_zh_CN:"开曼群岛", itc:"+1-"},
// {pode:"N140", iso2:"CF", name_en:"Central African Republic", name_zh_CN:"中非", name_native:"République centrafricaine", itc:"+236-"},
// {pode:"N144", iso2:"LK", name_en:"Sri Lanka", name_zh_CN:"斯里兰卡", name_native:"ශ්‍රී ලංකාව", itc:"+94-"},
// {pode:"N148", iso2:"TD", name_en:"Chad", name_zh_CN:"乍得", name_native:"Tchad", itc:"+235-"},
// {pode:"N152", iso2:"CL", name_en:"Chile", name_zh_CN:"智利", itc:"+56-"},
{ pode: "N156", iso2: "CN", name_en: "China", name_zh_CN: "中国", name_native: "中国", itc: "+86-" },
{ pode: "N158", iso2: "TW", name_en: "Taiwan", name_zh_CN: "台湾", name_native: "台灣", itc: "+886-" },
// {pode:"N162", iso2:"CX", name_en:"Christmas Island", name_zh_CN:"圣诞岛", itc:"+61-"},
// {pode:"N166", iso2:"CC", name_en:"Cocos (Keeling) Islands", name_zh_CN:"科科斯群岛", itc:"+61-"},
// {pode:"N170", iso2:"CO", name_en:"Colombia", name_zh_CN:"哥伦比亚", itc:"+57-"},
// {pode:"N174", iso2:"KM", name_en:"Comoros", name_zh_CN:"科摩罗", name_native:"‫جزر القمر‬‎", itc:"+269-"},
// {pode:"N175", iso2:"YT", name_en:"Mayotte", name_zh_CN:"马约特", itc:"+262-"},
// {pode:"N178", iso2:"CG", name_en:"Congo (Republic)", name_zh_CN:"刚果(布)", name_native:"Congo-Brazzaville", itc:"+242-"},
// {pode:"N180", iso2:"CD", name_en:"Congo (DRC)", name_zh_CN:"刚果(金)", name_native:"Jamhuri ya Kidemokrasia ya Kongo", itc:"+243-"},
// {pode:"N184", iso2:"CK", name_en:"Cook Islands", name_zh_CN:"库克群岛", itc:"+682-"},
// {pode:"N188", iso2:"CR", name_en:"Costa Rica", name_zh_CN:"哥斯达黎加", itc:"+506-"},
// {pode:"N191", iso2:"HR", name_en:"Croatia", name_zh_CN:"克罗地亚", name_native:"Hrvatska", itc:"+385-"},
// {pode:"N192", iso2:"CU", name_en:"Cuba", name_zh_CN:"古巴", itc:"+53-"},
// {pode:"N196", iso2:"CY", name_en:"Cyprus", name_zh_CN:"塞浦路斯", name_native:"Κύπρος", itc:"+357-"},
// {pode:"N203", iso2:"CZ", name_en:"Czech Republic", name_zh_CN:"捷克", name_native:"Česká republika", itc:"+420-"},
// {pode:"N204", iso2:"BJ", name_en:"Benin", name_zh_CN:"贝宁", name_native:"Bénin", itc:"+229-"},
// {pode:"N208", iso2:"DK", name_en:"Denmark", name_zh_CN:"丹麦", name_native:"Danmark", itc:"+45-"},
// {pode:"N212", iso2:"DM", name_en:"Dominica", name_zh_CN:"多米尼克", itc:"+1-"},
// {pode:"N214", iso2:"DO", name_en:"Dominican Republic", name_zh_CN:"多米尼加", name_native:"República Dominicana", itc:"+1-"},
// {pode:"N218", iso2:"EC", name_en:"Ecuador", name_zh_CN:"厄瓜多尔", itc:"+593-"},
// {pode:"N222", iso2:"SV", name_en:"El Salvador", name_zh_CN:"萨尔瓦多", itc:"+503-"},
// {pode:"N226", iso2:"GQ", name_en:"Equatorial Guinea", name_zh_CN:"赤道几内亚", name_native:"Guinea Ecuatorial", itc:"+240-"},
// {pode:"N231", iso2:"ET", name_en:"Ethiopia", name_zh_CN:"埃塞俄比亚", itc:"+251-"},
// {pode:"N232", iso2:"ER", name_en:"Eritrea", name_zh_CN:"厄立特里亚", itc:"+291-"},
// {pode:"N233", iso2:"EE", name_en:"Estonia", name_zh_CN:"爱沙尼亚", name_native:"Eesti", itc:"+372-"},
// {pode:"N234", iso2:"FO", name_en:"Faroe Islands", name_zh_CN:"法罗群岛", name_native:"Føroyar", itc:"+298-"},
// {pode:"N238", iso2:"FK", name_en:"Falkland Islands", name_zh_CN:"马尔维纳斯群岛(福克兰)", name_native:"Islas Malvinas", itc:"+500-"},
// {pode:"N239", iso2:"GS", name_en:"South Georgia and the South Sandwich Islands", name_zh_CN:"南乔治亚岛和南桑威奇群岛", itc:"+500-"},
// {pode:"N242", iso2:"FJ", name_en:"Fiji", name_zh_CN:"斐济群岛", itc:"+679-"},
// {pode:"N246", iso2:"FI", name_en:"Finland", name_zh_CN:"芬兰", name_native:"Suomi", itc:"+358-"},
// {pode:"N248", iso2:"AX", name_en:"Åland Islands", name_zh_CN:"奥兰群岛", itc:"+358-"},
{pode:"N250", iso2:"FR", name_en:"France", name_zh_CN:"法国", itc:"+33-"},
// {pode:"N254", iso2:"GF", name_en:"French Guiana", name_zh_CN:"法属圭亚那", name_native:"Guyane française", itc:"+594-"},
// {pode:"N258", iso2:"PF", name_en:"French Polynesia", name_zh_CN:"法属波利尼西亚", name_native:"Polynésie française", itc:"+689-"},
// {pode:"N260", iso2:"TF", name_en:"French Southern Territories", name_zh_CN:"法属南部领地", itc:"+262-"},
// {pode:"N262", iso2:"DJ", name_en:"Djibouti", name_zh_CN:"吉布提", itc:"+253-"},
// {pode:"N266", iso2:"GA", name_en:"Gabon", name_zh_CN:"加蓬", itc:"+241-"},
// {pode:"N268", iso2:"GE", name_en:"Georgia", name_zh_CN:"格鲁吉亚", name_native:"საქართველო", itc:"+995-"},
// {pode:"N270", iso2:"GM", name_en:"Gambia", name_zh_CN:"冈比亚", itc:"+220-"},
// {pode:"N275", iso2:"PS", name_en:"Palestine", name_zh_CN:"巴勒斯坦", name_native:"‫فلسطين‬‎", itc:"+970-"},
// {pode:"N276", iso2:"DE", name_en:"Germany", name_zh_CN:"德国", name_native:"Deutschland", itc:"+49-"},
// {pode:"N288", iso2:"GH", name_en:"Ghana", name_zh_CN:"加纳", name_native:"Gaana", itc:"+233-"},
// {pode:"N292", iso2:"GI", name_en:"Gibraltar", name_zh_CN:"直布罗陀", itc:"+350-"},
// {pode:"N296", iso2:"KI", name_en:"Kiribati", name_zh_CN:"基里巴斯", itc:"+686-"},
// {pode:"N300", iso2:"GR", name_en:"Greece", name_zh_CN:"希腊", name_native:"Ελλάδα", itc:"+30-"},
// {pode:"N304", iso2:"GL", name_en:"Greenland", name_zh_CN:"格陵兰", name_native:"Kalaallit Nunaat", itc:"+299-"},
// {pode:"N308", iso2:"GD", name_en:"Grenada", name_zh_CN:"格林纳达", itc:"+1-"},
// {pode:"N312", iso2:"GP", name_en:"Guadeloupe", name_zh_CN:"瓜德罗普", itc:"+590-"},
{pode:"N316", iso2:"GU", name_en:"Guam", name_zh_CN:"关岛", itc:"+1-"},
// {pode:"N320", iso2:"GT", name_en:"Guatemala", name_zh_CN:"危地马拉", itc:"+502-"},
// {pode:"N324", iso2:"GN", name_en:"Guinea", name_zh_CN:"几内亚", name_native:"Guinée", itc:"+224-"},
// {pode:"N328", iso2:"GY", name_en:"Guyana", name_zh_CN:"圭亚那", itc:"+592-"},
// {pode:"N332", iso2:"HT", name_en:"Haiti", name_zh_CN:"海地", itc:"+509-"},
// {pode:"N334", iso2:"HM", name_en:"Heard Island and McDonald Islands", name_zh_CN:"赫德岛和麦克唐纳群岛", itc:""},
// {pode:"N336", iso2:"VA", name_en:"Vatican City", name_zh_CN:"梵蒂冈", name_native:"Città del Vaticano", itc:"+39-"},
// {pode:"N340", iso2:"HN", name_en:"Honduras", name_zh_CN:"洪都拉斯", itc:"+504-"},
{ pode: "N344", iso2: "HK", name_en: "Hong Kong", name_zh_CN: "中国香港", name_native: "中国香港", itc: "+852-" },
// {pode:"N348", iso2:"HU", name_en:"Hungary", name_zh_CN:"匈牙利", name_native:"Magyarország", itc:"+36-"},
// {pode:"N352", iso2:"IS", name_en:"Iceland", name_zh_CN:"冰岛", name_native:"Ísland", itc:"+354-"},
// {pode:"N356", iso2:"IN", name_en:"India", name_zh_CN:"印度", name_native:"भारत", itc:"+91-"},
// {pode:"N360", iso2:"ID", name_en:"Indonesia", name_zh_CN:"印尼", itc:"+62-"},
// {pode:"N364", iso2:"IR", name_en:"Iran", name_zh_CN:"伊朗", name_native:"‫ایران‬‎", itc:"+98-"},
// {pode:"N368", iso2:"IQ", name_en:"Iraq", name_zh_CN:"伊拉克", name_native:"‫العراق‬‎", itc:"+964-"},
// {pode:"N372", iso2:"IE", name_en:"Ireland", name_zh_CN:"爱尔兰", itc:"+353-"},
// {pode:"N376", iso2:"IL", name_en:"Israel", name_zh_CN:"以色列", name_native:"‫ישראל‬‎", itc:"+972-"},
// {pode:"N380", iso2:"IT", name_en:"Italy", name_zh_CN:"意大利", name_native:"Italia", itc:"+39-"},
// {pode:"N384", iso2:"CI", name_en:"Côte D'Ivoire", name_zh_CN:"科特迪瓦", itc:"+225-"},
// {pode:"N388", iso2:"JM", name_en:"Jamaica", name_zh_CN:"牙买加", itc:"+1-"},
{pode:"N392", iso2:"JP", name_en:"Japan", name_zh_CN:"日本", name_native:"日本", itc:"+81-"},
// {pode:"N398", iso2:"KZ", name_en:"Kazakhstan", name_zh_CN:"哈萨克斯坦", name_native:"Казахстан", itc:"+7-"},
// {pode:"N400", iso2:"JO", name_en:"Jordan", name_zh_CN:"约旦", name_native:"‫الأردن‬‎", itc:"+962-"},
// {pode:"N404", iso2:"KE", name_en:"Kenya", name_zh_CN:"肯尼亚", itc:"+254-"},
// {pode:"N408", iso2:"KP", name_en:"North Korea", name_zh_CN:"朝鲜", name_native:"조선 민주주의 인민 공화국", itc:"+850-"},
{pode:"N410", iso2:"KR", name_en:"South Korea", name_zh_CN:"韩国", name_native:"대한민국", itc:"+82-"},
// {pode:"N414", iso2:"KW", name_en:"Kuwait", name_zh_CN:"科威特", name_native:"‫الكويت‬‎", itc:"+965-"},
// {pode:"N417", iso2:"KG", name_en:"Kyrgyzstan", name_zh_CN:"吉尔吉斯斯坦", name_native:"Кыргызстан", itc:"+996-"},
// {pode:"N418", iso2:"LA", name_en:"Laos", name_zh_CN:"老挝", name_native:"ລາວ", itc:"+856-"},
// {pode:"N422", iso2:"LB", name_en:"Lebanon", name_zh_CN:"黎巴嫩", name_native:"‫لبنان‬‎", itc:"+961-"},
// {pode:"N426", iso2:"LS", name_en:"Lesotho", name_zh_CN:"莱索托", itc:"+266-"},
// {pode:"N428", iso2:"LV", name_en:"Latvia", name_zh_CN:"拉脱维亚", name_native:"Latvija", itc:"+371-"},
// {pode:"N430", iso2:"LR", name_en:"Liberia", name_zh_CN:"利比里亚", itc:"+231-"},
// {pode:"N434", iso2:"LY", name_en:"Libya", name_zh_CN:"利比亚", name_native:"‫ليبيا‬‎", itc:"+218-"},
// {pode:"N438", iso2:"LI", name_en:"Liechtenstein", name_zh_CN:"列支敦士登", itc:"+423-"},
// {pode:"N440", iso2:"LT", name_en:"Lithuania", name_zh_CN:"立陶宛", name_native:"Lietuva", itc:"+370-"},
// {pode:"N442", iso2:"LU", name_en:"Luxembourg", name_zh_CN:"卢森堡", itc:"+352-"},
{ pode: "N446", iso2: "MO", name_en: "Macao", name_zh_CN: "中国澳门", name_native: "中国澳门", itc: "+853-" },
// {pode:"N450", iso2:"MG", name_en:"Madagascar", name_zh_CN:"马达加斯加", name_native:"Madagasikara", itc:"+261-"},
// {pode:"N454", iso2:"MW", name_en:"Malawi", name_zh_CN:"马拉维", itc:"+265-"},
{pode:"N458", iso2:"MY", name_en:"Malaysia", name_zh_CN:"马来西亚", itc:"+60-"},
// {pode:"N462", iso2:"MV", name_en:"Maldives", name_zh_CN:"马尔代夫", itc:"+960-"},
// {pode:"N466", iso2:"ML", name_en:"Mali", name_zh_CN:"马里", itc:"+223-"},
// {pode:"N470", iso2:"MT", name_en:"Malta", name_zh_CN:"马耳他", itc:"+356-"},
// {pode:"N474", iso2:"MQ", name_en:"Martinique", name_zh_CN:"马提尼克", itc:"+596-"},
// {pode:"N478", iso2:"MR", name_en:"Mauritania", name_zh_CN:"毛里塔尼亚", name_native:"‫موريتانيا‬‎", itc:"+222-"},
// {pode:"N480", iso2:"MU", name_en:"Mauritius", name_zh_CN:"毛里求斯", name_native:"Moris", itc:"+230-"},
{pode:"N484", iso2:"MX", name_en:"Mexico", name_zh_CN:"墨西哥", name_native:"México", itc:"+52-"},
// {pode:"N492", iso2:"MC", name_en:"Monaco", name_zh_CN:"摩纳哥", itc:"+377-"},
// {pode:"N496", iso2:"MN", name_en:"Mongolia", name_zh_CN:"蒙古", name_native:"Монгол", itc:"+976-"},
// {pode:"N498", iso2:"MD", name_en:"Moldova", name_zh_CN:"摩尔多瓦", name_native:"Republica Moldova", itc:"+373-"},
// {pode:"N499", iso2:"ME", name_en:"Montenegro", name_zh_CN:"黑山", name_native:"Crna Gora", itc:"+382-"},
// {pode:"N500", iso2:"MS", name_en:"Montserrat", name_zh_CN:"蒙塞拉特岛", itc:"+1-"},
// {pode:"N504", iso2:"MA", name_en:"Morocco", name_zh_CN:"摩洛哥", name_native:"‫المغرب‬‎", itc:"+212-"},
// {pode:"N508", iso2:"MZ", name_en:"Mozambique", name_zh_CN:"莫桑比克", name_native:"Moçambique", itc:"+258-"},
// {pode:"N512", iso2:"OM", name_en:"Oman", name_zh_CN:"阿曼", name_native:"‫عُمان‬‎", itc:"+968-"},
// {pode:"N516", iso2:"NA", name_en:"Namibia", name_zh_CN:"纳米比亚", name_native:"Namibië", itc:"+264-"},
// {pode:"N520", iso2:"NR", name_en:"Nauru", name_zh_CN:"瑙鲁", itc:"+674-"},
// {pode:"N524", iso2:"NP", name_en:"Nepal", name_zh_CN:"尼泊尔", name_native:"नेपाल", itc:"+977-"},
// {pode:"N528", iso2:"NL", name_en:"Netherlands", name_zh_CN:"荷兰", name_native:"Nederland", itc:"+31-"},
// {pode:"N531", iso2:"CW", name_en:"Curaçao", name_zh_CN:"库拉索", itc:"+599-"},
// {pode:"N533", iso2:"AW", name_en:"Aruba", name_zh_CN:"阿鲁巴", itc:"+297-"},
// {pode:"N534", iso2:"SX", name_en:"Sint Maarten", name_zh_CN:"荷属圣马丁", itc:"+1-"},
// {pode:"N535", iso2:"BQ", name_en:"Caribbean Netherlands", name_zh_CN:"荷兰加勒比区", itc:"+599-"},
// {pode:"N540", iso2:"NC", name_en:"New Caledonia", name_zh_CN:"新喀里多尼亚", name_native:"Nouvelle-Calédonie", itc:"+687-"},
// {pode:"N548", iso2:"VU", name_en:"Vanuatu", name_zh_CN:"瓦努阿图", itc:"+678-"},
// {pode:"N554", iso2:"NZ", name_en:"New Zealand", name_zh_CN:"新西兰", itc:"+64-"},
// {pode:"N558", iso2:"NI", name_en:"Nicaragua", name_zh_CN:"尼加拉瓜", itc:"+505-"},
// {pode:"N562", iso2:"NE", name_en:"Niger", name_zh_CN:"尼日尔", name_native:"Nijar", itc:"+227-"},
// {pode:"N566", iso2:"NG", name_en:"Nigeria", name_zh_CN:"尼日利亚", itc:"+234-"},
// {pode:"N570", iso2:"NU", name_en:"Niue", name_zh_CN:"纽埃", itc:"+683-"},
// {pode:"N574", iso2:"NF", name_en:"Norfolk Island", name_zh_CN:"诺福克岛", itc:"+672-"},
// {pode:"N578", iso2:"NO", name_en:"Norway", name_zh_CN:"挪威", name_native:"Norge", itc:"+47-"},
// {pode:"N580", iso2:"MP", name_en:"Northern Mariana Islands", name_zh_CN:"北马里亚纳群岛", itc:"+1-"},
// {pode:"N581", iso2:"UM", name_en:"U.S. Minor Outlying Islands", name_zh_CN:"美国本土外小岛屿", itc:"+1-"},
// {pode:"N583", iso2:"FM", name_en:"Micronesia", name_zh_CN:"密克罗尼西亚联邦", itc:"+691-"},
// {pode:"N584", iso2:"MH", name_en:"Marshall Islands", name_zh_CN:"马绍尔群岛", itc:"+692-"},
// {pode:"N585", iso2:"PW", name_en:"Palau", name_zh_CN:"帕劳", itc:"+680-"},
// {pode:"N586", iso2:"PK", name_en:"Pakistan", name_zh_CN:"巴基斯坦", name_native:"‫پاکستان‬‎", itc:"+92-"},
// {pode:"N591", iso2:"PA", name_en:"Panama", name_zh_CN:"巴拿马", name_native:"Panamá", itc:"+507-"},
// {pode:"N598", iso2:"PG", name_en:"Papua New Guinea", name_zh_CN:"巴布亚新几内亚", itc:"+675-"},
// {pode:"N600", iso2:"PY", name_en:"Paraguay", name_zh_CN:"巴拉圭", itc:"+595-"},
// {pode:"N604", iso2:"PE", name_en:"Peru", name_zh_CN:"秘鲁", name_native:"Perú", itc:"+51-"},
// {pode:"N608", iso2:"PH", name_en:"Philippines", name_zh_CN:"菲律宾", itc:"+63-"},
// {pode:"N612", iso2:"PN", name_en:"Pitcairn Islands", name_zh_CN:"皮特凯恩群岛", itc:"+64-"},
// {pode:"N616", iso2:"PL", name_en:"Poland", name_zh_CN:"波兰", name_native:"Polska", itc:"+48-"},
// {pode:"N620", iso2:"PT", name_en:"Portugal", name_zh_CN:"葡萄牙", itc:"+351-"},
// {pode:"N624", iso2:"GW", name_en:"Guinea-Bissau", name_zh_CN:"几内亚比绍", name_native:"Guiné Bissau", itc:"+245-"},
// {pode:"N626", iso2:"TL", name_en:"Timor-Leste", name_zh_CN:"东帝汶", itc:"+670-"},
// {pode:"N630", iso2:"PR", name_en:"Puerto Rico", name_zh_CN:"波多黎各", itc:"+1-"},
// {pode:"N634", iso2:"QA", name_en:"Qatar", name_zh_CN:"卡塔尔", name_native:"‫قطر‬‎", itc:"+974-"},
// {pode:"N638", iso2:"RE", name_en:"Réunion", name_zh_CN:"留尼汪", name_native:"La Réunion", itc:"+262-"},
// {pode:"N642", iso2:"RO", name_en:"Romania", name_zh_CN:"罗马尼亚", name_native:"România", itc:"+40-"},
// {pode:"N643", iso2:"RU", name_en:"Russia", name_zh_CN:"俄罗斯", name_native:"Россия", itc:"+7-"},
// {pode:"N646", iso2:"RW", name_en:"Rwanda", name_zh_CN:"卢旺达", itc:"+250-"},
// {pode:"N652", iso2:"BL", name_en:"Saint Barthélemy", name_zh_CN:"圣巴泰勒米岛", itc:"+590-"},
// {pode:"N654", iso2:"SH", name_en:"Saint Helena", name_zh_CN:"圣赫勒拿", itc:"+290-"},
// {pode:"N659", iso2:"KN", name_en:"Saint Kitts and Nevis", name_zh_CN:"圣基茨和尼维斯", itc:"+1-"},
// {pode:"N660", iso2:"AI", name_en:"Anguilla", name_zh_CN:"安圭拉", itc:"+1-"},
// {pode:"N662", iso2:"LC", name_en:"Saint Lucia", name_zh_CN:"圣卢西亚", itc:"+1-"},
// {pode:"N663", iso2:"MF", name_en:"Saint Martin", name_zh_CN:"法属圣马丁", name_native:"Saint-Martin (partie française)", itc:"+590-"},
// {pode:"N666", iso2:"PM", name_en:"Saint Pierre and Miquelon", name_zh_CN:"圣皮埃尔和密克隆", name_native:"Saint-Pierre-et-Miquelon", itc:"+508-"},
// {pode:"N670", iso2:"VC", name_en:"Saint Vincent and the Grenadines", name_zh_CN:"圣文森特和格林纳丁斯", itc:"+1-"},
// {pode:"N674", iso2:"SM", name_en:"San Marino", name_zh_CN:"圣马力诺", itc:"+378-"},
// {pode:"N678", iso2:"ST", name_en:"São Tomé and Príncipe", name_zh_CN:"圣多美和普林西比", name_native:"São Tomé e Príncipe", itc:"+239-"},
// {pode:"N682", iso2:"SA", name_en:"Saudi Arabia", name_zh_CN:"沙特阿拉伯", name_native:"‫المملكة العربية السعودية‬‎", itc:"+966-"},
// {pode:"N686", iso2:"SN", name_en:"Senegal", name_zh_CN:"塞内加尔", name_native:"Sénégal", itc:"+221-"},
// {pode:"N688", iso2:"RS", name_en:"Serbia", name_zh_CN:"塞尔维亚", name_native:"Србија", itc:"+381-"},
// {pode:"N690", iso2:"SC", name_en:"Seychelles", name_zh_CN:"塞舌尔", itc:"+248-"},
// {pode:"N694", iso2:"SL", name_en:"Sierra Leone", name_zh_CN:"塞拉利昂", itc:"+232-"},
{pode:"N702", iso2:"SG", name_en:"Singapore", name_zh_CN:"新加坡", itc:"+65-"},
// {pode:"N703", iso2:"SK", name_en:"Slovakia", name_zh_CN:"斯洛伐克", name_native:"Slovensko", itc:"+421-"},
// {pode:"N704", iso2:"VN", name_en:"Vietnam", name_zh_CN:"越南", name_native:"Việt Nam", itc:"+84-"},
// {pode:"N705", iso2:"SI", name_en:"Slovenia", name_zh_CN:"斯洛文尼亚", name_native:"Slovenija", itc:"+386-"},
// {pode:"N706", iso2:"SO", name_en:"Somalia", name_zh_CN:"索马里", name_native:"Soomaaliya", itc:"+252-"},
// {pode:"N710", iso2:"ZA", name_en:"South Africa", name_zh_CN:"南非", itc:"+27-"},
// {pode:"N716", iso2:"ZW", name_en:"Zimbabwe", name_zh_CN:"津巴布韦", itc:"+263-"},
// {pode:"N724", iso2:"ES", name_en:"Spain", name_zh_CN:"西班牙", name_native:"España", itc:"+34-"},
// {pode:"N728", iso2:"SS", name_en:"South Sudan", name_zh_CN:"南苏丹", name_native:"‫جنوب السودان‬‎", itc:"+211-"},
// {pode:"N729", iso2:"SD", name_en:"Sudan", name_zh_CN:"苏丹", name_native:"‫السودان‬‎", itc:"+249-"},
// {pode:"N732", iso2:"EH", name_en:"Western Sahara", name_zh_CN:"西撒哈拉", name_native:"‫الصحراء الغربية‬‎", itc:"+212-"},
// {pode:"N740", iso2:"SR", name_en:"Suriname", name_zh_CN:"苏里南", itc:"+597-"},
// {pode:"N744", iso2:"SJ", name_en:"Svalbard and Jan Mayen", name_zh_CN:"斯瓦尔巴群岛和扬马延岛", itc:"+47-"},
// {pode:"N748", iso2:"SZ", name_en:"Swaziland", name_zh_CN:"斯威士兰", itc:"+268-"},
{pode:"N752", iso2:"SE", name_en:"Sweden", name_zh_CN:"瑞典", name_native:"Sverige", itc:"+46-"},
{pode:"N756", iso2:"CH", name_en:"Switzerland", name_zh_CN:"瑞士", name_native:"Schweiz", itc:"+41-"},
// {pode:"N760", iso2:"SY", name_en:"Syria", name_zh_CN:"叙利亚", name_native:"‫سوريا‬‎", itc:"+963-"},
// {pode:"N762", iso2:"TJ", name_en:"Tajikistan", name_zh_CN:"塔吉克斯坦", itc:"+992-"},
// {pode:"N764", iso2:"TH", name_en:"Thailand", name_zh_CN:"泰国", name_native:"ไทย", itc:"+66-"},
// {pode:"N768", iso2:"TG", name_en:"Togo", name_zh_CN:"多哥", itc:"+228-"},
// {pode:"N772", iso2:"TK", name_en:"Tokelau", name_zh_CN:"托克劳", itc:"+690-"},
// {pode:"N776", iso2:"TO", name_en:"Tonga", name_zh_CN:"汤加", itc:"+676-"},
// {pode:"N780", iso2:"TT", name_en:"Trinidad and Tobago", name_zh_CN:"特立尼达和多巴哥", itc:"+1-"},
// {pode:"N784", iso2:"AE", name_en:"United Arab Emirates", name_zh_CN:"阿联酋", name_native:"‫الإمارات العربية المتحدة‬‎", itc:"+971-"},
// {pode:"N788", iso2:"TN", name_en:"Tunisia", name_zh_CN:"突尼斯", name_native:"‫تونس‬‎", itc:"+216-"},
// {pode:"N792", iso2:"TR", name_en:"Turkey", name_zh_CN:"土耳其", name_native:"Türkiye", itc:"+90-"},
// {pode:"N795", iso2:"TM", name_en:"Turkmenistan", name_zh_CN:"土库曼斯坦", itc:"+993-"},
// {pode:"N796", iso2:"TC", name_en:"Turks and Caicos Islands", name_zh_CN:"特克斯和凯科斯群岛", itc:"+1-"},
// {pode:"N798", iso2:"TV", name_en:"Tuvalu", name_zh_CN:"图瓦卢", itc:"+688-"},
// {pode:"N800", iso2:"UG", name_en:"Uganda", name_zh_CN:"乌干达", itc:"+256-"},
// {pode:"N804", iso2:"UA", name_en:"Ukraine", name_zh_CN:"乌克兰", name_native:"Україна", itc:"+380-"},
// {pode:"N807", iso2:"MK", name_en:"Macedonia", name_zh_CN:"马其顿", name_native:"Македонија", itc:"+389-"},
// {pode:"N818", iso2:"EG", name_en:"Egypt", name_zh_CN:"埃及", name_native:"‫مصر‬‎", itc:"+20-"},
{pode:"N826", iso2:"GB", name_en:"United Kingdom", name_zh_CN:"英国", itc:"+44-"},
// {pode:"N831", iso2:"GG", name_en:"Guernsey", name_zh_CN:"根西岛", itc:"+44-"},
// {pode:"N832", iso2:"JE", name_en:"Jersey", name_zh_CN:"泽西岛", itc:"+44-"},
// {pode:"N833", iso2:"IM", name_en:"Isle of Man", name_zh_CN:"马恩岛", itc:"+44-"},
// {pode:"N834", iso2:"TZ", name_en:"Tanzania", name_zh_CN:"坦桑尼亚", itc:"+255-"},
{pode:"N840", iso2:"US", name_en:"United States", name_zh_CN:"美国", itc:"+1-"},
// {pode:"N850", iso2:"VI", name_en:"U.S. Virgin Islands", name_zh_CN:"美属维尔京群岛", itc:"+1-"},
// {pode:"N854", iso2:"BF", name_en:"Burkina Faso", name_zh_CN:"布基纳法索", itc:"+226-"},
// {pode:"N858", iso2:"UY", name_en:"Uruguay", name_zh_CN:"乌拉圭", itc:"+598-"},
// {pode:"N860", iso2:"UZ", name_en:"Uzbekistan", name_zh_CN:"乌兹别克斯坦", name_native:"Oʻzbekiston", itc:"+998-"},
// {pode:"N862", iso2:"VE", name_en:"Venezuela", name_zh_CN:"委内瑞拉", itc:"+58-"},
// {pode:"N876", iso2:"WF", name_en:"Wallis and Futuna", name_zh_CN:"瓦利斯和富图纳", name_native:"Wallis-et-Futuna", itc:"+681-"},
// {pode:"N882", iso2:"WS", name_en:"Samoa", name_zh_CN:"萨摩亚", itc:"+685-"},
// {pode:"N887", iso2:"YE", name_en:"Yemen", name_zh_CN:"也门", name_native:"‫اليمن‬‎", itc:"+967-"},
// {pode:"N894", iso2:"ZM", name_en:"Zambia", name_zh_CN:"赞比亚", itc:"+260-"}
]
};

50
src/utils/messenger.js Normal file
View File

@@ -0,0 +1,50 @@
'use strict';
async function sendSmsAliyun (smsClient, phone, msgParam, templateCode, signName) { // msgParam 是消息模板参数对象,例如 { code: "890353" }
let matches = phone.match(/\d+/g);
let smsNumber = matches[0] === '86' ? matches[1] : '00' + matches[0] + matches[1];
const res = await smsClient.sendSMS({
PhoneNumbers: smsNumber,//必填:待发送手机号。支持以逗号分隔的形式进行批量调用批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时接收号码格式为00+国际区号+号码如“0085200000000”
SignName: signName,//必填:短信签名-可在短信控制台中找到
TemplateCode: templateCode,//必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版
TemplateParam: JSON.stringify(msgParam) //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时。
});
if (res && res.Code && res.Code === 'ok') {
return { state: 'DONE' };
} else {
return { state: 'FAILED', error: res };
}
}
module.exports = {
sendMail: function (SysConfig) {
const smtpTransporter = require('nodemailer').createTransport(SysConfig.SMTP);
return async function (option) { // 或者如果smtp参数已经确定就可以直接定义 sendMail: Bluebird.promisify(Smtp.sendMail).bind(Smtp)
/*
{
from: '"Fred Foo 👻" <foo@example.com>', // sender address
to: "bar@example.com, baz@example.com", // list of receivers
subject: "Hello ✔", // Subject line
text: "Hello world?", // plain text body
html: "<b>Hello world?</b>" // html body
}
*/
return await smtpTransporter.sendMail(option);
};
}
,
sendSms: function (SysConfig) {
const smsClient = new (require('@alicloud/sms-sdk'))({ // 在调用时,才创建 smsClient防止 SysConfig 还没有建立好。
accessKeyId: SysConfig.SMS.aliyun.accessKeyId,
secretAccessKey: SysConfig.SMS.aliyun.secretAccessKey
});
return async function (phone, option = {}) { // 通过option对象对外提供统一的调用参数格式
if (/^\+\d+-\d+$/.test(phone)) {
if (option.msgParam && option.templateCode && option.signName) {
return await sendSmsAliyun(smsClient, phone, option.msgParam, option.templateCode, option.signName);
}
}
return null; // 手机号格式错误,或者 option.vendor 错误。
};
}
};