mirror of
1
2
Fork 0

Added server, client and shared code

This commit is contained in:
Devshh 2020-03-15 13:35:18 +05:30
parent 850748221f
commit 9644eec4bc
34 changed files with 4681 additions and 0 deletions

26
client/.eslintrc.js Normal file
View File

@ -0,0 +1,26 @@
module.exports = {
'env': {
'browser': true,
'es6': true,
},
'extends': [
'google'
],
'globals': {
'Atomics': 'readonly',
'SharedArrayBuffer': 'readonly',
},
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 2018,
'sourceType': 'module',
},
'plugins': [
'@typescript-eslint'
],
'rules': {
"require-jsdoc": "off",
"quotes": ["error", "double"],
"comma-dangle": "off"
},
};

2
client/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

BIN
client/.package.json.swp Normal file

Binary file not shown.

1423
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
client/package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "ucaptcha-client",
"version": "1.0.0",
"description": "Client-facing project for uCaptcha",
"main": "index.js",
"scripts": {
"dev": "NODE_ENV=development; rollup -c rollup.config.js -w",
"build": "NODE_ENV=production; rollup -c rollup.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Captcha",
"Bots",
"Antispam"
],
"author": "Devshh",
"license": "GPL-3.0-or-later",
"devDependencies": {
"@rollup/plugin-commonjs": "^11.0.2",
"@rollup/plugin-node-resolve": "^7.1.1",
"@rollup/plugin-typescript": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^2.23.0",
"@typescript-eslint/parser": "^2.23.0",
"eslint": "^6.8.0",
"eslint-config-google": "^0.14.0",
"rollup": "^2.0.6",
"rollup-plugin-terser": "^5.3.0",
"tslib": "^1.11.1",
"typescript": "^3.8.3"
},
"dependencies": {}
}

24
client/rollup.config.js Normal file
View File

@ -0,0 +1,24 @@
import {terser} from "rollup-plugin-terser";
import typescript from "@rollup/plugin-typescript";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
const isProd = process.env.NODE_ENV === "production";
const typescriptOptions = {include: ["src/**/*", "../shared/**/*"]};
if (!isProd) typescriptOptions.noEmitOnError = false;
module.exports = {
input: "src/index.ts",
plugins: [
commonjs({extensions: [".js", ".ts"]}),
resolve({extensions: [".js", ".ts"]}),
isProd && terser(),
typescript(typescriptOptions),
],
output: {
file: "dist/bundle.js",
name: "ucaptcha",
format: "iife"
}
};

30
client/src/index.ts Normal file
View File

@ -0,0 +1,30 @@
import createElement from "./util/createElement";
import InitSession from "../../shared/fun/initializeSession";
function uCaptchaBox(key: string) {
const checkbox = createElement("div");
checkbox.setAttribute("style", "cursor:pointer;border-radius:3px;border:2px solid #888;width:25px;height:25px;display:inline-block");
checkbox.onclick = function() {
fetch(`https://localhost:444/api/init?k=${key}`)
.then((r)=>r.text())
.then((r)=>r.substr(2))
.then((r)=>JSON.parse(r))
.then((resp)=>{
const session = new InitSession();
session.deserialize(resp);
});
checkbox.setAttribute("style",
checkbox.getAttribute("style") + "background-color:royalblue;");
};
const captchaBox = createElement("div");
captchaBox.appendChild(checkbox);
return captchaBox;
}
export function create(websiteKey: string, selector: string) {
// const iframe = createElement("iframe");
// iframe.setAttribute("src", "https://localhost:444/?k="+websiteKey)
document.querySelector(selector)!.appendChild(uCaptchaBox(websiteKey));
}

View File

@ -0,0 +1,3 @@
export default function(tagName: string) {
return document.createElement(tagName)
}

16
client/test/index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>uCaptcha Test</title>
<script src="../dist/bundle.js"></script>
</head>
<body>
<div class="captcha-box"></div>
<script>
ucaptcha.create("hUYUhuytryftyi6787yYtg67", ".captcha-box")
</script>
</body>
</html>

69
client/tsconfig.json Normal file
View File

@ -0,0 +1,69 @@
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "ES2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"rootDirs": [
"./src",
"../shared"
], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

4
server/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
.env
*.tsbuildinfo
public/images

7
server/dist/R.js vendored Normal file
View File

@ -0,0 +1,7 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var path_1 = __importDefault(require("path"));
exports.IMAGES_FOLDER = path_1.default.join(__dirname, "..", "public", "images");

