├── .prettierrc
├── tsconfig.build.json
├── nest-cli.json
├── src
├── main.ts
├── config.sample.ts
├── app.module.ts
├── utils
│ └── axios.ts
├── components
│ └── telegram.ts
└── scheduler
│ └── scheduler.service.ts
├── .gitignore
├── tsconfig.json
├── .eslintrc.js
├── README.md
└── package.json
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/nest-cli",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src"
5 | }
6 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 |
4 | async function bootstrap() {
5 | const app = await NestFactory.create(AppModule);
6 | await app.listen(3030);
7 | }
8 | bootstrap();
9 |
--------------------------------------------------------------------------------
/src/config.sample.ts:
--------------------------------------------------------------------------------
1 | // Restake Scheduler Health
2 | export const SCHEDULER_HEALTH_URL = '';
3 | export const SCHEDULER_HEALTH_PATH = '';
4 | // API Health
5 | export const API_HEALTH_URL = '';
6 | export const API_HEALTH_PATH = '';
7 | // Telegram Bot
8 | export const HEALTH_BOT_TOKEN = '';
9 | export const HEALTH_BOT_CHATID = '';
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ScheduleModule } from '@nestjs/schedule';
3 |
4 | import { SchedulerService } from './scheduler/scheduler.service';
5 |
6 | @Module({
7 | imports: [
8 | ScheduleModule.forRoot(),
9 | ],
10 | controllers: [],
11 | providers: [SchedulerService],
12 | })
13 | export class AppModule {}
14 |
--------------------------------------------------------------------------------
/src/utils/axios.ts:
--------------------------------------------------------------------------------
1 | import Axios from 'axios';
2 |
3 | function createAxios(url: string) {
4 | try {
5 | return Axios.create({
6 | baseURL: url,
7 | headers: {
8 | Accept: 'application/json'
9 | },
10 | timeout: 5000
11 | });
12 | }
13 | catch (e) {
14 | console.log(e);
15 | }
16 | }
17 |
18 | export async function getAxios(url: string, path: string) {
19 | const _axios = createAxios(url);
20 | return await _axios.get(path);
21 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # enviroment & config
6 | config.ts
7 | package-lock.json
8 |
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | pnpm-debug.log*
14 | yarn-debug.log*
15 | yarn-error.log*
16 | lerna-debug.log*
17 |
18 | # OS
19 | .DS_Store
20 |
21 | # Tests
22 | /coverage
23 | /.nyc_output
24 |
25 | # IDEs and editors
26 | /.idea
27 | .project
28 | .classpath
29 | .c9/
30 | *.launch
31 | .settings/
32 | *.sublime-workspace
33 |
34 | # IDE - VSCode
35 | .vscode/*
36 | !.vscode/settings.json
37 | !.vscode/tasks.json
38 | !.vscode/launch.json
39 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "es2017",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "strictNullChecks": false,
16 | "noImplicitAny": false,
17 | "strictBindCallApply": false,
18 | "forceConsistentCasingInFileNames": false,
19 | "noFallthroughCasesInSwitch": false,
20 | "resolveJsonModule": true,
21 | "esModuleInterop": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir : __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/src/components/telegram.ts:
--------------------------------------------------------------------------------
1 | import TelegramBot from 'node-telegram-bot-api';
2 | import { HEALTH_BOT_CHATID, HEALTH_BOT_TOKEN } from 'src/config';
3 |
4 | const healthTelegram = new TelegramBot(HEALTH_BOT_TOKEN, { polling: false });
5 |
6 | export const sendAllHealthErrorMessage = async () => {
7 | await sendMessage('[SCHEDULER & API] Health Error');
8 | }
9 |
10 | export const sendSchedulerHealthErrorMessage = async () => {
11 | await sendMessage('[SCHEDULER] Health Error');
12 | }
13 |
14 | export const sendApiHealthErrorMessage = async () => {
15 | await sendMessage('[API] Health Error');
16 | }
17 |
18 | const sendMessage = async (message: string) => {
19 | try {
20 | return await healthTelegram.sendMessage(HEALTH_BOT_CHATID, message, { disable_web_page_preview: true });
21 | } catch (e) {
22 | console.log(e);
23 | return ;
24 | }
25 | }
--------------------------------------------------------------------------------
/src/scheduler/scheduler.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron, CronExpression } from '@nestjs/schedule';
3 |
4 | import { getAxios } from 'src/utils/axios';
5 | import {
6 | SCHEDULER_HEALTH_URL,
7 | SCHEDULER_HEALTH_PATH,
8 | API_HEALTH_URL,
9 | API_HEALTH_PATH
10 | } from 'src/config';
11 | import {
12 | sendAllHealthErrorMessage,
13 | sendApiHealthErrorMessage,
14 | sendSchedulerHealthErrorMessage
15 | } from 'src/components/telegram';
16 |
17 | @Injectable()
18 | export class SchedulerService {
19 | constructor() {
20 | }
21 |
22 | @Cron(CronExpression.EVERY_MINUTE, {
23 | name: 'scheduler_healthcheck_handling',
24 | timeZone: 'Etc/UTC'
25 | })
26 | async healthCheckHandler() {
27 | const [schedulerHealthCheck, apiHealthCheck] = await Promise.all([
28 | this.getStatus(SCHEDULER_HEALTH_URL, SCHEDULER_HEALTH_PATH),
29 | this.getStatus(API_HEALTH_URL, API_HEALTH_PATH)
30 | ]);
31 |
32 | switch (true) {
33 | case !schedulerHealthCheck && !apiHealthCheck:
34 | await sendAllHealthErrorMessage();
35 | break;
36 |
37 | case !schedulerHealthCheck:
38 | await sendSchedulerHealthErrorMessage();
39 | break;
40 |
41 | case !apiHealthCheck:
42 | await sendApiHealthErrorMessage();
43 | break;
44 | }
45 | }
46 |
47 | async getStatus(url: string, path: string) {
48 | let healthCheck = false;
49 |
50 | try {
51 | await getAxios(url, path);
52 | healthCheck = true;
53 | } catch (e) {
54 | healthCheck = false;
55 | }
56 |
57 | return healthCheck;
58 | }
59 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FirmaStation Restake HealthCheck
2 | 
3 |
4 | ## **Overview**
5 | The FirmaChain Health Checker is a project that monitors the status of the Restake Scheduler and Restake API. If a status check response is not received, a Telegram bot is used to send notifications. The project utilizes cron functionality provided by NestJS, checking the status every minute.
6 |
7 |
8 |
9 | ## **Configuration**
10 | First, copy the file config.sample.ts to config.ts and then set the values of each variable as described below.
11 |
12 | ```bash
13 | # Copy config file
14 | $ cp config.sample.ts config.ts
15 | ```
16 |
17 | - SCHEDULER_HEALTH_URL: Input the health check URL for the Restake Scheduler.
18 |
19 | - SCHEDULER_HEALTH_PATH: Input the health check path for the Restake Scheduler.
20 |
21 | - API_HEALTH_URL: Input the health check URL for the Restake API.
22 |
23 | - API_HEALTH_PATH: Input the health check path for the Restake API.
24 |
25 | - HEALTH_BOT_TOKEN: Input the token value for the Telegram bot.
26 |
27 | - HEALTH_BOT_CHATID: Input the chat id for the Telegram bot.
28 |
29 |
30 |
31 | ## **Build Instructions**
32 | ```bash
33 | # Clone the repository
34 | $ git clone https://github.com/FirmaChain/firmastation-restake-healthcheck.git
35 |
36 | # Move into the project folder
37 | $ cd firmastation-restake-healthcheck
38 |
39 | # Install necessary packages
40 | $ npm install
41 |
42 | # Copy config.sample.ts to config.ts
43 | $ cp config.sample.ts config.ts
44 |
45 | # Edit the config.ts file and set each variable
46 | $ nano config.ts
47 | ```
48 |
49 |
50 |
51 | ## **Execution**
52 | ```bash
53 | # Start according to PRODUCTION_MODE
54 | $ npm run start
55 |
56 | # start:dev enables the --watch option.
57 | $ npm run start:dev
58 | ```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "firmastation-restake-healthcheck",
3 | "version": "0.0.1",
4 | "description": "firmastation-restake-healthcheck",
5 | "author": "",
6 | "private": true,
7 | "license": "UNLICENSED",
8 | "scripts": {
9 | "prebuild": "rimraf dist",
10 | "build": "nest build",
11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12 | "start": "nest start",
13 | "start:dev": "nest start --watch",
14 | "start:debug": "nest start --debug --watch",
15 | "start:prod": "node dist/main",
16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
17 | "test": "jest",
18 | "test:watch": "jest --watch",
19 | "test:cov": "jest --coverage",
20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
21 | "test:e2e": "jest --config ./test/jest-e2e.json"
22 | },
23 | "dependencies": {
24 | "@nestjs/common": "^9.4.3",
25 | "@nestjs/core": "^9.4.3",
26 | "@nestjs/platform-express": "^9.4.3",
27 | "@nestjs/schedule": "^3.0.1",
28 | "axios": "^1.4.0",
29 | "node-telegram-bot-api": "^0.61.0",
30 | "reflect-metadata": "^0.1.13",
31 | "rimraf": "^5.0.1",
32 | "rxjs": "^7.8.1"
33 | },
34 | "devDependencies": {
35 | "@nestjs/cli": "^9.5.0",
36 | "@nestjs/schematics": "^9.2.0",
37 | "@nestjs/testing": "^9.4.3",
38 | "@types/express": "^4.17.17",
39 | "@types/jest": "^29.5.2",
40 | "@types/node": "^20.3.1",
41 | "@types/node-telegram-bot-api": "^0.61.6",
42 | "@types/supertest": "^2.0.12",
43 | "@typescript-eslint/eslint-plugin": "^5.60.0",
44 | "@typescript-eslint/parser": "^5.60.0",
45 | "eslint": "^8.43.0",
46 | "eslint-config-prettier": "^8.8.0",
47 | "eslint-plugin-prettier": "^4.2.1",
48 | "jest": "^29.5.0",
49 | "prettier": "^2.8.8",
50 | "source-map-support": "^0.5.21",
51 | "supertest": "^6.3.3",
52 | "ts-jest": "^29.1.0",
53 | "ts-loader": "^9.4.3",
54 | "ts-node": "^10.9.1",
55 | "tsconfig-paths": "^4.2.0",
56 | "typescript": "^5.1.3"
57 | },
58 | "jest": {
59 | "moduleFileExtensions": [
60 | "js",
61 | "json",
62 | "ts"
63 | ],
64 | "rootDir": "src",
65 | "testRegex": ".*\\.spec\\.ts$",
66 | "transform": {
67 | "^.+\\.(t|j)s$": "ts-jest"
68 | },
69 | "collectCoverageFrom": [
70 | "**/*.(t|j)s"
71 | ],
72 | "coverageDirectory": "../coverage",
73 | "testEnvironment": "node"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------