first-commit

This commit is contained in:
Kiril Burlaka 2025-03-11 10:19:55 +01:00
commit 054cb94c47
12 changed files with 2523 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
.idea
.env
build

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
# Declare build-time argument
ARG DOCKER_IMAGE_SOURCE
FROM ${DOCKER_IMAGE_SOURCE}
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE ${PORT}
# CMD npm run build;npm run start
# CMD [ "npm", "run", "start-ts" ]
CMD ./start.sh

22
docker-compose.yml Normal file
View File

@ -0,0 +1,22 @@
services:
svg:
build:
dockerfile: Dockerfile
context: .
args:
DOCKER_IMAGE_SOURCE: ${DOCKER_IMAGE_SOURCE}
ports:
- ${PORT}:8000
container_name: ${CONTAINER_NAME}
tmpfs:
- /tmp:size=${TMPFS_SIZE}
- /var/log:size=50m
deploy:
resources:
limits:
cpus: ${CPU_LIMIT}
memory: ${MEMORY_LIMIT}
volumes:
- .:/wrkdir
restart: unless-stopped
env_file: .env

34
eslint.config.cjs Normal file
View File

@ -0,0 +1,34 @@
// eslint.config.cjs
const js = require("@eslint/js");
const ts = require("@typescript-eslint/eslint-plugin");
const tsParser = require("@typescript-eslint/parser");
const prettier = require("eslint-plugin-prettier");
const globals = require("globals");
module.exports = [
js.configs.recommended,
{
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
parser: tsParser,
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.node,
},
},
plugins: {
"@typescript-eslint": ts,
prettier,
},
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
indent: "off",
"linebreak-style": "off",
quotes: "off",
semi: ["error", "always"],
"prettier/prettier": "error",
},
},
];

2259
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "svg_schemes",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "ts-node src/index.ts",
"build": "tsc",
"start": "node build/index.js",
"lint": "npx eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^22.13.10",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"eslint": "^9.22.0",
"ts-node": "^10.9.2",
"typescript": "^5.8.2"
},
"dependencies": {
"@eslint/js": "^9.22.0",
"@fastify/cors": "^11.0.0",
"dotenv": "^16.4.7",
"eslint-plugin-prettier": "^5.2.3",
"fastify": "^5.2.1",
"globals": "^16.0.0"
}
}

View File

@ -0,0 +1,30 @@
import Config from "../config";
function isIpPrivate(ip: string): boolean {
if ("192.168" === ip.slice(0, 7)) {
return true;
} else if (
ip.slice(0, 3) === "172" &&
ip.slice(4, 6) >= "16" &&
ip.slice(4, 6) <= "31"
) {
return true;
} else if ("10" === ip.slice(0, 2)) {
return true;
}
return false;
}
export const GlobalAccessGuard = async (req, reply): Promise<void> => {
const ip = req.ip;
const token: string = (req.query as any).token;
if (Config.nodeEnv === "LOCAL") {
return;
}
if (!isIpPrivate(ip) || token !== Config.token) {
reply.code(401);
throw new Error("Access denied");
}
};

40
src/config/index.ts Normal file
View File

@ -0,0 +1,40 @@
import * as dotenv from "dotenv";
import * as process from "node:process";
dotenv.config();
class Config {
public readonly nodeEnv: "DEV" | "PROD" | "LOCAL";
public readonly port: number;
public readonly token: string;
constructor() {
this.nodeEnv = process.env.NODE_ENV as "DEV" | "PROD" | "LOCAL";
this.port = +process.env.PORT;
this.token = process.env.TOKEN;
if (
this.nodeEnv !== "DEV" &&
this.nodeEnv !== "PROD" &&
this.nodeEnv !== "LOCAL"
) {
throw new Error("Config: nodeEnv is not defined or not valid");
}
const numberValues = ["port"];
for (const numberValue of numberValues) {
if (!this[numberValue] || typeof this[numberValue] !== "number") {
throw new Error(`Config: ${numberValue} is not defined`);
}
}
const stringValues = ["token"];
for (const stringValue of stringValues) {
if (!this[stringValue] || typeof this[stringValue] !== "string") {
throw new Error(`Config: ${stringValue} is not defined`);
}
}
}
}
export default new Config();

57
src/index.ts Normal file
View File

@ -0,0 +1,57 @@
import fastify from "fastify";
import * as cors from "@fastify/cors";
import Config from "./config";
import { GlobalAccessGuard } from "./auth/global-access-guard";
let app: fastify.FastifyInstance;
const createApp = () => {
console.log("createApp");
app = fastify({
logger: true,
});
//accept only private ips + token OR LOCAL
app.addHook("preValidation", GlobalAccessGuard);
app.setErrorHandler((error, request, reply) => {
console.error(error.message);
// Send error response
reply.status(500).send({
status: "error",
data: {
message: error.message,
},
});
});
app.register(cors);
app.get("/health-check", () => ({ statusCode: 200, status: "ok" }));
// app.register(authController, { prefix: '/auth' });
// if (Config.nodeEnv === 'LOCAL' || Config.nodeEnv === 'DEV') {
// app.register(DevController, { prefix: '/dev' });
// console.log('register dev controller');
// }
console.log("appCreated");
};
const start = async (): Promise<void> => {
try {
createApp();
await app.listen({
port: 8000,
host: "0.0.0.0",
});
console.log("Server started on port: 8000");
console.log(
`You can access it from outside the docker on port ${Config.port}`,
);
} catch (err) {
console.log(err);
process.exit(1);
}
};
start();

10
start.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/sh
if [ "$NODE" = "LOCAL" ] ; then
npm run lint
npm run dev
else
npm run lint
npm run build
npm run start
fi

8
tmpls/.env-template Normal file
View File

@ -0,0 +1,8 @@
PORT=8000
CONTAINER_NAME='svgdev'
TMPFS_SIZE='8192m'
CPU_LIMIT='10'
MEMORY_LIMIT='8g'
DOCKER_IMAGE_SOURCE='node:20.12.1-alpine'
NODE_ENV='LOCAL'
TOKEN='secret_token'

10
tsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es2022",
"module": "commonjs",
"esModuleInterop": true,
"strict": true,
"rootDir": "src",
"outDir": "build",
},
}