106
server/dist/helpers/fetchImagesJob.js vendored Normal file
View File

@ -0,0 +1,106 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var cron_1 = require("cron");
var request_1 = __importDefault(require("request"));
var path_1 = __importDefault(require("path"));
var R_1 = require("../R");
var utils_1 = require("./utils");
var jimp_1 = __importDefault(require("jimp"));
function saveImage(filepath, uri) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve, reject) {
jimp_1.default.read(uri)
.then(function (image) {
image
.crop(0, 0, 384, 384)
.write(filepath, function (err) {
if (err)
reject(err);
else
resolve();
});
});
})];
});
});
}
function fetchImages(_a) {
var _this = this;
var _b = _a === void 0 ? {} : _a, _c = _b.lat, lat = _c === void 0 ? 40.6971576 : _c, _d = _b.lng, lng = _d === void 0 ? -83.608754 : _d, _e = _b.radius, radius = _e === void 0 ? 5000 : _e;
console.log("Collecting images...");
var requestBody = {
lat: lat,
lng: lng,
radius: radius
};
request_1.default("https://openstreetcam.org/1.0/list/nearby-photos/", { formData: requestBody, method: "POST" }, function (apiError, _, body) { return __awaiter(_this, void 0, void 0, function () {
var json, _i, _a, item, filepath;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (apiError)
throw apiError;
json = JSON.parse(body);
_i = 0, _a = json.currentPageItems;
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
item = _a[_i];
filepath = path_1.default.join(R_1.IMAGES_FOLDER, utils_1.randomBytes(12) + ".jpg");
console.log(filepath);
return [4 /*yield*/, saveImage(filepath, "https://openstreetcam.org/" + item.lth_name)];
case 2:
_b.sent();
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/];
}
});
}); });
}
exports.fetchImages = fetchImages;
exports.fetchImagesJob = new cron_1.CronJob("0 0 0 * * *", function () {
fetchImages();
}, null, true);

93
server/dist/helpers/jobs.js vendored Normal file
View File

@ -0,0 +1,93 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var cron_1 = require("cron");
var request_1 = __importDefault(require("request"));
var fs_1 = __importDefault(require("fs"));
var path_1 = __importDefault(require("path"));
var utils_1 = require("./utils");
function saveImage(filename, uri) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve, _reject) {
request_1.default(uri)
.pipe(fs_1.default.createWriteStream(filename))
.once("close", resolve);
})];
});
});
}
function fetchImages() {
var _this = this;
var requestBody = {
lat: 40.730610,
lng: -73.935242,
radius: 5000
};
request_1.default("https://openstreetcam.org/1.0/list/nearby-photos/", { formData: requestBody, method: "POST" }, function (apiError, _, json) { return __awaiter(_this, void 0, void 0, function () {
var _i, _a, item, filename;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (apiError)
throw apiError;
_i = 0, _a = json.currentPageItems;
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
item = _a[_i];
filename = path_1.default.join(__dirname, "public", "images") + utils_1.randomBytes(8) + ".jpg";
return [4 /*yield*/, saveImage(filename, "https://openstreetcam.org/" + item.lth_name)];
case 2:
_b.sent();
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/];
}
});
}); });
}
exports.fetchImages = fetchImages;
exports.fetchImagesJob = new cron_1.CronJob("0 0 0 * * *", function () {
fetchImages();
}, null, true);

16
server/dist/helpers/utils.js vendored Normal file
View File

@ -0,0 +1,16 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var crypto_1 = __importDefault(require("crypto"));
var alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
function randomBytes(length) {
var bytes = crypto_1.default.randomBytes(length);
var chars = [];
for (var i = 0; i < length; i++) {
chars.push(alphabets[bytes[i] % alphabets.length]);
}
return chars.join("");
}
exports.randomBytes = randomBytes;

22
server/dist/main.js vendored Normal file
View File

@ -0,0 +1,22 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
require("dotenv").config();
var express_1 = __importDefault(require("express"));
var app = express_1.default();
var cookie_parser_1 = __importDefault(require("cookie-parser"));
app.use(cookie_parser_1.default(process.env.COOKIE_SECRET));
app.use(express_1.default.json({
limit: "128kb"
}));
var api_1 = __importDefault(require("./routes/api"));
app.use("/api", api_1.default);
var fetchImagesJob_1 = require("./helpers/fetchImagesJob");
app.listen(8080, function () {
fetchImagesJob_1.fetchImagesJob.start();
// fetchImages({radius: 1000});
console.log("Server started");
console.log("=========================================================");
});

