Merge pull request #6 from Redume/chore/migrating-to-hjson
Some checks failed
Create and publish a Docker image / build-and-push-chart (push) Has been cancelled
Create and publish a Docker image / detect what files changed (push) Has been cancelled
Create and publish a Docker image / build-and-push-server (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
Create and publish a Docker image / build-and-push-web (push) Has been cancelled

Chore/migrating to hjson
This commit is contained in:
Данил 2025-03-05 19:28:54 +00:00 committed by GitHub
commit 5387a91a2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 278 additions and 201 deletions

View file

@ -17,7 +17,8 @@ module.exports = {
'error', { 'error', {
singleQuote: true, singleQuote: true,
parser: "flow", parser: "flow",
tabWidth: 4 tabWidth: 4,
endOfLine: 'lf'
}, },
], ],
}, },

10
.gitignore vendored
View file

@ -1,12 +1,20 @@
# IDE & text redactors files
.idea .idea
.vscode .vscode
# Project
CertSSL/ CertSSL/
charts/ charts/
# Depends
node_modules/ node_modules/
# Docker compose
postgres-data/ postgres-data/
config.yaml # Config
config.hjson
.env .env
# Other
.DS_Store .DS_Store

View file

@ -9,7 +9,7 @@ import asyncpg
from utils.load_config import load_config from utils.load_config import load_config
config = load_config('config.yaml') config = load_config('config.hjson')
async def create_pool() -> asyncpg.pool.Pool: async def create_pool() -> asyncpg.pool.Pool:
""" """

View file

@ -16,7 +16,7 @@ from routes import get_chart, get_chart_period
from utils.load_config import load_config from utils.load_config import load_config
app = FastAPI() app = FastAPI()
config = load_config('config.yaml') config = load_config('config.hjson')
if not os.path.exists('../charts'): if not os.path.exists('../charts'):
os.mkdir('../charts') os.mkdir('../charts')
@ -35,10 +35,10 @@ if __name__ == '__main__':
port=3030, port=3030,
ssl_keyfile= ssl_keyfile=
config['server']['ssl']['private_key'] config['server']['ssl']['private_key']
if config['server']['ssl']['work'] if config['server']['ssl']['enabled']
else None, else None,
ssl_certfile= ssl_certfile=
config['server']['ssl']['cert'] config['server']['ssl']['cert']
if config['server']['ssl']['work'] if config['server']['ssl']['enabled']
else None else None
) )

View file

@ -9,7 +9,7 @@ from user_agents import parse as ua_parse
from utils.load_config import load_config from utils.load_config import load_config
config = load_config('config.yaml') config = load_config('config.hjson')
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
class PlausibleAnalytics: class PlausibleAnalytics:
@ -23,7 +23,7 @@ class PlausibleAnalytics:
async def __call__(self, request, call_next): async def __call__(self, request, call_next):
response = await call_next(request) response = await call_next(request)
if HTTPStatus(response.status_code).is_client_error or not config['analytics']['work']: if HTTPStatus(response.status_code).is_client_error or not config['analytics']['enabled']:
return response return response
user_agent = request.headers.get('user-agent', 'unknown') user_agent = request.headers.get('user-agent', 'unknown')

View file

@ -1,5 +1,5 @@
matplotlib~=3.9.1 matplotlib~=3.9.1
PyYAML~=6.0.1 hjson~=3.1.0
uvicorn~=0.29.0 uvicorn~=0.29.0
fastapi[standard]~=0.115.2 fastapi[standard]~=0.115.2
starlette~=0.40.0 starlette~=0.40.0

View file

@ -1,20 +1,20 @@
# pylint: disable=R0801
""" """
This module provides a function for loading a YAML configuration file. Parsing and converting HJSON config to JSON
The function reads the file content and returns it as a Python dictionary.
""" """
import yaml import hjson
import json
def load_config(file_path: str) -> dict: def load_config(file_path: str) -> dict:
""" """
Loads a YAML configuration file and returns its contents as a dictionary. Load an HJSON file, convert it to a JSON string with indentation,
and return it.
This function opens the specified YAML file, parses its content, and params: file_path (str): The path to the HJSON file.
returns it in dictionary format, making it accessible for use in
the application.
:param file_path: The path to the YAML configuration file to be loaded. returns str: The JSON string formatted with indentation.
:return: A dictionary containing the parsed content of the YAML file.
""" """
with open(file_path, 'r', encoding='utf-8') as file: with open(file_path, 'r', encoding='utf-8') as file:
return yaml.safe_load(file) hjson_data = hjson.load(file)
return json.loads(
json.dumps(hjson_data, indent=4)
)

