Skip to content

Commit

Permalink
Completed /api/add
Browse files Browse the repository at this point in the history
  • Loading branch information
mmdzov committed Aug 19, 2023
1 parent 0d083c4 commit 0256599
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ SSH_PORT=22
# true | false
API_ENABLE=true

# api documentation
API_DOCS=false

# Secret key for your tokens
API_SECRET=S@CrET

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

users.json
blocked_ips.csv
users.csv

# Logs
logs
Expand Down
12 changes: 10 additions & 2 deletions api/controller.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
const { AuthApiKey, Validator } = require("./middlewares");
const Model = require("./model");
const Validator = require("./validator");
const router = require("express").Router();

const validator = new Validator();
const model = new Model();

// Send your api username and password in json format to generate api_key
router.post("/token", validator.token, (req, res) => {
const result = model.token();

return res.json({ api_key: result });
return res.json(result);
});

// Send your proxy email and limit data in json format to be applied
router.post("/add", AuthApiKey, validator.add, (req, res) => {
const result = model.add(req.body);

return res.json(result);
});

module.exports = router;
104 changes: 104 additions & 0 deletions api/middlewares.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const { response } = require("./utils");
const crypto = require("crypto-js");
const Joi = require("joi");

const AuthApiKey = (req, res, next) => {
let apiKey = (req.headers["api_key"] || "").trim();

if (!apiKey)
return res.status(403).json(
response({
error: {
type: "AUTH",
reason: "api_key Not found!",
},
}),
);

let decryptedKey = crypto.AES.decrypt(
apiKey,
process.env.API_SECRET,
).toString(crypto.enc.Utf8);

const parseKey = JSON.parse(decryptedKey);

if (Date.now() > +parseKey.expireAt)
return res.status(403).json(
response({
error: {
type: "AUTH",
reason: "api_key expired Please get a new api_key",
},
}),
);

return next();
};

class Validator {
token(req, res, next) {
/**
* @type {ApiSetTokenType}
*/
const data = req.body;

const schema = Joi.object({
username: Joi.string().required(),
password: Joi.string().required(),
});

const { error } = schema.validate(data);

if (error) {
return res.status(403).json(
response({
error: {
type: "INVALID",
reason: error.message,
},
}),
);
}

if (process.env.API_LOGIN !== `${data.username}:${data.password}`)
return res.status(403).json(
response({
error: {
type: "NOT_MATCH",
reason: "Username or password doesn't match",
},
}),
);

return next();
}

add(req, res, next) {
const schema = Joi.object({
email: Joi.string().required(),
limit: Joi.number().required(),
});

const data = req.body;

const { error } = schema.validate(data);

if (error) {
return res.status(403).json(
response({
error: {
type: "INVALID",
reason: error.message,
},
}),
);
}

return next();
}
}

module.exports = {
AuthApiKey,
Validator,
};
43 changes: 42 additions & 1 deletion api/model.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
const crypto = require("crypto-js");
const { File } = require("../utils");
const { join } = require("path");
const fs = require("fs");
const { response } = require("./utils");

class Model {
constructor() {
this.usersCsvPath = join(__dirname, "../", "users.csv");
}

token() {
const expireAt =
Date.now() + 1000 * 60 * 60 * 24 * +process.env.API_EXPIRE_TOKEN_AT;
Expand All @@ -12,7 +20,40 @@ class Model {
process.env.API_SECRET,
).toString();

return token;
return response({
data: { api_key: token },
status: 1,
});
}

/**
* @typedef {Object} ApiAddDataType
* @property {string} email
* @property {string} limit
*
* @param {ApiAddDataType} data
*/

add(data) {
let file = new File().GetCsvFile(this.usersCsvPath).toString();

if (file.includes(data.email))
return response({
error: {
type: "DUPLICATE",
reason: "This email already exists",
},
});

const dataToCsv = `${data.email},${data.limit}\r\n`;

file += dataToCsv;

fs.writeFileSync(this.usersCsvPath, file);

return response({
status: 1,
});
}
}

Expand Down
14 changes: 14 additions & 0 deletions api/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
*
* @param {ApiResponseType} params
*/

const response = (params) => {
if (!params?.data) params.data = null;
if (!params?.error) params.error = null;
if (!params?.status) params.status = 0;

return params;
};

module.exports = { response };
28 changes: 0 additions & 28 deletions api/validator.js

This file was deleted.

2 changes: 1 addition & 1 deletion db/DBSqlite3.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class DBSqlite3 extends DBInterface {
const indexOfIp = ips.findIndex((item) => item.ip === ipData.ip);

// Get the users.json file
const usersJson = new File().GetFilesJson(join("users.json"));
const usersJson = new File().GetJsonFile(join("users.json"));
const indexOfUser = usersJson.findIndex((item) => item[0] === email);

const userJson = usersJson[indexOfUser] || [
Expand Down
17 changes: 17 additions & 0 deletions types.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,20 @@
* @property {string} username
* @property {string} password
*/

/**
* @typedef {"INVALID" | "NOT_MATCH" | "AUTH" | "DUPLICATE"} ApiErrorTypes
*/

/**
* @typedef {Object} ApiResponseErrorType
* @property {ApiErrorTypes} type
* @property {string} reason
*/

/**
* @typedef {Object} ApiResponseType
* @property {Object | null} data
* @property {ApiResponseErrorType} error
* @property {0|1} status
*/
10 changes: 8 additions & 2 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,17 @@ class File {
return;
}

GetFilesJson(path) {
GetJsonFile(path) {
this.ForceExistsFile(path);

return JSON.parse(fs.readFileSync(path));
}

GetCsvFile(path) {
this.ForceExistsFile(path, "");

return fs.readFileSync(path);
}
}

/**
Expand All @@ -174,7 +180,7 @@ class IPGuard {

const indexOfIp = data.ips.findIndex((item) => item.ip === `${ip}`);

const users = new File().GetFilesJson("users.json");
const users = new File().GetJsonFile("users.json");
const user = users.filter((item) => item[0] === data.email)[0] || null;

const maxAllowConnection = user ? +user[1] : +process.env.MAX_ALLOW_USERS;
Expand Down

0 comments on commit 0256599

Please sign in to comment.