63
server/dist/routes/api/index.js vendored Normal file
View File

@ -0,0 +1,63 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var express_1 = __importDefault(require("express"));
var initializeSession_1 = __importDefault(require("./initializeSession"));
var utils_1 = require("../../helpers/utils");
var router = express_1.default.Router();
router.get("/init", function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var randomSessionId, result, payload;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
randomSessionId = utils_1.randomBytes(16);
return [4 /*yield*/, initializeSession_1.default()];
case 1:
result = _a.sent();
res.cookie("s", randomSessionId, { expires: new Date(Date.now() + 1000 * 60 * 2), httpOnly: true });
res.setHeader("Content-Type", "application/json");
payload = [result];
res.send(":)" + JSON.stringify(payload));
return [2 /*return*/];
}
});
}); });
exports.default = router;

View File

@ -0,0 +1,56 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var pickRandomFile_1 = __importDefault(require("./pickRandomFile"));
function default_1() {
return __awaiter(this, void 0, void 0, function () {
var image;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, pickRandomFile_1.default()];
case 1:
image = _a.sent();
return [2 /*return*/, image];
}
});
});
}
exports.default = default_1;

View File

@ -0,0 +1,72 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var fs_1 = __importDefault(require("fs"));
var path_1 = __importDefault(require("path"));
var jimp_1 = __importDefault(require("jimp"));
var util_1 = require("util");
var R_1 = require("../../R");
var readdirAsync = util_1.promisify(fs_1.default.readdir);
function default_1() {
return __awaiter(this, void 0, void 0, function () {
var files, randomFilePath, image, base64;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
console.time("file");
return [4 /*yield*/, readdirAsync(R_1.IMAGES_FOLDER)];
case 1:
files = _a.sent();
randomFilePath = path_1.default.join(R_1.IMAGES_FOLDER, files[Math.floor(Math.random() * files.length)]);
return [4 /*yield*/, jimp_1.default.read(randomFilePath)];
case 2:
image = _a.sent();
return [4 /*yield*/, image.quality(20).getBase64Async(jimp_1.default.MIME_JPEG)];
case 3:
base64 = _a.sent();
// console.log(base64)
console.timeEnd("file");
return [2 /*return*/, base64];
}
});
});
}
exports.default = default_1;

2
server/dist/types/OSCResponse.js vendored Normal file
View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

2292
server/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

34
server/package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "ucaptcha-server",
"version": "1.0.0",
"description": "Server for uCaptcha",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Captcha",
"Bots",
"Antispam"
],
"author": "Devshh",
"license": "GPL-3.0-or-later",
"dependencies": {
"cookie-parser": "^1.4.4",
"cron": "^1.8.2",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jimp": "^0.9.5",
"redis": "^3.0.2",
"request": "^2.88.2"
},
"devDependencies": {
"@types/cookie-parser": "^1.4.2",
"@types/cron": "^1.7.2",
"@types/express": "^4.17.3",
"@types/node": "^13.9.1",
"@types/request": "^2.48.4",
"nodemon": "^2.0.2",
"typescript": "^3.8.3"
}
}

3
server/src/R.ts Normal file
View File

@ -0,0 +1,3 @@
import path from "path";
export const IMAGES_FOLDER = path.join(__dirname, "..", "public", "images");

View File

@ -0,0 +1,47 @@
import {CronJob} from "cron";
import request from "request";
import path from "path";
import {IMAGES_FOLDER} from "../R";
import {OSCResponse} from "../types/OSCResponse";
import {randomBytes} from "./utils";
import Jimp from "jimp";
async function saveImage(filepath: string, uri: string) {
return new Promise((resolve, reject)=>{
Jimp.read(uri)
.then(image=>{
image
.crop(0, 0, 384, 384)
.write(filepath, (err)=>{
if(err)
reject(err);
else
resolve();
})
})
})
}
export function fetchImages({lat=40.6971576, lng=-83.608754, radius=5000} = {}) {
console.log("Collecting images...")
const requestBody = {
lat,
lng,
radius
};
request("https://openstreetcam.org/1.0/list/nearby-photos/", {formData: requestBody, method: "POST"}, async (apiError, _, body) => {
if (apiError) throw apiError;
const json = JSON.parse(body);
for (const item of (json as OSCResponse).currentPageItems) {
const filepath = path.join(IMAGES_FOLDER, randomBytes(12) + ".jpg");
console.log(filepath);
await saveImage(filepath, `https://openstreetcam.org/${item.lth_name}`);
}
});
}
export const fetchImagesJob = new CronJob("0 0 0 * * *", () => {
fetchImages();
}, null, true);