View file

@ -13,7 +13,7 @@ async function validateSchedule(schedule) {
} }
async function initialize() { async function initialize() {
await require('../shared/database/src/create_table')(); //await require('../shared/database/src/create_table')();
} }
async function runTasks() { async function runTasks() {
@ -21,6 +21,7 @@ async function runTasks() {
} }
async function main() { async function main() {
console.log(config['currency']['collecting']['schedule'])
await initialize(); await initialize();
await validateSchedule(config['currency']['collecting']['schedule']); await validateSchedule(config['currency']['collecting']['schedule']);
@ -32,8 +33,6 @@ async function main() {
); );
} }
main().catch((err) => { main();
logger.error('Error in main execution:', err);
});
module.exports = { main }; module.exports = { main };

57
config.example.hjson Normal file
View file

@ -0,0 +1,57 @@
{
database:
{
user: DATABASE_USERNAME
password: DATABASE_PASSWORD
host: localhost
name: kekkai
port: 5432
}
server:
{
host: 0.0.0.0
ssl:
{
private_key: /CertSSL/privkey.pem
cert: /CertSSL/fullchain.pem
enabled: false
}
log:
{
level: info
}
}
analytics:
{
plausible_domain: plausible.io
plausible_token: TOKEN
plausiblee_api: https://plausible.io//api/event/
enabled: false
}
currency:
{
collecting:
{
fiat: true
crypto: false
schedule: 30 8 * * *
crypto_apikey: TOKEN
}
fiat:
[
USD
RUB
EUR
UAH
TRY
KZT
]
crypto:
[
ETH
TON
USDT
BTC
]
}
}

View file

@ -1,42 +0,0 @@
# For more information, see the documentation
# https://kekkai-docs.redume.su/
database: # Postgresql database data, for connection
user: 'DATABASE_USERNAME'
password: 'DATABASE_PASSWORD'
host: 'DATABASE_HOST'
name: 'DATABASE_NAME'
port: 5432
server:
host: '0.0.0.0'
ssl:
private_key: '/CertSSL/privkey.pem' # The path to the private SSL key file (String)
cert: '/CertSSL/fullchain.pem' # The path to the SSL certificate (String)
work: false # Enable or disable SSL support [Boolean]
log:
print: true # Enable or disable logging [Boolean]
level: 'info' # Log level (Fatal/Error/Warn/Log/Debug) [String]
analytics:
plausible_api: 'https://plausible.io/api/event/'
plausible_domain: 'PLAUSIBLE_DOMAIN'
plausible_token: 'PLAUSIBLE_TOKEN'
work: false
currency:
chart:
save: false # Enable or disable saving graphs to an image (Boolean)
collecting:
fiat: true # Turn off or turn on the collection of the fiat currency rate [Boolean]
crypto: false
schedule: '30 8 * * *' # Currency collection schedule in crontab format [String]
crypto_apikey: 'APIKEY'
fiat: # List of fiat currency to save the exchange rate [Array]
- USD
- RUB
- EUR
- UAH
- TRY
- KZT
crypto:
- ETH
- TON
- USDT

View file

@ -24,7 +24,7 @@ services:
- '3000:3000' - '3000:3000'
volumes: volumes:
- './CertSSL:/CertSSL' - './CertSSL:/CertSSL'
- './config.yaml:/config.yaml' - './config.hjson:/config.hjson'
depends_on: depends_on:
- postgres - postgres
@ -36,7 +36,7 @@ services:
- '3030:3030' - '3030:3030'
volumes: volumes:
- './CertSSL:/CertSSL' - './CertSSL:/CertSSL'
- './config.yaml:/config.yaml' - './config.hjson:/config.hjson'
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
@ -50,7 +50,7 @@ services:
- '3050:3050' - '3050:3050'
volumes: volumes:
- './CertSSL:/CertSSL' - './CertSSL:/CertSSL'
- './config.yaml:/config.yaml' - './config.hjson:/config.hjson'
docs: docs:
build: build:
@ -63,7 +63,7 @@ services:
dockerfile: Dockerfile-collect-currency dockerfile: Dockerfile-collect-currency
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- './config.yaml:/config.yaml' - './config.hjson:/config.hjson'
depends_on: depends_on:
- postgres - postgres

View file

