mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Bump to ES6
This commit is contained in:
@@ -1,30 +1,29 @@
|
||||
const { HTTP } = require('./open-api');
|
||||
import { HTTP } from './open-api';
|
||||
|
||||
const cache = new Map();
|
||||
|
||||
async function download(url, ua) {
|
||||
ua = ua || 'Quantumult%20X/1.0.29 (iPhone14,5; iOS 15.4.1)';
|
||||
const id = ua + url;
|
||||
if (cache.has(id)) {
|
||||
return cache.get(id);
|
||||
}
|
||||
export default async function download(url, ua) {
|
||||
ua = ua || 'Quantumult%20X/1.0.29 (iPhone14,5; iOS 15.4.1)';
|
||||
const id = ua + url;
|
||||
if (cache.has(id)) {
|
||||
return cache.get(id);
|
||||
}
|
||||
|
||||
const $http = HTTP({
|
||||
headers: {
|
||||
'User-Agent': ua
|
||||
}
|
||||
});
|
||||
const $http = HTTP({
|
||||
headers: {
|
||||
'User-Agent': ua,
|
||||
},
|
||||
});
|
||||
|
||||
const result = new Promise((resolve, reject) => {
|
||||
$http.get(url).then((resp) => {
|
||||
const body = resp.body;
|
||||
if (body.replace(/\s/g, '').length === 0) reject(new Error('订阅内容为空!'));
|
||||
else resolve(body);
|
||||
});
|
||||
});
|
||||
const result = new Promise((resolve, reject) => {
|
||||
$http.get(url).then((resp) => {
|
||||
const body = resp.body;
|
||||
if (body.replace(/\s/g, '').length === 0)
|
||||
reject(new Error('订阅内容为空!'));
|
||||
else resolve(body);
|
||||
});
|
||||
});
|
||||
|
||||
cache[id] = result;
|
||||
return result;
|
||||
cache.set(id, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = download;
|
||||
|
||||
@@ -1,275 +1,285 @@
|
||||
const $ = require('../core/app');
|
||||
const { ENV } = require('./open-api');
|
||||
import { ENV } from './open-api';
|
||||
import $ from '../core/app';
|
||||
|
||||
function express({ port } = { port: 3000 }) {
|
||||
const { isNode } = ENV();
|
||||
const DEFAULT_HEADERS = {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'POST,GET,OPTIONS,PATCH,PUT,DELETE',
|
||||
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
|
||||
};
|
||||
export default function express({ port } = { port: 3000 }) {
|
||||
const { isNode } = ENV();
|
||||
const DEFAULT_HEADERS = {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'POST,GET,OPTIONS,PATCH,PUT,DELETE',
|
||||
'Access-Control-Allow-Headers':
|
||||
'Origin, X-Requested-With, Content-Type, Accept',
|
||||
};
|
||||
|
||||
// node support
|
||||
if (isNode) {
|
||||
const express_ = eval(`require("express")`);
|
||||
const bodyParser = eval(`require("body-parser")`);
|
||||
const app = express_();
|
||||
app.use(bodyParser.json({ verify: rawBodySaver }));
|
||||
app.use(bodyParser.urlencoded({ verify: rawBodySaver, extended: true }));
|
||||
app.use(bodyParser.raw({ verify: rawBodySaver, type: '*/*' }));
|
||||
app.use((_, res, next) => {
|
||||
res.set(DEFAULT_HEADERS);
|
||||
next();
|
||||
});
|
||||
// node support
|
||||
if (isNode) {
|
||||
const express_ = eval(`require("express")`);
|
||||
const bodyParser = eval(`require("body-parser")`);
|
||||
const app = express_();
|
||||
app.use(bodyParser.json({ verify: rawBodySaver }));
|
||||
app.use(
|
||||
bodyParser.urlencoded({ verify: rawBodySaver, extended: true }),
|
||||
);
|
||||
app.use(bodyParser.raw({ verify: rawBodySaver, type: '*/*' }));
|
||||
app.use((_, res, next) => {
|
||||
res.set(DEFAULT_HEADERS);
|
||||
next();
|
||||
});
|
||||
|
||||
// adapter
|
||||
app.start = () => {
|
||||
app.listen(port, () => {
|
||||
$.log(`Express started on port: ${port}`);
|
||||
});
|
||||
};
|
||||
return app;
|
||||
}
|
||||
// adapter
|
||||
app.start = () => {
|
||||
app.listen(port, () => {
|
||||
$.log(`Express started on port: ${port}`);
|
||||
});
|
||||
};
|
||||
return app;
|
||||
}
|
||||
|
||||
// route handlers
|
||||
const handlers = [];
|
||||
// route handlers
|
||||
const handlers = [];
|
||||
|
||||
// http methods
|
||||
const METHODS_NAMES = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', "HEAD'", 'ALL' ];
|
||||
// http methods
|
||||
const METHODS_NAMES = [
|
||||
'GET',
|
||||
'POST',
|
||||
'PUT',
|
||||
'DELETE',
|
||||
'PATCH',
|
||||
'OPTIONS',
|
||||
"HEAD'",
|
||||
'ALL',
|
||||
];
|
||||
|
||||
// dispatch url to route
|
||||
const dispatch = (request, start = 0) => {
|
||||
let { method, url, headers, body } = request;
|
||||
if (/json/i.test(headers['Content-Type'])) {
|
||||
body = JSON.parse(body);
|
||||
}
|
||||
// dispatch url to route
|
||||
const dispatch = (request, start = 0) => {
|
||||
let { method, url, headers, body } = request;
|
||||
if (/json/i.test(headers['Content-Type'])) {
|
||||
body = JSON.parse(body);
|
||||
}
|
||||
|
||||
method = method.toUpperCase();
|
||||
const { path, query } = extractURL(url);
|
||||
method = method.toUpperCase();
|
||||
const { path, query } = extractURL(url);
|
||||
|
||||
// pattern match
|
||||
let handler = null;
|
||||
let i;
|
||||
let longestMatchedPattern = 0;
|
||||
for (i = start; i < handlers.length; i++) {
|
||||
if (handlers[i].method === 'ALL' || method === handlers[i].method) {
|
||||
const { pattern } = handlers[i];
|
||||
if (patternMatched(pattern, path)) {
|
||||
if (pattern.split('/').length > longestMatchedPattern) {
|
||||
handler = handlers[i];
|
||||
longestMatchedPattern = pattern.split('/').length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handler) {
|
||||
// dispatch to next handler
|
||||
const next = () => {
|
||||
dispatch(method, url, i);
|
||||
};
|
||||
const req = {
|
||||
method,
|
||||
url,
|
||||
path,
|
||||
query,
|
||||
params: extractPathParams(handler.pattern, path),
|
||||
headers,
|
||||
body
|
||||
};
|
||||
const res = Response();
|
||||
const cb = handler.callback;
|
||||
// pattern match
|
||||
let handler = null;
|
||||
let i;
|
||||
let longestMatchedPattern = 0;
|
||||
for (i = start; i < handlers.length; i++) {
|
||||
if (handlers[i].method === 'ALL' || method === handlers[i].method) {
|
||||
const { pattern } = handlers[i];
|
||||
if (patternMatched(pattern, path)) {
|
||||
if (pattern.split('/').length > longestMatchedPattern) {
|
||||
handler = handlers[i];
|
||||
longestMatchedPattern = pattern.split('/').length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handler) {
|
||||
// dispatch to next handler
|
||||
const next = () => {
|
||||
dispatch(method, url, i);
|
||||
};
|
||||
const req = {
|
||||
method,
|
||||
url,
|
||||
path,
|
||||
query,
|
||||
params: extractPathParams(handler.pattern, path),
|
||||
headers,
|
||||
body,
|
||||
};
|
||||
const res = Response();
|
||||
const cb = handler.callback;
|
||||
|
||||
const errFunc = (err) => {
|
||||
res.status(500).json({
|
||||
status: 'failed',
|
||||
message: `Internal Server Error: ${err}`
|
||||
});
|
||||
};
|
||||
const errFunc = (err) => {
|
||||
res.status(500).json({
|
||||
status: 'failed',
|
||||
message: `Internal Server Error: ${err}`,
|
||||
});
|
||||
};
|
||||
|
||||
if (cb.constructor.name === 'AsyncFunction') {
|
||||
cb(req, res, next).catch(errFunc);
|
||||
} else {
|
||||
try {
|
||||
cb(req, res, next);
|
||||
} catch (err) {
|
||||
errFunc(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no route, return 404
|
||||
const res = Response();
|
||||
res.status(404).json({
|
||||
status: 'failed',
|
||||
message: 'ERROR: 404 not found'
|
||||
});
|
||||
}
|
||||
};
|
||||
if (cb.constructor.name === 'AsyncFunction') {
|
||||
cb(req, res, next).catch(errFunc);
|
||||
} else {
|
||||
try {
|
||||
cb(req, res, next);
|
||||
} catch (err) {
|
||||
errFunc(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no route, return 404
|
||||
const res = Response();
|
||||
res.status(404).json({
|
||||
status: 'failed',
|
||||
message: 'ERROR: 404 not found',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const app = {};
|
||||
const app = {};
|
||||
|
||||
// attach http methods
|
||||
METHODS_NAMES.forEach((method) => {
|
||||
app[method.toLowerCase()] = (pattern, callback) => {
|
||||
// add handler
|
||||
handlers.push({ method, pattern, callback });
|
||||
};
|
||||
});
|
||||
// attach http methods
|
||||
METHODS_NAMES.forEach((method) => {
|
||||
app[method.toLowerCase()] = (pattern, callback) => {
|
||||
// add handler
|
||||
handlers.push({ method, pattern, callback });
|
||||
};
|
||||
});
|
||||
|
||||
// chainable route
|
||||
app.route = (pattern) => {
|
||||
const chainApp = {};
|
||||
METHODS_NAMES.forEach((method) => {
|
||||
chainApp[method.toLowerCase()] = (callback) => {
|
||||
// add handler
|
||||
handlers.push({ method, pattern, callback });
|
||||
return chainApp;
|
||||
};
|
||||
});
|
||||
return chainApp;
|
||||
};
|
||||
// chainable route
|
||||
app.route = (pattern) => {
|
||||
const chainApp = {};
|
||||
METHODS_NAMES.forEach((method) => {
|
||||
chainApp[method.toLowerCase()] = (callback) => {
|
||||
// add handler
|
||||
handlers.push({ method, pattern, callback });
|
||||
return chainApp;
|
||||
};
|
||||
});
|
||||
return chainApp;
|
||||
};
|
||||
|
||||
// start service
|
||||
app.start = () => {
|
||||
dispatch($request);
|
||||
};
|
||||
// start service
|
||||
app.start = () => {
|
||||
dispatch($request);
|
||||
};
|
||||
|
||||
return app;
|
||||
return app;
|
||||
|
||||
/************************************************
|
||||
/************************************************
|
||||
Utility Functions
|
||||
*************************************************/
|
||||
function rawBodySaver(req, res, buf, encoding) {
|
||||
if (buf && buf.length) {
|
||||
req.rawBody = buf.toString(encoding || 'utf8');
|
||||
}
|
||||
}
|
||||
function rawBodySaver(req, res, buf, encoding) {
|
||||
if (buf && buf.length) {
|
||||
req.rawBody = buf.toString(encoding || 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
function Response() {
|
||||
let statusCode = 200;
|
||||
const { isQX, isLoon, isSurge } = ENV();
|
||||
const headers = DEFAULT_HEADERS;
|
||||
const STATUS_CODE_MAP = {
|
||||
200: 'HTTP/1.1 200 OK',
|
||||
201: 'HTTP/1.1 201 Created',
|
||||
302: 'HTTP/1.1 302 Found',
|
||||
307: 'HTTP/1.1 307 Temporary Redirect',
|
||||
308: 'HTTP/1.1 308 Permanent Redirect',
|
||||
404: 'HTTP/1.1 404 Not Found',
|
||||
500: 'HTTP/1.1 500 Internal Server Error'
|
||||
};
|
||||
return new class {
|
||||
status(code) {
|
||||
statusCode = code;
|
||||
return this;
|
||||
}
|
||||
function Response() {
|
||||
let statusCode = 200;
|
||||
const { isQX, isLoon, isSurge } = ENV();
|
||||
const headers = DEFAULT_HEADERS;
|
||||
const STATUS_CODE_MAP = {
|
||||
200: 'HTTP/1.1 200 OK',
|
||||
201: 'HTTP/1.1 201 Created',
|
||||
302: 'HTTP/1.1 302 Found',
|
||||
307: 'HTTP/1.1 307 Temporary Redirect',
|
||||
308: 'HTTP/1.1 308 Permanent Redirect',
|
||||
404: 'HTTP/1.1 404 Not Found',
|
||||
500: 'HTTP/1.1 500 Internal Server Error',
|
||||
};
|
||||
return new (class {
|
||||
status(code) {
|
||||
statusCode = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
send(body = '') {
|
||||
const response = {
|
||||
status: isQX ? STATUS_CODE_MAP[statusCode] : statusCode,
|
||||
body,
|
||||
headers
|
||||
};
|
||||
if (isQX) {
|
||||
$done(response);
|
||||
} else if (isLoon || isSurge) {
|
||||
$done({
|
||||
response
|
||||
});
|
||||
}
|
||||
}
|
||||
send(body = '') {
|
||||
const response = {
|
||||
status: isQX ? STATUS_CODE_MAP[statusCode] : statusCode,
|
||||
body,
|
||||
headers,
|
||||
};
|
||||
if (isQX) {
|
||||
$done(response);
|
||||
} else if (isLoon || isSurge) {
|
||||
$done({
|
||||
response,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
this.send();
|
||||
}
|
||||
end() {
|
||||
this.send();
|
||||
}
|
||||
|
||||
html(data) {
|
||||
this.set('Content-Type', 'text/html;charset=UTF-8');
|
||||
this.send(data);
|
||||
}
|
||||
html(data) {
|
||||
this.set('Content-Type', 'text/html;charset=UTF-8');
|
||||
this.send(data);
|
||||
}
|
||||
|
||||
json(data) {
|
||||
this.set('Content-Type', 'application/json;charset=UTF-8');
|
||||
this.send(JSON.stringify(data));
|
||||
}
|
||||
json(data) {
|
||||
this.set('Content-Type', 'application/json;charset=UTF-8');
|
||||
this.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
set(key, val) {
|
||||
headers[key] = val;
|
||||
return this;
|
||||
}
|
||||
}();
|
||||
}
|
||||
set(key, val) {
|
||||
headers[key] = val;
|
||||
return this;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function patternMatched(pattern, path) {
|
||||
if (pattern instanceof RegExp && pattern.test(path)) {
|
||||
return true;
|
||||
} else {
|
||||
// root pattern, match all
|
||||
if (pattern === '/') return true;
|
||||
// normal string pattern
|
||||
if (pattern.indexOf(':') === -1) {
|
||||
const spath = path.split('/');
|
||||
const spattern = pattern.split('/');
|
||||
for (let i = 0; i < spattern.length; i++) {
|
||||
if (spath[i] !== spattern[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (extractPathParams(pattern, path)) {
|
||||
// string pattern with path parameters
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function patternMatched(pattern, path) {
|
||||
if (pattern instanceof RegExp && pattern.test(path)) {
|
||||
return true;
|
||||
} else {
|
||||
// root pattern, match all
|
||||
if (pattern === '/') return true;
|
||||
// normal string pattern
|
||||
if (pattern.indexOf(':') === -1) {
|
||||
const spath = path.split('/');
|
||||
const spattern = pattern.split('/');
|
||||
for (let i = 0; i < spattern.length; i++) {
|
||||
if (spath[i] !== spattern[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (extractPathParams(pattern, path)) {
|
||||
// string pattern with path parameters
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function extractURL(url) {
|
||||
// extract path
|
||||
const match = url.match(/https?:\/\/[^\/]+(\/[^?]*)/) || [];
|
||||
const path = match[1] || '/';
|
||||
function extractURL(url) {
|
||||
// extract path
|
||||
const match = url.match(/https?:\/\/[^\/]+(\/[^?]*)/) || [];
|
||||
const path = match[1] || '/';
|
||||
|
||||
// extract query string
|
||||
const split = url.indexOf('?');
|
||||
const query = {};
|
||||
if (split !== -1) {
|
||||
let hashes = url.slice(url.indexOf('?') + 1).split('&');
|
||||
for (let i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i].split('=');
|
||||
query[hash[0]] = hash[1];
|
||||
}
|
||||
}
|
||||
return {
|
||||
path,
|
||||
query
|
||||
};
|
||||
}
|
||||
// extract query string
|
||||
const split = url.indexOf('?');
|
||||
const query = {};
|
||||
if (split !== -1) {
|
||||
let hashes = url.slice(url.indexOf('?') + 1).split('&');
|
||||
for (let i = 0; i < hashes.length; i++) {
|
||||
const hash = hashes[i].split('=');
|
||||
query[hash[0]] = hash[1];
|
||||
}
|
||||
}
|
||||
return {
|
||||
path,
|
||||
query,
|
||||
};
|
||||
}
|
||||
|
||||
function extractPathParams(pattern, path) {
|
||||
if (pattern.indexOf(':') === -1) {
|
||||
return null;
|
||||
} else {
|
||||
const params = {};
|
||||
for (let i = 0, j = 0; i < pattern.length; i++, j++) {
|
||||
if (pattern[i] === ':') {
|
||||
let key = [];
|
||||
let val = [];
|
||||
while (pattern[++i] !== '/' && i < pattern.length) {
|
||||
key.push(pattern[i]);
|
||||
}
|
||||
while (path[j] !== '/' && j < path.length) {
|
||||
val.push(path[j++]);
|
||||
}
|
||||
params[key.join('')] = val.join('');
|
||||
} else {
|
||||
if (pattern[i] !== path[j]) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
function extractPathParams(pattern, path) {
|
||||
if (pattern.indexOf(':') === -1) {
|
||||
return null;
|
||||
} else {
|
||||
const params = {};
|
||||
for (let i = 0, j = 0; i < pattern.length; i++, j++) {
|
||||
if (pattern[i] === ':') {
|
||||
let key = [];
|
||||
let val = [];
|
||||
while (pattern[++i] !== '/' && i < pattern.length) {
|
||||
key.push(pattern[i]);
|
||||
}
|
||||
while (path[j] !== '/' && j < path.length) {
|
||||
val.push(path[j++]);
|
||||
}
|
||||
params[key.join('')] = val.join('');
|
||||
} else {
|
||||
if (pattern[i] !== path[j]) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = express;
|
||||
|
||||
@@ -1,209 +1,319 @@
|
||||
const { HTTP } = require('./open-api');
|
||||
import { HTTP } from './open-api';
|
||||
|
||||
// get proxy flag according to its name
|
||||
function getFlag(name) {
|
||||
// flags from @KOP-XIAO: https://github.com/KOP-XIAO/QuantumultX/blob/master/Scripts/resource-parser.js
|
||||
const flags = {
|
||||
'🇦🇿': [ '阿塞拜疆' ],
|
||||
'🇦🇹': [ '奥地利', '奧地利', 'Austria', '维也纳' ],
|
||||
'🇦🇺': [ 'AU', 'Australia', 'Sydney', '澳大利亚', '澳洲', '墨尔本', '悉尼', '土澳', '京澳', '廣澳', '滬澳', '沪澳', '广澳' ],
|
||||
'🇧🇪': [ 'BE', '比利時', '比利时' ],
|
||||
'🇧🇬': [ '保加利亚', '保加利亞', 'Bulgaria' ],
|
||||
'🇧🇭': [ 'BH', '巴林' ],
|
||||
'🇧🇩': [ 'BD', '孟加拉' ],
|
||||
'🇵🇰': [ '巴基斯坦' ],
|
||||
'🇰🇭': [ '柬埔寨' ],
|
||||
'🇺🇦': [ '烏克蘭', '乌克兰' ],
|
||||
'🇭🇷': [ '克罗地亚', 'HR', '克羅地亞' ],
|
||||
'🇨🇦': [ 'Canada', 'CANADA', 'CAN', 'Waterloo', '加拿大', '蒙特利尔', '温哥华', '楓葉', '枫叶', '滑铁卢', '多伦多', 'CA' ],
|
||||
'🇨🇭': [ '瑞士', '苏黎世', 'Switzerland', 'Zurich' ],
|
||||
'🇳🇬': [ '尼日利亚', 'NG', '尼日利亞' ],
|
||||
'🇨🇿': [ 'Czechia', '捷克' ],
|
||||
'🇸🇰': [ '斯洛伐克', 'SK' ],
|
||||
'🇷🇸': [ 'RS', '塞尔维亚' ],
|
||||
'🇲🇩': [ '摩爾多瓦', 'MD', '摩尔多瓦' ],
|
||||
'🇩🇪': [ 'DE', 'German', 'GERMAN', '德国', '德國', '法兰克福', '京德', '滬德', '廣德', '沪德', '广德', 'Frankfurt' ],
|
||||
'🇩🇰': [ 'DK', 'DNK', '丹麦', '丹麥' ],
|
||||
'🇪🇸': [ 'ES', '西班牙', 'Spain' ],
|
||||
'🇪🇺': [ 'EU', '欧盟', '欧罗巴' ],
|
||||
'🇫🇮': [ 'Finland', '芬兰', '芬蘭', '赫尔辛基' ],
|
||||
'🇫🇷': [ 'FR', 'France', '法国', '法國', '巴黎' ],
|
||||
'🇬🇧': [ 'UK', 'GB', 'England', 'United Kingdom', '英国', '伦敦', '英', 'London' ],
|
||||
'🇲🇴': [ 'MO', 'Macao', '澳门', '澳門', 'CTM' ],
|
||||
'🇰🇿': [ '哈萨克斯坦', '哈萨克' ],
|
||||
'🇭🇺': [ '匈牙利', 'Hungary' ],
|
||||
'🇭🇰': [
|
||||
'HK',
|
||||
'Hongkong',
|
||||
'Hong Kong',
|
||||
'HongKong',
|
||||
'HONG KONG',
|
||||
'香港',
|
||||
'深港',
|
||||
'沪港',
|
||||
'呼港',
|
||||
'HKT',
|
||||
'HKBN',
|
||||
'HGC',
|
||||
'WTT',
|
||||
'CMI',
|
||||
'穗港',
|
||||
'京港',
|
||||
'港'
|
||||
],
|
||||
'🇮🇩': [ 'Indonesia', '印尼', '印度尼西亚', '雅加达' ],
|
||||
'🇮🇪': [ 'Ireland', 'IRELAND', '爱尔兰', '愛爾蘭', '都柏林' ],
|
||||
'🇮🇱': [ 'Israel', '以色列' ],
|
||||
'🇮🇳': [ 'India', 'IND', 'INDIA', '印度', '孟买', 'MFumbai' ],
|
||||
'🇮🇸': [ 'IS', 'ISL', '冰岛', '冰島' ],
|
||||
'🇰🇵': [ 'KP', '朝鲜' ],
|
||||
'🇰🇷': [ 'KR', 'Korea', 'KOR', '韩国', '首尔', '韩', '韓', '春川', 'Chuncheon', 'Seoul' ],
|
||||
'🇱🇺': [ '卢森堡' ],
|
||||
'🇱🇻': [ 'Latvia', 'Latvija', '拉脱维亚' ],
|
||||
'🇲🇽': [ 'MEX', 'MX', '墨西哥' ],
|
||||
'🇲🇾': [ 'MY', 'Malaysia', 'MALAYSIA', '马来西亚', '大馬', '馬來西亞', '吉隆坡' ],
|
||||
'🇳🇱': [ 'NL', 'Netherlands', '荷兰', '荷蘭', '尼德蘭', '阿姆斯特丹' ],
|
||||
'🇳🇵': [ '尼泊尔' ],
|
||||
'🇵🇭': [ 'PH', 'Philippines', '菲律宾', '菲律賓' ],
|
||||
'🇵🇷': [ 'PR', '波多黎各' ],
|
||||
'🇷🇴': [ 'RO', '罗马尼亚' ],
|
||||
'🇷🇺': [
|
||||
'RU',
|
||||
'Russia',
|
||||
'俄罗斯',
|
||||
'俄国',
|
||||
'俄羅斯',
|
||||
'伯力',
|
||||
'莫斯科',
|
||||
'圣彼得堡',
|
||||
'西伯利亚',
|
||||
'新西伯利亚',
|
||||
'京俄',
|
||||
'杭俄',
|
||||
'廣俄',
|
||||
'滬俄',
|
||||
'广俄',
|
||||
'沪俄',
|
||||
'Moscow'
|
||||
],
|
||||
'🇸🇦': [ '沙特' ],
|
||||
'🇸🇪': [ 'SE', 'Sweden', '瑞典' ],
|
||||
'🇲🇹': [ '马耳他' ],
|
||||
'🇲🇦': [ 'MA', '摩洛哥' ],
|
||||
'🇸🇬': [ 'SG', 'Singapore', 'SINGAPORE', '新加坡', '狮城', '沪新', '京新', '泉新', '穗新', '深新', '杭新', '广新', '廣新', '滬新' ],
|
||||
'🇹🇭': [ 'TH', 'Thailand', '泰国', '泰國', '曼谷' ],
|
||||
'🇹🇷': [ 'TR', 'Turkey', '土耳其', '伊斯坦布尔' ],
|
||||
'🇹🇼': [ 'TW', 'Taiwan', 'TAIWAN', '台湾', '台北', '台中', '新北', '彰化', 'CHT', '台', 'HINET', 'Taipei' ],
|
||||
'🇺🇸': [
|
||||
'US',
|
||||
'USA',
|
||||
'America',
|
||||
'United States',
|
||||
'美国',
|
||||
'美',
|
||||
'京美',
|
||||
'波特兰',
|
||||
'达拉斯',
|
||||
'俄勒冈',
|
||||
'凤凰城',
|
||||
'费利蒙',
|
||||
'硅谷',
|
||||
'矽谷',
|
||||
'拉斯维加斯',
|
||||
'洛杉矶',
|
||||
'圣何塞',
|
||||
'圣克拉拉',
|
||||
'西雅图',
|
||||
'芝加哥',
|
||||
'沪美',
|
||||
'哥伦布',
|
||||
'纽约',
|
||||
'Los Angeles',
|
||||
'San Jose',
|
||||
'Sillicon Valley',
|
||||
'Michigan'
|
||||
],
|
||||
'🇻🇳': [ 'VN', '越南', '胡志明市' ],
|
||||
'🇻🇪': [ 'VE', '委内瑞拉' ],
|
||||
'🇮🇹': [ 'Italy', 'IT', 'Nachash', '意大利', '米兰', '義大利' ],
|
||||
'🇿🇦': [ 'South Africa', '南非' ],
|
||||
'🇦🇪': [ 'United Arab Emirates', '阿联酋', '迪拜', 'AE' ],
|
||||
'🇧🇷': [ 'BR', 'Brazil', '巴西', '圣保罗' ],
|
||||
'🇯🇵': [
|
||||
'JP',
|
||||
'Japan',
|
||||
'JAPAN',
|
||||
'日本',
|
||||
'东京',
|
||||
'大阪',
|
||||
'埼玉',
|
||||
'沪日',
|
||||
'穗日',
|
||||
'川日',
|
||||
'中日',
|
||||
'泉日',
|
||||
'杭日',
|
||||
'深日',
|
||||
'辽日',
|
||||
'广日',
|
||||
'大坂',
|
||||
'Osaka',
|
||||
'Tokyo'
|
||||
],
|
||||
'🇦🇷': [ 'AR', '阿根廷' ],
|
||||
'🇳🇴': [ 'Norway', '挪威', 'NO' ],
|
||||
'🇨🇳': [ 'CN', 'China', '回国', '中国', '中國', '江苏', '北京', '上海', '广州', '深圳', '杭州', '徐州', '青岛', '宁波', '镇江', 'back' ],
|
||||
'🇵🇱': [ 'PL', 'POL', '波兰', '波蘭' ],
|
||||
'🇨🇱': [ '智利' ],
|
||||
'🇳🇿': [ '新西蘭', '新西兰' ],
|
||||
'🇬🇷': [ '希腊', '希臘' ],
|
||||
'🇪🇬': [ '埃及' ],
|
||||
'🇨🇾': [ 'CY', '塞浦路斯' ],
|
||||
'🇨🇷': [ 'CR', '哥斯达黎加' ],
|
||||
'🇸🇮': [ 'SI', '斯洛文尼亚' ],
|
||||
'🇱🇹': [ 'LT', '立陶宛' ],
|
||||
'🇵🇦': [ 'PA', '巴拿马' ],
|
||||
'🇹🇳': [ 'TN', '突尼斯' ],
|
||||
'🇮🇲': [ '马恩岛', '馬恩島' ],
|
||||
'🇧🇾': [ 'BY', '白俄', '白俄罗斯' ],
|
||||
'🇵🇹': [ '葡萄牙' ],
|
||||
'🇰🇪': [ 'KE', '肯尼亚' ],
|
||||
'🇰🇬': [ 'KG', '吉尔吉斯坦' ],
|
||||
'🇯🇴': [ 'JO', '约旦' ],
|
||||
'🇺🇾': [ 'UY', '乌拉圭' ],
|
||||
'🇲🇳': [ '蒙古' ],
|
||||
'🇮🇷': [ 'IR', '伊朗' ],
|
||||
'🇵🇪': [ '秘鲁', '祕魯' ],
|
||||
'🇨🇴': [ '哥伦比亚' ],
|
||||
'🇪🇪': [ '爱沙尼亚' ],
|
||||
'🇪🇨': [ 'EC', '厄瓜多尔' ],
|
||||
'🇲🇰': [ '马其顿', '馬其頓' ],
|
||||
'🇧🇦': [ '波黑共和国', '波黑' ],
|
||||
'🇬🇪': [ '格魯吉亞', '格鲁吉亚' ],
|
||||
'🇦🇱': [ '阿爾巴尼亞', '阿尔巴尼亚' ],
|
||||
'🏳️🌈': [ '流量', '时间', '应急', '过期', 'Bandwidth', 'expire' ]
|
||||
};
|
||||
for (let k of Object.keys(flags)) {
|
||||
if (flags[k].some((item) => name.indexOf(item) !== -1)) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
// no flag found
|
||||
const oldFlag = (name.match(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/) || [])[0];
|
||||
return oldFlag || '🏴☠️';
|
||||
export function getFlag(name) {
|
||||
// flags from @KOP-XIAO: https://github.com/KOP-XIAO/QuantumultX/blob/master/Scripts/resource-parser.js
|
||||
const flags = {
|
||||
'🇦🇿': ['阿塞拜疆'],
|
||||
'🇦🇹': ['奥地利', '奧地利', 'Austria', '维也纳'],
|
||||
'🇦🇺': [
|
||||
'AU',
|
||||
'Australia',
|
||||
'Sydney',
|
||||
'澳大利亚',
|
||||
'澳洲',
|
||||
'墨尔本',
|
||||
'悉尼',
|
||||
'土澳',
|
||||
'京澳',
|
||||
'廣澳',
|
||||
'滬澳',
|
||||
'沪澳',
|
||||
'广澳',
|
||||
],
|
||||
'🇧🇪': ['BE', '比利時', '比利时'],
|
||||
'🇧🇬': ['保加利亚', '保加利亞', 'Bulgaria'],
|
||||
'🇧🇭': ['BH', '巴林'],
|
||||
'🇧🇩': ['BD', '孟加拉'],
|
||||
'🇵🇰': ['巴基斯坦'],
|
||||
'🇰🇭': ['柬埔寨'],
|
||||
'🇺🇦': ['烏克蘭', '乌克兰'],
|
||||
'🇭🇷': ['克罗地亚', 'HR', '克羅地亞'],
|
||||
'🇨🇦': [
|
||||
'Canada',
|
||||
'CANADA',
|
||||
'CAN',
|
||||
'Waterloo',
|
||||
'加拿大',
|
||||
'蒙特利尔',
|
||||
'温哥华',
|
||||
'楓葉',
|
||||
'枫叶',
|
||||
'滑铁卢',
|
||||
'多伦多',
|
||||
'CA',
|
||||
],
|
||||
'🇨🇭': ['瑞士', '苏黎世', 'Switzerland', 'Zurich'],
|
||||
'🇳🇬': ['尼日利亚', 'NG', '尼日利亞'],
|
||||
'🇨🇿': ['Czechia', '捷克'],
|
||||
'🇸🇰': ['斯洛伐克', 'SK'],
|
||||
'🇷🇸': ['RS', '塞尔维亚'],
|
||||
'🇲🇩': ['摩爾多瓦', 'MD', '摩尔多瓦'],
|
||||
'🇩🇪': [
|
||||
'DE',
|
||||
'German',
|
||||
'GERMAN',
|
||||
'德国',
|
||||
'德國',
|
||||
'法兰克福',
|
||||
'京德',
|
||||
'滬德',
|
||||
'廣德',
|
||||
'沪德',
|
||||
'广德',
|
||||
'Frankfurt',
|
||||
],
|
||||
'🇩🇰': ['DK', 'DNK', '丹麦', '丹麥'],
|
||||
'🇪🇸': ['ES', '西班牙', 'Spain'],
|
||||
'🇪🇺': ['EU', '欧盟', '欧罗巴'],
|
||||
'🇫🇮': ['Finland', '芬兰', '芬蘭', '赫尔辛基'],
|
||||
'🇫🇷': ['FR', 'France', '法国', '法國', '巴黎'],
|
||||
'🇬🇧': [
|
||||
'UK',
|
||||
'GB',
|
||||
'England',
|
||||
'United Kingdom',
|
||||
'英国',
|
||||
'伦敦',
|
||||
'英',
|
||||
'London',
|
||||
],
|
||||
'🇲🇴': ['MO', 'Macao', '澳门', '澳門', 'CTM'],
|
||||
'🇰🇿': ['哈萨克斯坦', '哈萨克'],
|
||||
'🇭🇺': ['匈牙利', 'Hungary'],
|
||||
'🇭🇰': [
|
||||
'HK',
|
||||
'Hongkong',
|
||||
'Hong Kong',
|
||||
'HongKong',
|
||||
'HONG KONG',
|
||||
'香港',
|
||||
'深港',
|
||||
'沪港',
|
||||
'呼港',
|
||||
'HKT',
|
||||
'HKBN',
|
||||
'HGC',
|
||||
'WTT',
|
||||
'CMI',
|
||||
'穗港',
|
||||
'京港',
|
||||
'港',
|
||||
],
|
||||
'🇮🇩': ['Indonesia', '印尼', '印度尼西亚', '雅加达'],
|
||||
'🇮🇪': ['Ireland', 'IRELAND', '爱尔兰', '愛爾蘭', '都柏林'],
|
||||
'🇮🇱': ['Israel', '以色列'],
|
||||
'🇮🇳': ['India', 'IND', 'INDIA', '印度', '孟买', 'MFumbai'],
|
||||
'🇮🇸': ['IS', 'ISL', '冰岛', '冰島'],
|
||||
'🇰🇵': ['KP', '朝鲜'],
|
||||
'🇰🇷': [
|
||||
'KR',
|
||||
'Korea',
|
||||
'KOR',
|
||||
'韩国',
|
||||
'首尔',
|
||||
'韩',
|
||||
'韓',
|
||||
'春川',
|
||||
'Chuncheon',
|
||||
'Seoul',
|
||||
],
|
||||
'🇱🇺': ['卢森堡'],
|
||||
'🇱🇻': ['Latvia', 'Latvija', '拉脱维亚'],
|
||||
'🇲🇽': ['MEX', 'MX', '墨西哥'],
|
||||
'🇲🇾': [
|
||||
'MY',
|
||||
'Malaysia',
|
||||
'MALAYSIA',
|
||||
'马来西亚',
|
||||
'大馬',
|
||||
'馬來西亞',
|
||||
'吉隆坡',
|
||||
],
|
||||
'🇳🇱': ['NL', 'Netherlands', '荷兰', '荷蘭', '尼德蘭', '阿姆斯特丹'],
|
||||
'🇳🇵': ['尼泊尔'],
|
||||
'🇵🇭': ['PH', 'Philippines', '菲律宾', '菲律賓'],
|
||||
'🇵🇷': ['PR', '波多黎各'],
|
||||
'🇷🇴': ['RO', '罗马尼亚'],
|
||||
'🇷🇺': [
|
||||
'RU',
|
||||
'Russia',
|
||||
'俄罗斯',
|
||||
'俄国',
|
||||
'俄羅斯',
|
||||
'伯力',
|
||||
'莫斯科',
|
||||
'圣彼得堡',
|
||||
'西伯利亚',
|
||||
'新西伯利亚',
|
||||
'京俄',
|
||||
'杭俄',
|
||||
'廣俄',
|
||||
'滬俄',
|
||||
'广俄',
|
||||
'沪俄',
|
||||
'Moscow',
|
||||
],
|
||||
'🇸🇦': ['沙特'],
|
||||
'🇸🇪': ['SE', 'Sweden', '瑞典'],
|
||||
'🇲🇹': ['马耳他'],
|
||||
'🇲🇦': ['MA', '摩洛哥'],
|
||||
'🇸🇬': [
|
||||
'SG',
|
||||
'Singapore',
|
||||
'SINGAPORE',
|
||||
'新加坡',
|
||||
'狮城',
|
||||
'沪新',
|
||||
'京新',
|
||||
'泉新',
|
||||
'穗新',
|
||||
'深新',
|
||||
'杭新',
|
||||
'广新',
|
||||
'廣新',
|
||||
'滬新',
|
||||
],
|
||||
'🇹🇭': ['TH', 'Thailand', '泰国', '泰國', '曼谷'],
|
||||
'🇹🇷': ['TR', 'Turkey', '土耳其', '伊斯坦布尔'],
|
||||
'🇹🇼': [
|
||||
'TW',
|
||||
'Taiwan',
|
||||
'TAIWAN',
|
||||
'台湾',
|
||||
'台北',
|
||||
'台中',
|
||||
'新北',
|
||||
'彰化',
|
||||
'CHT',
|
||||
'台',
|
||||
'HINET',
|
||||
'Taipei',
|
||||
],
|
||||
'🇺🇸': [
|
||||
'US',
|
||||
'USA',
|
||||
'America',
|
||||
'United States',
|
||||
'美国',
|
||||
'美',
|
||||
'京美',
|
||||
'波特兰',
|
||||
'达拉斯',
|
||||
'俄勒冈',
|
||||
'凤凰城',
|
||||
'费利蒙',
|
||||
'硅谷',
|
||||
'矽谷',
|
||||
'拉斯维加斯',
|
||||
'洛杉矶',
|
||||
'圣何塞',
|
||||
'圣克拉拉',
|
||||
'西雅图',
|
||||
'芝加哥',
|
||||
'沪美',
|
||||
'哥伦布',
|
||||
'纽约',
|
||||
'Los Angeles',
|
||||
'San Jose',
|
||||
'Sillicon Valley',
|
||||
'Michigan',
|
||||
],
|
||||
'🇻🇳': ['VN', '越南', '胡志明市'],
|
||||
'🇻🇪': ['VE', '委内瑞拉'],
|
||||
'🇮🇹': ['Italy', 'IT', 'Nachash', '意大利', '米兰', '義大利'],
|
||||
'🇿🇦': ['South Africa', '南非'],
|
||||
'🇦🇪': ['United Arab Emirates', '阿联酋', '迪拜', 'AE'],
|
||||
'🇧🇷': ['BR', 'Brazil', '巴西', '圣保罗'],
|
||||
'🇯🇵': [
|
||||
'JP',
|
||||
'Japan',
|
||||
'JAPAN',
|
||||
'日本',
|
||||
'东京',
|
||||
'大阪',
|
||||
'埼玉',
|
||||
'沪日',
|
||||
'穗日',
|
||||
'川日',
|
||||
'中日',
|
||||
'泉日',
|
||||
'杭日',
|
||||
'深日',
|
||||
'辽日',
|
||||
'广日',
|
||||
'大坂',
|
||||
'Osaka',
|
||||
'Tokyo',
|
||||
],
|
||||
'🇦🇷': ['AR', '阿根廷'],
|
||||
'🇳🇴': ['Norway', '挪威', 'NO'],
|
||||
'🇨🇳': [
|
||||
'CN',
|
||||
'China',
|
||||
'回国',
|
||||
'中国',
|
||||
'中國',
|
||||
'江苏',
|
||||
'北京',
|
||||
'上海',
|
||||
'广州',
|
||||
'深圳',
|
||||
'杭州',
|
||||
'徐州',
|
||||
'青岛',
|
||||
'宁波',
|
||||
'镇江',
|
||||
'back',
|
||||
],
|
||||
'🇵🇱': ['PL', 'POL', '波兰', '波蘭'],
|
||||
'🇨🇱': ['智利'],
|
||||
'🇳🇿': ['新西蘭', '新西兰'],
|
||||
'🇬🇷': ['希腊', '希臘'],
|
||||
'🇪🇬': ['埃及'],
|
||||
'🇨🇾': ['CY', '塞浦路斯'],
|
||||
'🇨🇷': ['CR', '哥斯达黎加'],
|
||||
'🇸🇮': ['SI', '斯洛文尼亚'],
|
||||
'🇱🇹': ['LT', '立陶宛'],
|
||||
'🇵🇦': ['PA', '巴拿马'],
|
||||
'🇹🇳': ['TN', '突尼斯'],
|
||||
'🇮🇲': ['马恩岛', '馬恩島'],
|
||||
'🇧🇾': ['BY', '白俄', '白俄罗斯'],
|
||||
'🇵🇹': ['葡萄牙'],
|
||||
'🇰🇪': ['KE', '肯尼亚'],
|
||||
'🇰🇬': ['KG', '吉尔吉斯坦'],
|
||||
'🇯🇴': ['JO', '约旦'],
|
||||
'🇺🇾': ['UY', '乌拉圭'],
|
||||
'🇲🇳': ['蒙古'],
|
||||
'🇮🇷': ['IR', '伊朗'],
|
||||
'🇵🇪': ['秘鲁', '祕魯'],
|
||||
'🇨🇴': ['哥伦比亚'],
|
||||
'🇪🇪': ['爱沙尼亚'],
|
||||
'🇪🇨': ['EC', '厄瓜多尔'],
|
||||
'🇲🇰': ['马其顿', '馬其頓'],
|
||||
'🇧🇦': ['波黑共和国', '波黑'],
|
||||
'🇬🇪': ['格魯吉亞', '格鲁吉亚'],
|
||||
'🇦🇱': ['阿爾巴尼亞', '阿尔巴尼亚'],
|
||||
'🏳️🌈': ['流量', '时间', '应急', '过期', 'Bandwidth', 'expire'],
|
||||
};
|
||||
for (let k of Object.keys(flags)) {
|
||||
if (flags[k].some((item) => name.indexOf(item) !== -1)) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
// no flag found
|
||||
const oldFlag = (name.match(
|
||||
/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/,
|
||||
) || [])[0];
|
||||
return oldFlag || '🏴☠️';
|
||||
}
|
||||
|
||||
// util API
|
||||
async function IP_API(req, res) {
|
||||
const server = decodeURIComponent(req.params.server);
|
||||
const $http = HTTP();
|
||||
const result = await $http
|
||||
.get(`http://ip-api.com/json/${server}?lang=zh-CN`)
|
||||
.then((resp) => JSON.parse(resp.body));
|
||||
res.json(result);
|
||||
export async function IP_API(req, res) {
|
||||
const server = decodeURIComponent(req.params.server);
|
||||
const $http = HTTP();
|
||||
const result = await $http
|
||||
.get(`http://ip-api.com/json/${server}?lang=zh-CN`)
|
||||
.then((resp) => JSON.parse(resp.body));
|
||||
res.json(result);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getFlag,
|
||||
IP_API
|
||||
};
|
||||
|
||||
@@ -1,75 +1,77 @@
|
||||
const { HTTP } = require('./open-api');
|
||||
import { HTTP } from './open-api';
|
||||
|
||||
/**
|
||||
* Gist backup
|
||||
*/
|
||||
function Gist({ token, key }) {
|
||||
const http = HTTP({
|
||||
baseURL: 'https://api.github.com',
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36'
|
||||
},
|
||||
events: {
|
||||
onResponse: (resp) => {
|
||||
if (/^[45]/.test(String(resp.statusCode))) {
|
||||
return Promise.reject(`ERROR: ${JSON.parse(resp.body).message}`);
|
||||
} else {
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
export default function Gist({ token, key }) {
|
||||
const http = HTTP({
|
||||
baseURL: 'https://api.github.com',
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36',
|
||||
},
|
||||
events: {
|
||||
onResponse: (resp) => {
|
||||
if (/^[45]/.test(String(resp.statusCode))) {
|
||||
return Promise.reject(
|
||||
`ERROR: ${JSON.parse(resp.body).message}`,
|
||||
);
|
||||
} else {
|
||||
return resp;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
async function locate() {
|
||||
return http.get('/gists').then((response) => {
|
||||
const gists = JSON.parse(response.body);
|
||||
for (let g of gists) {
|
||||
if (g.description === key) {
|
||||
return g.id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
async function locate() {
|
||||
return http.get('/gists').then((response) => {
|
||||
const gists = JSON.parse(response.body);
|
||||
for (let g of gists) {
|
||||
if (g.description === key) {
|
||||
return g.id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
|
||||
this.upload = async function(files) {
|
||||
const id = await locate();
|
||||
this.upload = async function (files) {
|
||||
const id = await locate();
|
||||
|
||||
if (id === -1) {
|
||||
// create a new gist for backup
|
||||
return http.post({
|
||||
url: '/gists',
|
||||
body: JSON.stringify({
|
||||
description: key,
|
||||
public: false,
|
||||
files
|
||||
})
|
||||
});
|
||||
} else {
|
||||
// update an existing gist
|
||||
return http.patch({
|
||||
url: `/gists/${id}`,
|
||||
body: JSON.stringify({ files })
|
||||
});
|
||||
}
|
||||
};
|
||||
if (id === -1) {
|
||||
// create a new gist for backup
|
||||
return http.post({
|
||||
url: '/gists',
|
||||
body: JSON.stringify({
|
||||
description: key,
|
||||
public: false,
|
||||
files,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
// update an existing gist
|
||||
return http.patch({
|
||||
url: `/gists/${id}`,
|
||||
body: JSON.stringify({ files }),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.download = async function(filename) {
|
||||
const id = await locate();
|
||||
if (id === -1) {
|
||||
return Promise.reject('未找到Gist备份!');
|
||||
} else {
|
||||
try {
|
||||
const { files } = await http.get(`/gists/${id}`).then((resp) => JSON.parse(resp.body));
|
||||
const url = files[filename].raw_url;
|
||||
return await http.get(url).then((resp) => resp.body);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.download = async function (filename) {
|
||||
const id = await locate();
|
||||
if (id === -1) {
|
||||
return Promise.reject('未找到Gist备份!');
|
||||
} else {
|
||||
try {
|
||||
const { files } = await http
|
||||
.get(`/gists/${id}`)
|
||||
.then((resp) => JSON.parse(resp.body));
|
||||
const url = files[filename].raw_url;
|
||||
return await http.get(url).then((resp) => resp.body);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Gist;
|
||||
|
||||
@@ -14,9 +14,4 @@ function FULL(length, bool) {
|
||||
return [...Array(length).keys()].map(() => bool);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AND,
|
||||
OR,
|
||||
NOT,
|
||||
FULL
|
||||
}
|
||||
export { AND, OR, NOT, FULL };
|
||||
|
||||
@@ -1,271 +1,318 @@
|
||||
function ENV() {
|
||||
const isQX = typeof $task !== 'undefined';
|
||||
const isLoon = typeof $loon !== 'undefined';
|
||||
const isSurge = typeof $httpClient !== 'undefined' && !isLoon;
|
||||
const isNode = eval(`typeof process !== "undefined"`);
|
||||
return { isQX, isLoon, isSurge, isNode };
|
||||
export function ENV() {
|
||||
const isQX = typeof $task !== 'undefined';
|
||||
const isLoon = typeof $loon !== 'undefined';
|
||||
const isSurge = typeof $httpClient !== 'undefined' && !isLoon;
|
||||
const isNode = eval(`typeof process !== "undefined"`);
|
||||
return { isQX, isLoon, isSurge, isNode };
|
||||
}
|
||||
|
||||
function HTTP(defaultOptions = { baseURL: '' }) {
|
||||
const { isQX, isLoon, isSurge, isNode } = ENV();
|
||||
const methods = [ 'GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH' ];
|
||||
const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
|
||||
export function HTTP(defaultOptions = { baseURL: '' }) {
|
||||
const { isQX, isLoon, isSurge, isNode } = ENV();
|
||||
const methods = [
|
||||
'GET',
|
||||
'POST',
|
||||
'PUT',
|
||||
'DELETE',
|
||||
'HEAD',
|
||||
'OPTIONS',
|
||||
'PATCH',
|
||||
];
|
||||
const URL_REGEX =
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
|
||||
|
||||
function send(method, options) {
|
||||
options = typeof options === 'string' ? { url: options } : options;
|
||||
const baseURL = defaultOptions.baseURL;
|
||||
if (baseURL && !URL_REGEX.test(options.url || '')) {
|
||||
options.url = baseURL ? baseURL + options.url : options.url;
|
||||
}
|
||||
options = { ...defaultOptions, ...options };
|
||||
const timeout = options.timeout;
|
||||
const events = {
|
||||
...{
|
||||
onRequest: () => {},
|
||||
onResponse: (resp) => resp,
|
||||
onTimeout: () => {}
|
||||
},
|
||||
...options.events
|
||||
};
|
||||
function send(method, options) {
|
||||
options = typeof options === 'string' ? { url: options } : options;
|
||||
const baseURL = defaultOptions.baseURL;
|
||||
if (baseURL && !URL_REGEX.test(options.url || '')) {
|
||||
options.url = baseURL ? baseURL + options.url : options.url;
|
||||
}
|
||||
options = { ...defaultOptions, ...options };
|
||||
const timeout = options.timeout;
|
||||
const events = {
|
||||
...{
|
||||
onRequest: () => {},
|
||||
onResponse: (resp) => resp,
|
||||
onTimeout: () => {},
|
||||
},
|
||||
...options.events,
|
||||
};
|
||||
|
||||
events.onRequest(method, options);
|
||||
events.onRequest(method, options);
|
||||
|
||||
let worker;
|
||||
if (isQX) {
|
||||
worker = $task.fetch({
|
||||
method,
|
||||
url: options.url,
|
||||
headers: options.headers,
|
||||
body: options.body
|
||||
});
|
||||
} else if (isLoon || isSurge || isNode) {
|
||||
worker = new Promise((resolve, reject) => {
|
||||
const request = isNode ? eval("require('request')") : $httpClient;
|
||||
request[method.toLowerCase()](options, (err, response, body) => {
|
||||
if (err) reject(err);
|
||||
else
|
||||
resolve({
|
||||
statusCode: response.status || response.statusCode,
|
||||
headers: response.headers,
|
||||
body
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
let worker;
|
||||
if (isQX) {
|
||||
worker = $task.fetch({
|
||||
method,
|
||||
url: options.url,
|
||||
headers: options.headers,
|
||||
body: options.body,
|
||||
});
|
||||
} else if (isLoon || isSurge || isNode) {
|
||||
worker = new Promise((resolve, reject) => {
|
||||
const request = isNode
|
||||
? eval("require('request')")
|
||||
: $httpClient;
|
||||
request[method.toLowerCase()](
|
||||
options,
|
||||
(err, response, body) => {
|
||||
if (err) reject(err);
|
||||
else
|
||||
resolve({
|
||||
statusCode:
|
||||
response.status || response.statusCode,
|
||||
headers: response.headers,
|
||||
body,
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
let timeoutid;
|
||||
const timer = timeout
|
||||
? new Promise((_, reject) => {
|
||||
timeoutid = setTimeout(() => {
|
||||
events.onTimeout();
|
||||
return reject(`${method} URL: ${options.url} exceeds the timeout ${timeout} ms`);
|
||||
}, timeout);
|
||||
})
|
||||
: null;
|
||||
let timeoutid;
|
||||
const timer = timeout
|
||||
? new Promise((_, reject) => {
|
||||
timeoutid = setTimeout(() => {
|
||||
events.onTimeout();
|
||||
return reject(
|
||||
`${method} URL: ${options.url} exceeds the timeout ${timeout} ms`,
|
||||
);
|
||||
}, timeout);
|
||||
})
|
||||
: null;
|
||||
|
||||
return (timer
|
||||
? Promise.race([ timer, worker ]).then((res) => {
|
||||
clearTimeout(timeoutid);
|
||||
return res;
|
||||
})
|
||||
: worker).then((resp) => events.onResponse(resp));
|
||||
}
|
||||
return (
|
||||
timer
|
||||
? Promise.race([timer, worker]).then((res) => {
|
||||
clearTimeout(timeoutid);
|
||||
return res;
|
||||
})
|
||||
: worker
|
||||
).then((resp) => events.onResponse(resp));
|
||||
}
|
||||
|
||||
const http = {};
|
||||
methods.forEach((method) => (http[method.toLowerCase()] = (options) => send(method, options)));
|
||||
return http;
|
||||
const http = {};
|
||||
methods.forEach(
|
||||
(method) =>
|
||||
(http[method.toLowerCase()] = (options) => send(method, options)),
|
||||
);
|
||||
return http;
|
||||
}
|
||||
|
||||
function API(name = 'untitled', debug = false) {
|
||||
const { isQX, isLoon, isSurge, isNode } = ENV();
|
||||
return new class {
|
||||
constructor(name, debug) {
|
||||
this.name = name;
|
||||
this.debug = debug;
|
||||
export function API(name = 'untitled', debug = false) {
|
||||
const { isQX, isLoon, isSurge, isNode } = ENV();
|
||||
return new (class {
|
||||
constructor(name, debug) {
|
||||
this.name = name;
|
||||
this.debug = debug;
|
||||
|
||||
this.http = HTTP();
|
||||
this.env = ENV();
|
||||
this.http = HTTP();
|
||||
this.env = ENV();
|
||||
|
||||
this.node = (() => {
|
||||
if (isNode) {
|
||||
const fs = eval("require('fs')");
|
||||
this.node = (() => {
|
||||
if (isNode) {
|
||||
const fs = eval("require('fs')");
|
||||
|
||||
return {
|
||||
fs
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
this.initCache();
|
||||
return {
|
||||
fs,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
this.initCache();
|
||||
|
||||
const delay = (t, v) =>
|
||||
new Promise(function(resolve) {
|
||||
setTimeout(resolve.bind(null, v), t);
|
||||
});
|
||||
const delay = (t, v) =>
|
||||
new Promise(function (resolve) {
|
||||
setTimeout(resolve.bind(null, v), t);
|
||||
});
|
||||
|
||||
Promise.prototype.delay = function(t) {
|
||||
return this.then(function(v) {
|
||||
return delay(t, v);
|
||||
});
|
||||
};
|
||||
}
|
||||
Promise.prototype.delay = function (t) {
|
||||
return this.then(function (v) {
|
||||
return delay(t, v);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// persistence
|
||||
// initialize cache
|
||||
initCache() {
|
||||
if (isQX) this.cache = JSON.parse($prefs.valueForKey(this.name) || '{}');
|
||||
if (isLoon || isSurge) this.cache = JSON.parse($persistentStore.read(this.name) || '{}');
|
||||
// persistence
|
||||
// initialize cache
|
||||
initCache() {
|
||||
if (isQX)
|
||||
this.cache = JSON.parse($prefs.valueForKey(this.name) || '{}');
|
||||
if (isLoon || isSurge)
|
||||
this.cache = JSON.parse(
|
||||
$persistentStore.read(this.name) || '{}',
|
||||
);
|
||||
|
||||
if (isNode) {
|
||||
// create a json for root cache
|
||||
let fpath = 'root.json';
|
||||
if (!this.node.fs.existsSync(fpath)) {
|
||||
this.node.fs.writeFileSync(fpath, JSON.stringify({}), { flag: 'wx' }, (err) => console.log(err));
|
||||
}
|
||||
this.root = {};
|
||||
if (isNode) {
|
||||
// create a json for root cache
|
||||
let fpath = 'root.json';
|
||||
if (!this.node.fs.existsSync(fpath)) {
|
||||
this.node.fs.writeFileSync(
|
||||
fpath,
|
||||
JSON.stringify({}),
|
||||
{ flag: 'wx' },
|
||||
(err) => console.log(err),
|
||||
);
|
||||
}
|
||||
this.root = {};
|
||||
|
||||
// create a json file with the given name if not exists
|
||||
fpath = `${this.name}.json`;
|
||||
if (!this.node.fs.existsSync(fpath)) {
|
||||
this.node.fs.writeFileSync(fpath, JSON.stringify({}), { flag: 'wx' }, (err) => console.log(err));
|
||||
this.cache = {};
|
||||
} else {
|
||||
this.cache = JSON.parse(this.node.fs.readFileSync(`${this.name}.json`));
|
||||
}
|
||||
}
|
||||
}
|
||||
// create a json file with the given name if not exists
|
||||
fpath = `${this.name}.json`;
|
||||
if (!this.node.fs.existsSync(fpath)) {
|
||||
this.node.fs.writeFileSync(
|
||||
fpath,
|
||||
JSON.stringify({}),
|
||||
{ flag: 'wx' },
|
||||
(err) => console.log(err),
|
||||
);
|
||||
this.cache = {};
|
||||
} else {
|
||||
this.cache = JSON.parse(
|
||||
this.node.fs.readFileSync(`${this.name}.json`),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store cache
|
||||
persistCache() {
|
||||
const data = JSON.stringify(this.cache, null, 2);
|
||||
if (isQX) $prefs.setValueForKey(data, this.name);
|
||||
if (isLoon || isSurge) $persistentStore.write(data, this.name);
|
||||
if (isNode) {
|
||||
this.node.fs.writeFileSync(`${this.name}.json`, data, { flag: 'w' }, (err) => console.log(err));
|
||||
this.node.fs.writeFileSync('root.json', JSON.stringify(this.root, null, 2), { flag: 'w' }, (err) =>
|
||||
console.log(err)
|
||||
);
|
||||
}
|
||||
}
|
||||
// store cache
|
||||
persistCache() {
|
||||
const data = JSON.stringify(this.cache, null, 2);
|
||||
if (isQX) $prefs.setValueForKey(data, this.name);
|
||||
if (isLoon || isSurge) $persistentStore.write(data, this.name);
|
||||
if (isNode) {
|
||||
this.node.fs.writeFileSync(
|
||||
`${this.name}.json`,
|
||||
data,
|
||||
{ flag: 'w' },
|
||||
(err) => console.log(err),
|
||||
);
|
||||
this.node.fs.writeFileSync(
|
||||
'root.json',
|
||||
JSON.stringify(this.root, null, 2),
|
||||
{ flag: 'w' },
|
||||
(err) => console.log(err),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
write(data, key) {
|
||||
this.log(`SET ${key}`);
|
||||
if (key.indexOf('#') !== -1) {
|
||||
key = key.substr(1);
|
||||
if (isSurge || isLoon) {
|
||||
return $persistentStore.write(data, key);
|
||||
}
|
||||
if (isQX) {
|
||||
return $prefs.setValueForKey(data, key);
|
||||
}
|
||||
if (isNode) {
|
||||
this.root[key] = data;
|
||||
}
|
||||
} else {
|
||||
this.cache[key] = data;
|
||||
}
|
||||
this.persistCache();
|
||||
}
|
||||
write(data, key) {
|
||||
this.log(`SET ${key}`);
|
||||
if (key.indexOf('#') !== -1) {
|
||||
key = key.substr(1);
|
||||
if (isSurge || isLoon) {
|
||||
return $persistentStore.write(data, key);
|
||||
}
|
||||
if (isQX) {
|
||||
return $prefs.setValueForKey(data, key);
|
||||
}
|
||||
if (isNode) {
|
||||
this.root[key] = data;
|
||||
}
|
||||
} else {
|
||||
this.cache[key] = data;
|
||||
}
|
||||
this.persistCache();
|
||||
}
|
||||
|
||||
read(key) {
|
||||
this.log(`READ ${key}`);
|
||||
if (key.indexOf('#') !== -1) {
|
||||
key = key.substr(1);
|
||||
if (isSurge || isLoon) {
|
||||
return $persistentStore.read(key);
|
||||
}
|
||||
if (isQX) {
|
||||
return $prefs.valueForKey(key);
|
||||
}
|
||||
if (isNode) {
|
||||
return this.root[key];
|
||||
}
|
||||
} else {
|
||||
return this.cache[key];
|
||||
}
|
||||
}
|
||||
read(key) {
|
||||
this.log(`READ ${key}`);
|
||||
if (key.indexOf('#') !== -1) {
|
||||
key = key.substr(1);
|
||||
if (isSurge || isLoon) {
|
||||
return $persistentStore.read(key);
|
||||
}
|
||||
if (isQX) {
|
||||
return $prefs.valueForKey(key);
|
||||
}
|
||||
if (isNode) {
|
||||
return this.root[key];
|
||||
}
|
||||
} else {
|
||||
return this.cache[key];
|
||||
}
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
this.log(`DELETE ${key}`);
|
||||
if (key.indexOf('#') !== -1) {
|
||||
key = key.substr(1);
|
||||
if (isSurge || isLoon) {
|
||||
return $persistentStore.write(null, key);
|
||||
}
|
||||
if (isQX) {
|
||||
return $prefs.removeValueForKey(key);
|
||||
}
|
||||
if (isNode) {
|
||||
delete this.root[key];
|
||||
}
|
||||
} else {
|
||||
delete this.cache[key];
|
||||
}
|
||||
this.persistCache();
|
||||
}
|
||||
delete(key) {
|
||||
this.log(`DELETE ${key}`);
|
||||
if (key.indexOf('#') !== -1) {
|
||||
key = key.substr(1);
|
||||
if (isSurge || isLoon) {
|
||||
return $persistentStore.write(null, key);
|
||||
}
|
||||
if (isQX) {
|
||||
return $prefs.removeValueForKey(key);
|
||||
}
|
||||
if (isNode) {
|
||||
delete this.root[key];
|
||||
}
|
||||
} else {
|
||||
delete this.cache[key];
|
||||
}
|
||||
this.persistCache();
|
||||
}
|
||||
|
||||
// notification
|
||||
notify(title, subtitle = '', content = '', options = {}) {
|
||||
const openURL = options['open-url'];
|
||||
const mediaURL = options['media-url'];
|
||||
// notification
|
||||
notify(title, subtitle = '', content = '', options = {}) {
|
||||
const openURL = options['open-url'];
|
||||
const mediaURL = options['media-url'];
|
||||
|
||||
if (isQX) $notify(title, subtitle, content, options);
|
||||
if (isSurge) {
|
||||
$notification.post(title, subtitle, content + `${mediaURL ? '\n多媒体:' + mediaURL : ''}`, {
|
||||
url: openURL
|
||||
});
|
||||
}
|
||||
if (isLoon) {
|
||||
let opts = {};
|
||||
if (openURL) opts['openUrl'] = openURL;
|
||||
if (mediaURL) opts['mediaUrl'] = mediaURL;
|
||||
if (JSON.stringify(opts) === '{}') {
|
||||
$notification.post(title, subtitle, content);
|
||||
} else {
|
||||
$notification.post(title, subtitle, content, opts);
|
||||
}
|
||||
}
|
||||
if (isNode) {
|
||||
const content_ =
|
||||
content + (openURL ? `\n点击跳转: ${openURL}` : '') + (mediaURL ? `\n多媒体: ${mediaURL}` : '');
|
||||
console.log(`${title}\n${subtitle}\n${content_}\n\n`);
|
||||
}
|
||||
}
|
||||
if (isQX) $notify(title, subtitle, content, options);
|
||||
if (isSurge) {
|
||||
$notification.post(
|
||||
title,
|
||||
subtitle,
|
||||
content + `${mediaURL ? '\n多媒体:' + mediaURL : ''}`,
|
||||
{
|
||||
url: openURL,
|
||||
},
|
||||
);
|
||||
}
|
||||
if (isLoon) {
|
||||
let opts = {};
|
||||
if (openURL) opts['openUrl'] = openURL;
|
||||
if (mediaURL) opts['mediaUrl'] = mediaURL;
|
||||
if (JSON.stringify(opts) === '{}') {
|
||||
$notification.post(title, subtitle, content);
|
||||
} else {
|
||||
$notification.post(title, subtitle, content, opts);
|
||||
}
|
||||
}
|
||||
if (isNode) {
|
||||
const content_ =
|
||||
content +
|
||||
(openURL ? `\n点击跳转: ${openURL}` : '') +
|
||||
(mediaURL ? `\n多媒体: ${mediaURL}` : '');
|
||||
console.log(`${title}\n${subtitle}\n${content_}\n\n`);
|
||||
}
|
||||
}
|
||||
|
||||
// other helper functions
|
||||
log(msg) {
|
||||
if (this.debug) console.log(`[${this.name}] LOG: ${msg}`);
|
||||
}
|
||||
// other helper functions
|
||||
log(msg) {
|
||||
if (this.debug) console.log(`[${this.name}] LOG: ${msg}`);
|
||||
}
|
||||
|
||||
info(msg) {
|
||||
console.log(`[${this.name}] INFO: ${msg}`);
|
||||
}
|
||||
info(msg) {
|
||||
console.log(`[${this.name}] INFO: ${msg}`);
|
||||
}
|
||||
|
||||
error(msg) {
|
||||
console.log(`[${this.name}] ERROR: ${msg}`);
|
||||
}
|
||||
error(msg) {
|
||||
console.log(`[${this.name}] ERROR: ${msg}`);
|
||||
}
|
||||
|
||||
wait(millisec) {
|
||||
return new Promise((resolve) => setTimeout(resolve, millisec));
|
||||
}
|
||||
wait(millisec) {
|
||||
return new Promise((resolve) => setTimeout(resolve, millisec));
|
||||
}
|
||||
|
||||
done(value = {}) {
|
||||
if (isQX || isLoon || isSurge) {
|
||||
$done(value);
|
||||
} else if (isNode) {
|
||||
if (typeof $context !== 'undefined') {
|
||||
$context.headers = value.headers;
|
||||
$context.statusCode = value.statusCode;
|
||||
$context.body = value.body;
|
||||
}
|
||||
}
|
||||
}
|
||||
}(name, debug);
|
||||
done(value = {}) {
|
||||
if (isQX || isLoon || isSurge) {
|
||||
$done(value);
|
||||
} else if (isNode) {
|
||||
if (typeof $context !== 'undefined') {
|
||||
$context.headers = value.headers;
|
||||
$context.statusCode = value.statusCode;
|
||||
$context.body = value.body;
|
||||
}
|
||||
}
|
||||
}
|
||||
})(name, debug);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
HTTP,
|
||||
ENV,
|
||||
API
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user