View File

@ -0,0 +1,14 @@
import crypto from "crypto";
const alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
export function randomBytes(length: number) {
const bytes = crypto.randomBytes(length);
const chars = [];
for (let i = 0; i < length; i++) {
chars.push(alphabets[bytes[i] % alphabets.length]);
}
return chars.join("");
}

24
server/src/main.ts Normal file
View File

@ -0,0 +1,24 @@
require("dotenv").config();
import express from "express";
const app = express();
import cookieParser from "cookie-parser";
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(express.json({
limit: "128kb"
}));
import apis from "./routes/api";
app.use("/api", apis);
import {fetchImagesJob, fetchImages} from "./helpers/fetchImagesJob";
import pickRandomFile from "./routes/api/pickRandomFile";
app.listen(8080, ()=>{
fetchImagesJob.start();
// fetchImages({radius: 1000});
console.log("Server started")
console.log("=========================================================")
})

View File

@ -0,0 +1,21 @@
import express from "express";
import initializer from "./initializeSession"
import {randomBytes} from "../../helpers/utils";
import InitSession from "../../../../shared/fun/initializeSession";
const router = express.Router()
router.get("/init", async (req, res)=>{
const randomSessionId = randomBytes(8);
const result = await initializer();
const initSession = new InitSession();
initSession.sessionId = randomSessionId;
initSession.imageUrl = result;
const payload = initSession.serialize();
res.setHeader("Content-Type", "application/json");
res.send(":)"+JSON.stringify(payload))
});
export default router;

View File

@ -0,0 +1,7 @@
import pickRandomFile from "./pickRandomFile";
export default async function() {
const image = await pickRandomFile();
return image;
}

View File

@ -0,0 +1,20 @@
import fs from "fs";
import path from "path";
import Jimp from "jimp";
import {promisify} from "util";
import {IMAGES_FOLDER} from "../../R";
const readdirAsync = promisify(fs.readdir);
export default async function() {
const files = await readdirAsync(IMAGES_FOLDER);
return files[Math.floor(Math.random() * files.length)];
const randomFilePath = path.join(IMAGES_FOLDER, files[Math.floor(Math.random() * files.length)])
const image = await Jimp.read(randomFilePath)
const base64 = await image.quality(20).getBase64Async(Jimp.MIME_JPEG);
// console.log(base64)
return base64;
}

View File

@ -0,0 +1,32 @@
export interface currentPageItem {
id: string;
sequence_id: string;
sequence_index: string;
lat: string;
lng: string;
name: string;
lth_name: string;
th_name: string;
date_added: string;
timestamp: string;
match_segment_id: string | undefined,
match_lat: string;
match_lng: string;
way_id: string;
shot_date: string;
heading: string;
headers: string;
gps_accuracy: string;
username: string;
}
export interface OSCResponse {
status: {
apiCode: string;
apiMessage: string;
httpCode: number;
httpMessage: string;
};
currentPageItems: currentPageItem[];
totalFilteredItems: string[]
}

69
server/tsconfig.json Normal file
View File

@ -0,0 +1,69 @@
{
"compilerOptions": {
/* Basic Options */
"incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist", /* Redirect output structure to the directory. */
// "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"rootDirs": [
"./src",
"../shared"
], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

View File

@ -0,0 +1,41 @@
enum Indexes {
clientId = 0,
imageUrl
}
export default class InitSession {
private _clientSessionId: string | undefined;
private _imageUrl: string | undefined;
set sessionId(id: string) {
if(id.length !== 8) throw Error("ClientID is not 8 characters");
this._clientSessionId = id;
}
get sessionId(): string {
return this._clientSessionId!;
}
set imageUrl(url: string) {
this._imageUrl = url;
}
get imageUrl(): string {
return this._imageUrl!;
}
serialize(): any[] {
if(!this._clientSessionId || !this._imageUrl) {
throw Error("Unable to serialize because missing field(s)")
}
const payload = [];
payload[Indexes.clientId] = this._clientSessionId;
payload[Indexes.imageUrl] = this._imageUrl;
return payload;
}
deserialize(payload: any[]) {
}
}

11
shared/package.json Normal file
View File

@ -0,0 +1,11 @@
{
"name": "shared",
"version": "1.0.0",
"description": "Common files between server and client",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Devshh",
"license": "GPL-3.0-or-later"
}

View File