@ -51,8 +51,8 @@ export default defineConfig({
slug: 'docs/config/config-env' slug: 'docs/config/config-env'
}, },
{ {
label: 'Configure config.yaml', label: 'Configure config.hjson',
slug: 'docs/config/config-yaml' slug: 'docs/config/config-hjson'
}, },
{ {
label: 'Configure Nginx', label: 'Configure Nginx',

View file

@ -1,66 +1,93 @@
--- ---
title: Configure config.yaml title: Configure config.hjson
--- ---
import { Aside } from '@astrojs/starlight/components'; import { Aside } from '@astrojs/starlight/components';
Kekkai can be configured using the `config.yaml` file in the working directory. Kekkai can be configured using the `config.hjson` file in the working directory.
`config.example.yaml`. `config.example.hjson`.
```yaml ```hjson
# For more information, see the documentation # For more information, see the documentation
# https://kekkai-docs.redume.su/ # https://kekkai-docs.redume.su/
database: {
user: 'DATABASE_USERNAME' database:
password: 'DATABASE_PASSWORD' {
host: 'DATABASE_HOST' user: DATABASE_USERNAME
name: 'DATABASE_NAME' password: DATABASE_PASSWORD
port: 5432 host: localhost
server: name: kekkai
host: '0.0.0.0' port: 5432
ssl: }
private_key: '/CertSSL/privkey.pem' server:
cert: '/CertSSL/fullchain.pem' {
work: true host: 0.0.0.0
log: ssl:
print: true {
level: 'info' private_key: /CertSSL/privkey.pem
analytics: cert: /CertSSL/fullchain.pem
plausible_api: 'https://plausible.io/api/event/' enabled: false
plausible_domain: 'PLAUSIBLE_DOMAIN' }
plausible_token: 'PLAUSIBLE_TOKEN' log:
work: false {
currency: level: info
chart: }
save: false }
collecting: analytics:
{
plausible_domain: plausible.io
plausible_token: TOKEN
enabled: false
}
currency:
{
collecting:
{
fiat: true fiat: true
schedule: '30 8 * * *' crypto: false
fiat: schedule: 30 8 * * *
- USD crypto_apikey: TOKEN
- RUB }
- EUR fiat:
- UAH [
- TRY USD
- KZT RUB
EUR
UAH
TRY
KZT
]
crypto:
[
ETH
TON
USDT
BTC
]
}
}
``` ```
## Database ## Database
Kekkai is used as a `PostgreSQL` database. Kekkai is used as a `PostgreSQL` database.
<Aside> <Aside>
If you installed Kekkai via Docker Compose, If you installed Kekkai via `Docker Compose`,
then install it in the `database.host` value of `postgres`. set it to `database.host` for postgres.
The rest of the data does not have to be filled in. The password (database.password) should be the same as in `.env`,
They need to be filled in `.env`. the rest of the data doesn't need to be filled in,
it should be in `.env`
What should it look like: What should it look like:
```yaml ```hjson
database: database:
... {
host: 'postgres' ...
... password: PASSWORD_FROM_ENV
host: postgres
...
}
... ...
``` ```
</Aside> </Aside>
@ -96,7 +123,7 @@ analytics:
plausible_api: 'https://plausible.io/api/event/' plausible_api: 'https://plausible.io/api/event/'
plausible_domain: 'PLAUSIBLE_DOMAIN' plausible_domain: 'PLAUSIBLE_DOMAIN'
plausible_token: 'PLAUSIBLE_TOKEN' plausible_token: 'PLAUSIBLE_TOKEN'
work: true enabled: true
... ...
``` ```
@ -107,7 +134,7 @@ analytics:
You can add the domain [here](https://plausible.io/sites/new?flow=provisioning). You can add the domain [here](https://plausible.io/sites/new?flow=provisioning).
- `plausible_token`: Api token for authorization and sending requests. - `plausible_token`: Api token for authorization and sending requests.
You can create it [here](https://plausible.io/settings/api-keys). You can create it [here](https://plausible.io/settings/api-keys).
- `work`: Enable or disable analytics. - `enabled`: Enable or disable analytics.
## Currency ## Currency
`DuckDuckGo` (fiat currency collection) and `CoinMarketCap` (cryptocurrency collection) `DuckDuckGo` (fiat currency collection) and `CoinMarketCap` (cryptocurrency collection)
@ -116,25 +143,32 @@ are used to collect currency rates.
```yaml ```yaml
... ...
currency: currency:
chart: {
save: false # Enable or disable saving graphs to an image (Boolean) collecting:
collecting: {
fiat: true # Turn off or turn on the collection of the fiat currency rate [Boolean] fiat: true
crypto: false crypto: false
schedule: '30 8 * * *' # Currency collection schedule in crontab format [String] schedule: 30 8 * * *
crypto_apikey: 'APIKEY' crypto_apikey: TOKEN
fiat: # List of fiat currency to save the exchange rate [Array] }
- USD fiat:
- RUB [
- EUR USD
- UAH RUB
- TRY EUR
- KZT UAH
crypto: TRY
- ETH KZT
- TON ]
- USDT crypto:
[
ETH
TON
USDT
BTC
]
}
``` ```
- `currency.chart.save`: Enable or disable saving graphs. - `currency.chart.save`: Enable or disable saving graphs.

View file

@ -1,26 +1,32 @@
const logger = require("../shared/logger/src/main.js"); const logger = require('../shared/logger/src/main.js');
const config = require("../shared/config/src/main.js")(); const config = require('../shared/config/src/main.js')();
const fs = require("fs"); const fs = require('fs');
const axios = require("axios"); const axios = require('axios');
const UAParser = require("ua-parser-js"); const UAParser = require('ua-parser-js');
require("../shared/database/src/create_table.js")(); require('../shared/database/src/create_table.js')();
const fastify = require("fastify")({ const fastify = require('fastify')({
logger: config["server"]["log"]["print"] ? logger : false, logger: config['server']['log']['level'] !== 'none' ? logger : false,
...(config["server"]["ssl"]["work"] ...(config['server']['ssl']['enabled']
? { ? {
https: { https: {
key: fs.readFileSync(config["server"]["ssl"]["private_key"], "utf8"), key: fs.readFileSync(
cert: fs.readFileSync(config["server"]["ssl"]["cert"], "utf8"), config['server']['ssl']['private_key'],
}, 'utf8',
} ),
cert: fs.readFileSync(
config['server']['ssl']['cert'],
'utf8',
),
},
}
: false), : false),
}); });
const getRateRoute = require("./routes/getRate.js"); const getRateRoute = require('./routes/getRate.js');
const getMetadata = require("./routes/metadata.js"); const getMetadata = require('./routes/metadata.js');
fastify.register(getRateRoute); fastify.register(getRateRoute);
fastify.register(getMetadata); fastify.register(getMetadata);
@ -28,69 +34,81 @@ fastify.register(getMetadata);
fastify.setNotFoundHandler(function (res, reply) { fastify.setNotFoundHandler(function (res, reply) {
return reply.status(404).send({ return reply.status(404).send({
status: 404, status: 404,
message: "Page not found!", message: 'Page not found!',
documentation: "https://kekkai-docs.redume.su/", documentation: 'https://kekkai-docs.redume.su/',
}); });
}); });
fastify.addHook("onResponse", async (request, reply) => { fastify.addHook('onResponse', async (request, reply) => {
const routePart = request.raw.url.split("/"); const routePart = request.raw.url.split('/');
const routePartFiltered = routePart const routePartFiltered = routePart
.filter((part) => part !== "") .filter((part) => part !== '')
.map((part) => `${part}/`); .map((part) => `${part}/`);
routePartFiltered.unshift("/"); routePartFiltered.unshift('/');
if (!config?.["analytics"]["work"] ? config?.["analytics"]["work"] : false) if (!config?.['analytics']['work'] ? config?.['analytics']['work'] : false)
return; return;
else if (!fastify.printRoutes().includes(routePartFiltered.at(-1))) return; else if (!fastify.printRoutes().includes(routePartFiltered.at(-1))) return;
const userAgent = request.headers["user-agent"]; const userAgent = request.headers['user-agent'];
const parser = new UAParser(userAgent); const parser = new UAParser(userAgent);
const browser = parser.getBrowser(); const browser = parser.getBrowser();
const os = parser.getOS(); const os = parser.getOS();
const formattedOS = const formattedOS =
os.name && os.version ? `${os.name} ${os.version}` : "N/A"; os.name && os.version ? `${os.name} ${os.version}` : 'N/A';
const formattedBrowser = const formattedBrowser =
browser.name && browser.version browser.name && browser.version
? `${browser.name} ${browser.version}` ? `${browser.name} ${browser.version}`
: "N/A"; : 'N/A';
const event = { const event = {
domain: config["analytics"]["plausible_domain"], domain: config['analytics']['plausible_domain'],
name: request.routeOptions.url name: request.routeOptions.url
? request.routeOptions.url ? request.routeOptions.url
: "404 - Not Found", : '404 - Not Found',
url: request.raw.url, url: request.raw.url,
props: { props: {
method: request.method, method: request.method,
statusCode: reply.statusCode, statusCode: reply.statusCode,
browser: formattedBrowser, browser: formattedBrowser,
os: formattedOS, os: formattedOS,
source: request.headers["referer"] source: request.headers['referer']
? request.headers["referer"] ? request.headers['referer']
: "direct", : 'direct',
}, },
}; };
try { try {
await axios.post(config["analytics"]["plausible_api"], event, { <<<<<<< HEAD
await axios.post(
`https://${config['analytics']['plausible_domain']}/api/event/`,
event,
{
headers: {
Authorization: `Bearer ${config['analytics']['plausible_token']}`,
'Content-Type': 'application/json',
'User-Agent': userAgent,
},
=======
await axios.post(config['analytics']['plausible_api'], event, {
headers: { headers: {
Authorization: `Bearer ${config["analytics"]["plausible_token"]}`, Authorization: `Bearer ${config['analytics']['plausible_token']}`,
"Content-Type": "application/json", 'Content-Type': 'application/json',
"User-Agent": userAgent, 'User-Agent': userAgent,
>>>>>>> parent of da7134f (chore(server): Changed the name of the keys. Made it easier to change the domain)
}, },
}); });
} catch (error) { } catch (error) {
fastify.log.error("Error sending event to Plausible:", error.message); fastify.log.error('Error sending event to Plausible:', error.message);
} }
}); });
fastify.listen( fastify.listen(
{ {
port: 3000, port: 3000,
host: config["server"]["host"] ? config["server"]["host"] : "localhost", host: config['server']['host'] ? config['server']['host'] : 'localhost',
}, },
(err) => { (err) => {
if (err) { if (err) {

View file

@ -9,19 +9,16 @@
"version": "1.0.0", "version": "1.0.0",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"dependencies": { "dependencies": {
"yaml": "^2.5.0" "hjson": "^3.2.2"
} }
}, },
"node_modules/yaml": { "node_modules/hjson": {
"version": "2.5.0", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", "resolved": "https://registry.npmjs.org/hjson/-/hjson-3.2.2.tgz",
"integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", "integrity": "sha512-MkUeB0cTIlppeSsndgESkfFD21T2nXPRaBStLtf3cAYA2bVEFdXlodZB0TukwZiobPD1Ksax5DK4RTZeaXCI3Q==",
"license": "ISC", "license": "MIT",
"bin": { "bin": {
"yaml": "bin.mjs" "hjson": "bin/hjson"
},
"engines": {
"node": ">= 14"
} }
} }
} }

View file

@ -17,6 +17,6 @@
"homepage": "https://github.com/Redume/Kekkai#readme", "homepage": "https://github.com/Redume/Kekkai#readme",
"description": "Config management service", "description": "Config management service",
"dependencies": { "dependencies": {
"yaml": "^2.5.0" "hjson": "^3.2.2"
} }
} }

View file

@ -1,10 +1,10 @@
const fs = require('fs'); const fs = require('fs');
const yaml = require('yaml'); const hjson = require('hjson');
const config = () => { const config = () => {
if (!fs.existsSync('../config.yaml')) return; if (!fs.existsSync('../config.hjson')) throw new Error('Config not found');
return yaml.parse(fs.readFileSync('../config.yaml', 'utf-8')); return hjson.parse(fs.readFileSync('../config.hjson', 'utf-8'));
}; };
module.exports = config; module.exports = config;

View file

@ -1,18 +1,24 @@
const logger = require("../shared/logger"); const logger = require('../shared/logger');
const config = require("../shared/config/src/main.js")(); const config = require('../shared/config/src/main.js')();
const fs = require("fs"); const fs = require('fs');
const path = require("node:path"); const path = require('node:path');
const fastify = require("fastify")({ const fastify = require('fastify')({
logger: config["server"]["log"]["print"] ? logger : false, logger: config['server']['log']['level'] !== 'none' ? logger : false,
...(config["server"]["ssl"]["work"] ...(config['server']['ssl']['enabled']
? { ? {
https: { https: {
key: fs.readFileSync(config["server"]["ssl"]["private_key"], "utf8"), key: fs.readFileSync(
cert: fs.readFileSync(config["server"]["ssl"]["cert"], "utf8"), config['server']['ssl']['private_key'],
}, 'utf8',
} ),
cert: fs.readFileSync(
config['server']['ssl']['cert'],
'utf8',
),
},
}
: false), : false),
}); });
@ -23,11 +29,10 @@ fastify.register(require('@fastify/static'), {
fastify.register(require('./routes/home.js')); fastify.register(require('./routes/home.js'));
fastify.listen( fastify.listen(
{ {
port: 3050, port: 3050,
host: config["server"]["host"] ? config["server"]["host"] : "localhost", host: config['server']['host'] ? config['server']['host'] : 'localhost',
}, },
(err) => { (err) => {
if (err) { if (err) {