Compare commits

...

101 commits
v2.0.0 ... main

Author SHA1 Message Date
3fef9a219e chore: delete branch from docker actions
Some checks failed
Create and publish Docker images / Detect changed files (push) Failing after 32s
Create and publish Docker images / build-and-push-server (push) Has been skipped
Create and publish Docker images / build-and-push-chart (push) Has been skipped
Create and publish Docker images / build-and-push-CR (push) Has been skipped
Create and publish Docker images / build-and-push-web (push) Has been skipped
2025-03-28 16:24:28 +03:00
8ce23cf311 chore: assembling each microservice as a separate image
Some checks failed
Create and publish Docker images / Detect changed files (push) Failing after 32s
Create and publish Docker images / build-and-push-server (push) Has been skipped
Create and publish Docker images / build-and-push-web (push) Has been skipped
Create and publish Docker images / build-and-push-chart (push) Has been skipped
Create and publish Docker images / build-and-push-CR (push) Has been skipped
2025-03-28 16:23:25 +03:00
8a0b75e9ea Fix merge conflicts
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been skipped
Create and publish a Docker image / build-and-push-chart (push) Has been skipped
Create and publish a Docker image / detect what files changed (push) Failing after 32s
Create and publish a Docker image / build-and-push-CR (push) Has been skipped
Create and publish a Docker image / build-and-push-web (push) Has been skipped
2025-03-26 21:57:58 +03:00
1045e497ed
Merge pull request #7 from Redume/chore/add-timezone
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been skipped
Create and publish a Docker image / build-and-push-chart (push) Has been skipped
Create and publish a Docker image / detect what files changed (push) Failing after 32s
Create and publish a Docker image / build-and-push-CR (push) Has been skipped
Create and publish a Docker image / build-and-push-web (push) Has been skipped
Chore/add timezone
2025-03-26 10:42:06 +00:00
3a9043b244 feat(server): Added server timezone display 2025-03-26 13:39:01 +03:00
6c07e348b3 feat(server): Removed lines and text when conflating changes 2025-03-26 13:36:11 +03:00
57867e2d73 chore: auto fix prettier, more compact code 2025-03-26 13:11:21 +03:00
5387a91a2f
Merge pull request #6 from Redume/chore/migrating-to-hjson
Some checks failed
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-chart (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
2025-03-05 19:28:54 +00:00
f79d2b166a docs: Rename the file, replace the examples with a new config 2025-03-05 22:27:36 +03:00
dd73f73869 Revert "chore(server): Changed the name of the keys. Made it easier to change the domain"
This reverts commit da7134f444.
2025-03-05 22:25:26 +03:00
8675fd918f chore: delete catch error 2025-03-05 21:48:45 +03:00
dd69715f8e fix(config): fix crontab 2025-03-05 21:48:27 +03:00
64185e7565 chore(chart): change to new key config 2025-03-03 10:52:28 +03:00
f307644a8c fix(chart): fixed an error with getting a config item 2025-03-03 10:47:56 +03:00
f4475ae466 deps(chart): Replaced pyyaml with hjson. 2025-03-03 10:40:06 +03:00
6cddaa53a8 chore(chart): rename config file, yaml to hjson 2025-03-03 10:39:43 +03:00
c00dea8a81 сhore(chart): Made parsing and converting hjson to json 2025-03-03 10:38:53 +03:00
1964fd333a chore(config): Adapted the code to output a new config. Added an error if there is no config file 2025-03-03 09:44:49 +03:00
dd24356e81 chore(web): Changed the keys of the new config. Improved code readability 2025-03-03 09:43:13 +03:00
58e6ccfda6 chore: change config name 2025-03-03 09:07:13 +03:00
e2c513fe81 fix(server): added the patch at the end, it was causing the analytics to malfunction 2025-03-03 01:29:38 +03:00
547b6c2754 chore(server): change key for config 2025-03-03 01:29:00 +03:00
8aa070d0d1 chore(server): If logging is set to “none”, it is disabled 2025-03-03 01:27:32 +03:00
da7134f444 chore(server): Changed the name of the keys. Made it easier to change the domain 2025-03-03 01:22:31 +03:00
241494ec2d chore: fix url
Some checks failed
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-chart (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
2025-03-03 01:14:33 +03:00
ed7e54988e chore: delete npm cmd and change homepage 2025-03-03 01:13:23 +03:00
7240fc4472 chore: eslint fix 2025-03-02 22:33:02 +03:00
3cbfe5dc70 fix: Fix line endings from CRLF to LF to comply with Prettier and ESLint rules 2025-03-02 22:01:54 +03:00
2a4c02f601 chore(config): Changed gitignore and split 2025-03-02 21:50:46 +03:00
f4838349d4 chore(config): replace example configs 2025-03-02 21:46:41 +03:00
6e7dbe4c14 chore(chart): convert tabs to space
Some checks are pending
Create and publish a Docker image / detect what files changed (push) Waiting to run
Create and publish a Docker image / build-and-push-server (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-chart (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-CR (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-web (push) Blocked by required conditions
2025-03-02 21:08:47 +03:00
fc97f39011
Merge pull request #5 from Redume/docs/migrate-to-starlight
Some checks are pending
Create and publish a Docker image / detect what files changed (push) Waiting to run
Create and publish a Docker image / build-and-push-server (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-chart (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-CR (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-web (push) Blocked by required conditions
Docs/migrate to starlight
2025-03-01 22:34:38 +03:00
b1c234cb5e chore(nginx): Disabled static handling for _astro, assets, docs, and pagefind in Nginx 2025-03-01 22:03:07 +03:00
520df54650 fix(nginx): delete ratelimit for /static/chart 2025-03-01 22:02:17 +03:00
ae9402e0da fix(nginx): delete global ratelimit 2025-03-01 22:01:59 +03:00
84e5d389a0 chore: delete restarts for docs 2025-03-01 22:01:39 +03:00
5d36dfc6ee chore: add mime types for nginx 2025-03-01 22:01:25 +03:00
d479c8494c chore: delete healtcheck 2025-02-26 22:02:29 +03:00
d45bde7624 chore: delete ratelimit for web 2025-02-26 22:02:08 +03:00
d8396d2331 chore: added docker compoe for docs 2025-02-26 15:29:01 +03:00
9724fac958 chore: docs add to nginx 2025-02-26 15:28:46 +03:00
c057ca3c34 chore(readme): Added a mention and a Starlight license 2025-02-26 14:08:28 +03:00
8e1e9fe3c7 docs: Wrote pages for the configs 2025-02-26 14:02:30 +03:00
102790613f chore(docs) delete readme 2025-02-26 13:39:04 +03:00
5a1043e7e6 docs: create metadata page 2025-02-26 13:19:21 +03:00
7de6cf13d3 docs: wrote page for create chart 2025-02-26 13:02:01 +03:00
6269512daa docs: Wrote a page about getting currency 2025-02-25 17:14:11 +03:00
ac52635834 chore: optimize imports 2025-02-25 17:03:20 +03:00
9d6b54b0e4 chore: Adjusted the name of the subdomain 2025-02-25 16:56:49 +03:00
9c9d0352c6 docs: endpoints list page 2025-02-24 20:21:15 +03:00
fe8959577c docs: page for contributing 2025-02-24 20:15:30 +03:00
a707bb2da7 docs: set base url /docs/ 2025-02-24 16:32:32 +03:00
b3aab3e7aa docs: fix name in title file 2025-02-23 22:46:41 +03:00
c6f89c0ed0 docs: Line breaks, due to the length of the line 2025-02-23 22:41:16 +03:00
6e8921d3fe docs: Wrote a page to deploy the project via docker 2025-02-23 22:40:52 +03:00
f2b85ae0b0 docs: I made a homepage 2025-02-23 21:59:33 +03:00
91bacafe80 docs: change logo 2025-02-23 19:40:53 +03:00
00cfc7f611 chore: deleted github actions for mkdocs-material 2025-02-22 23:25:03 +03:00
179a9207df docs: The sample pages have been removed 2025-02-22 23:17:49 +03:00
07439f7d71 docs: The plug names have been changed to project 2025-02-22 23:12:11 +03:00
c812ea5025 docs: new init files starlight 2025-02-22 23:07:20 +03:00
8e1b6e6883 chore(readme): Removed mention of mkdocs material 2025-02-22 23:03:29 +03:00
6ff4881777 docs: delete all files mkdocs-material 2025-02-22 23:02:49 +03:00
02ef286326 chore(web): delete link to logo in html
Some checks failed
Deploy docs / detect what files changed (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-chart (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
Deploy docs / deploy (push) Has been cancelled
2025-02-19 21:53:03 +03:00
2c4a15a3fd
Merge pull request #4 from Redume/feat/improved-graphics
Some checks are pending
Create and publish a Docker image / detect what files changed (push) Waiting to run
Create and publish a Docker image / build-and-push-server (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-chart (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-CR (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-web (push) Blocked by required conditions
Deploy docs / detect what files changed (push) Waiting to run
Deploy docs / deploy (push) Blocked by required conditions
Feat/improved graphics
2025-02-19 14:29:06 +03:00
3daaedaa2d fix(chart): delete chart marker 'o' 2025-02-19 14:24:39 +03:00
77aaddb119 feat(chart): Made colors for rising, falling and stagnant rates 2025-02-19 14:19:18 +03:00
e1fdda4b54 fix(chart): Fixed currency rate and date indexes and other minor fixes 2025-02-19 14:18:10 +03:00
ef2c126d68 chore(chart): convert indent to tabs 2025-02-19 13:43:07 +03:00
ab97ee1e62 chore(chart): Added dependencies 2025-02-19 13:14:12 +03:00
5407830125 chore(chart): I made the hyphenation because of the big line. 2025-02-19 12:59:56 +03:00
2c28db0eb6 feat(chart): Rounded the corners, reduced the number of dates 2025-02-17 19:50:24 +03:00
7fab1b45fe
Merge pull request #3 from Redume/chore/path-filter
Some checks failed
Create and publish a Docker image / detect what files changed (push) Has been cancelled
Deploy docs / detect what files changed (push) Has been cancelled
Deploy docs / deploy (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-chart (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/path filter
2025-02-16 19:32:32 +03:00
bb4d8bbde8 chore: Added creation of a docker image for a web microservice 2025-02-16 19:26:32 +03:00
ef68511286 chore: Made a filter path for github action 2025-02-16 19:24:43 +03:00
125b00b4bb chore: Adding ignore vscode files
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been cancelled
Create and publish a Docker image / build-and-push-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
2025-02-13 13:10:48 +03:00
632f2f34b6 chore(docker): Using alpine versions of nginx and postgrte 2025-02-13 13:10:29 +03:00
6bae4dabfc chore(nginx): The routes /, /robots.txt, /favicon.ico were moved to nginx for the web microservice. Added html page to the home route 2025-02-13 12:43:09 +03:00
907b614f25 chore(nginx): Rout rules have been split into and moved to another folder
Some checks failed
Deploy docs / deploy (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-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
2025-02-10 17:57:43 +03:00
f543d2336f fix(docs): Fixed the curl root example
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been cancelled
Create and publish a Docker image / build-and-push-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
2025-01-17 16:21:42 +03:00
424ecff857 chore(docs): Updated the config information in the documentation
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been cancelled
Create and publish a Docker image / build-and-push-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
2025-01-17 16:11:58 +03:00
c944e552ec chore(docs): Added documentation about the metadata router 2025-01-17 16:11:41 +03:00
87d19540f8 fix(docs): Fixed table formatting 2025-01-17 15:52:19 +03:00
b6490ca65a fix(docs): Fixed text length in markdown 2025-01-17 15:49:28 +03:00
9bc6c0a203 feat(server): Made a new Root with available currencies to collect and first and last collection date 2025-01-17 15:11:29 +03:00
fff7cebdb7 chore(collect-currency): Removed checking for duplicate currency exchange rates over time. 2025-01-17 14:49:56 +03:00
3720c3d66f fix(server): correct currency conversion with large numbers and rounding
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been cancelled
Create and publish a Docker image / build-and-push-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
2025-01-09 13:12:57 +03:00
20773385f4 fix(chart): Corrects chart size for large number of dates 2025-01-09 13:12:57 +03:00
1a3c751abc fix(chart): Added a condition, if analytics is disabled in the config, the collection will be 2025-01-09 13:12:57 +03:00
c26cedf5ed Revert "chore(readme): update year license"
Some checks failed
Deploy docs / deploy (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-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
This reverts commit 266a2ae389.
2025-01-07 21:00:23 +03:00
266a2ae389 chore(readme): update year license 2025-01-07 20:08:04 +03:00
d3f9652d8a feat(chart): The size of the chart now increases every 5 dates in the list
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been cancelled
Create and publish a Docker image / build-and-push-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
2025-01-05 00:34:44 +03:00
bc1e05f575 chore(chart): add gitignore 2025-01-04 23:52:38 +03:00
0e8575a04f chore(nginx): add route /api/getRate/ to nginx
Some checks are pending
Create and publish a Docker image / build-and-push-server (push) Waiting to run
Create and publish a Docker image / build-and-push-chart (push) Waiting to run
Create and publish a Docker image / build-and-push-CR (push) Waiting to run
Deploy docs / deploy (push) Waiting to run
2025-01-04 14:17:35 +03:00
04a2d18eb3 chore(config): update example config for crypto
Some checks are pending
Create and publish a Docker image / build-and-push-server (push) Waiting to run
Create and publish a Docker image / build-and-push-chart (push) Waiting to run
Create and publish a Docker image / build-and-push-CR (push) Waiting to run
Deploy docs / deploy (push) Waiting to run
2025-01-03 13:59:26 +03:00
eae175efa9 chore(readme): Removed notice of removal of cryptocurrency rate collection 2025-01-03 13:34:06 +03:00
40331c909c chore(docker): Rejection of alpine version of python due to long build time and dependency issues 2025-01-03 13:28:21 +03:00
f1f26889ea feat(nginx): redirect http to https and allow cors. Deleted route /api/configuration 2025-01-03 12:46:09 +03:00
82ad228c1a feat(collect-currency): Bringing back cryptocurrency rate collection 2025-01-02 21:19:08 +03:00
e2cb35a880 fix(server): Fixed bug, if conv_amount was not specified, it was still shown in the response
Some checks are pending
Create and publish a Docker image / build-and-push-server (push) Waiting to run
Create and publish a Docker image / build-and-push-chart (push) Waiting to run
Create and publish a Docker image / build-and-push-CR (push) Waiting to run
Deploy docs / deploy (push) Waiting to run
2025-01-02 13:36:13 +03:00
2c5a80263b chore(docs): replace icon svg to png
Some checks failed
Create and publish a Docker image / build-and-push-server (push) Has been cancelled
Create and publish a Docker image / build-and-push-chart (push) Has been cancelled
Create and publish a Docker image / build-and-push-CR (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
2024-12-31 14:17:04 +03:00
74 changed files with 9916 additions and 1099 deletions

View file

@ -1,2 +1,3 @@
node_modules/
docs/
docs/
.gitignore

View file

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

View file

@ -1,46 +1,66 @@
name: Create and publish a Docker image
name: Create and publish Docker images
on:
push:
branches:
- main
release:
types:
- published
pull_request:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}/kekkai
jobs:
changes:
name: Detect changed files
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
server: ${{ steps.filter.outputs.server }}
chart: ${{ steps.filter.outputs.chart }}
CR: ${{ steps.filter.outputs.CR }}
web: ${{ steps.filter.outputs.web }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
server:
- 'server/**'
chart:
- 'chart/**'
CR:
- 'collect-currency/**'
web:
- 'web/**'
build-and-push-server:
needs: changes
if: ${{ needs.changes.outputs.server == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
images: ${{ env.IMAGE_PREFIX }}-server
tags: |
type=raw,value=latest
type=ref,event=branch
type=sha
- uses: docker/build-push-action@v5
with:
file: Dockerfile-server
context: .
@ -50,33 +70,30 @@ jobs:
platforms: linux/amd64,linux/arm64
build-and-push-chart:
needs: changes
if: ${{ needs.changes.outputs.chart == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
images: ${{ env.IMAGE_PREFIX }}-chart
tags: |
type=raw,value=latest
type=ref,event=branch
type=sha
- uses: docker/build-push-action@v5
with:
file: ./chart/Dockerfile
context: ./chart
@ -85,39 +102,68 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
build-and-push-CR:
needs: changes
if: ${{ needs.changes.outputs.CR == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
images: ${{ env.IMAGE_PREFIX }}-collect-currency
tags: |
type=raw,value=latest
type=ref,event=branch
type=sha
- uses: docker/build-push-action@v5
with:
file: Dockerfile-collect-currency
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
platforms: linux/amd64,linux/arm64
build-and-push-web:
needs: changes
if: ${{ needs.changes.outputs.web == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_PREFIX }}-web
tags: |
type=raw,value=latest
type=ref,event=branch
type=sha
- uses: docker/build-push-action@v5
with:
file: Dockerfile-web
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64

View file

@ -1,29 +0,0 @@
name: Deploy docs
on:
push:
branches:
- master
- main
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure Git Credentials
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
- uses: actions/setup-python@v5
with:
python-version: 3.x
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v4
with:
key: mkdocs-material-${{ env.cache_id }}
path: .cache
restore-keys: |
mkdocs-material-
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force

12
.gitignore vendored
View file

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

31
Dockerfile-web Normal file
View file

@ -0,0 +1,31 @@
FROM node:20-alpine AS shared-config
# Install shared/config dependencies
WORKDIR /
COPY ./shared/config/package*.json .
RUN npm install
FROM node:20-alpine AS shared-logger
# Install the shared/logger dependencies
WORKDIR /
COPY ./shared/logger/package*.json .
RUN npm install
FROM node:20-alpine AS web
WORKDIR /
# Install web dependencies
COPY ./web/package*.json ./
RUN npm install
# Copying shared dependencies, without database
COPY --from=shared-config /node_modules /node_modules
COPY --from=shared-logger /node_modules /node_modules
# Copy all the other files
COPY ./web/ ./
COPY ./shared/ ./shared/
EXPOSE 3050
CMD ["node", "main.js"]

View file

@ -14,9 +14,6 @@
</a>
</div>
> [!IMPORTANT]
> After Nov. 1, the coinAPI service removed the plan with free 100 requests. At the moment, cryptocurrency collection is limited (free of charge). Some cryptocurrencies are possible to get through DuckDuckGO, but the list of available currencies is unknown. More details can be found in the [discussion](https://github.com/Redume/Kekkai/discussions/1)
> [!NOTE]
> The first free Open-Source Tool for Saving Historical Currency data
@ -50,4 +47,4 @@
- [eslint/eslint](https://github.com/eslint/eslint) — [MIT](https://github.com/eslint/eslint/blob/main/LICENSE)
- [prettier/prettier](https://github.com/prettier/prettier) — [MIT](https://github.com/prettier/prettier/blob/main/LICENSE)
- [squidfunk/mkdocs-material](https://github.com/squidfunk/mkdocs-material?tab=MIT-1-ov-file) — [MIT](https://github.com/squidfunk/mkdocs-material/blob/master/LICENSE)
- [withastro/starlight](https://github.com/withastro/starlight) - [MIT](https://github.com/withastro/starlight/blob/main/LICENSE)

1
chart/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
__pycache__

View file

@ -1,11 +1,9 @@
FROM python:3.13-alpine
FROM python:3.13
WORKDIR /
COPY . .
RUN apk add --no-cache gcc g++ musl-dev python3-dev libffi-dev openssl-dev postgresql-dev
RUN pip3 install --no-cache-dir --upgrade -r ./requirements.txt
EXPOSE 3030

View file

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

View file

@ -6,6 +6,8 @@ based on historical data retrieved from the database.
from datetime import datetime
from matplotlib import pyplot as plt
from scipy.interpolate import make_interp_spline
import numpy as np
from function.gen_unique_name import generate_unique_name
from database.server import create_pool
@ -44,7 +46,8 @@ async def create_chart(
async with pool.acquire() as conn:
data = await conn.fetch(
'SELECT date, rate FROM currency '
'WHERE (date BETWEEN $1 AND $2) AND from_currency = $3 AND conv_currency = $4 ORDER BY date',
'WHERE (date BETWEEN $1 AND $2) ' +
'AND from_currency = $3 AND conv_currency = $4 ORDER BY date',
start_date_obj,
end_date_obj,
from_currency.upper(),
@ -57,28 +60,41 @@ async def create_chart(
date, rate = [], []
for row in data:
date.append(str(row['date']))
rate.append(row['rate'])
date.append(row[0])
rate.append(row[1])
if rate[0] < rate[-1]:
plt.plot(date, rate, color='green', marker='o')
elif rate[0] > rate[-1]:
plt.plot(date, rate, color='red', marker='o')
else:
plt.plot(date, rate, color='grey')
spline = make_interp_spline(range(len(date)), rate, k=2)
x = np.arange(len(date))
plt.xlabel('Date')
plt.ylabel('Rate')
newx_2 = np.linspace(0, len(date) - 1, 200)
newy_2 = spline(newx_2)
fig, ax = plt.subplots(figsize=(15, 6))
fig = plt.gcf()
fig.set_size_inches(18.5, 9.5)
for label in (ax.get_xticklabels() + ax.get_yticklabels()):
label.set_fontsize(10)
ax.set_xticks(np.linspace(0, len(date) - 1, 10))
ax.set_xticklabels(
[
date[int(i)].strftime('%Y-%m-%d')
for i in np.linspace(0, len(date) - 1, 10).astype(int)
]
)
name = await generate_unique_name(
f'{from_currency.upper()}_{conv_currency.upper()}',
datetime.now()
)
fig.savefig(f'../charts/{name}.png')
if rate[0] < rate[-1]:
plt.plot(newx_2, newy_2, color='green')
elif rate[0] > rate[-1]:
plt.plot(newx_2, newy_2, color='red')
else:
plt.plot(newx_2, newy_2, color='grey')
plt.savefig(f'../charts/{name}.png')
fig.clear()
return name

View file

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

View file

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

View file

@ -1,7 +1,9 @@
matplotlib~=3.9.1
PyYAML~=6.0.1
hjson~=3.1.0
uvicorn~=0.29.0
fastapi[standard]~=0.115.2
starlette~=0.40.0
user_agents==2.2.0
asyncpg~=0.30.0
asyncpg~=0.30.0
scipy~=1.15.2
numpy~=2.2.0

View file

@ -1,20 +1,20 @@
# pylint: disable=R0801
"""
This module provides a function for loading a YAML configuration file.
The function reads the file content and returns it as a Python dictionary.
Parsing and converting HJSON config to JSON
"""
import yaml
import hjson
import json
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
returns it in dictionary format, making it accessible for use in
the application.
params: file_path (str): The path to the HJSON file.
:param file_path: The path to the YAML configuration file to be loaded.
:return: A dictionary containing the parsed content of the YAML file.
returns str: The JSON string formatted with indentation.
"""
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

@ -3,6 +3,7 @@ const config = require('../shared/config/src/main.js')();
const cron = require('cron-validator');
const save_fiat = require('./save_fiat');
const save_crypto = require('./save_crypto');
const logger = require('../shared/logger/src/main.js');
async function validateSchedule(schedule) {
@ -12,14 +13,15 @@ async function validateSchedule(schedule) {
}
async function initialize() {
await require('../shared/database/src/create_table')();
//await require('../shared/database/src/create_table')();
}
async function runTasks() {
await Promise.all([save_fiat()]);
await Promise.all([save_fiat(), save_crypto()]);
}
async function main() {
console.log(config['currency']['collecting']['schedule'])
await initialize();
await validateSchedule(config['currency']['collecting']['schedule']);
@ -31,8 +33,6 @@ async function main() {
);
}
main().catch((err) => {
logger.error('Error in main execution:', err);
});
main();
module.exports = { main };

View file

@ -0,0 +1,47 @@
const pool = require('../shared/database/src/postgresql.js');
const axios = require('axios');
const config = require('../shared/config/src/main.js')();
const logger = require('../shared/logger/src/main.js');
async function save_crypto() {
if (!config['currency']['collecting']['crypto']) return;
config['currency']['crypto'].forEach((value) =>
config['currency']['crypto'].forEach(async (pair) => {
if (value === pair) return;
await axios.get('https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest', {
timeout: 3000,
params: {
'symbol': value,
'convert': pair,
},
headers: {
'X-CMC_PRO_API_KEY': config['currency']['collecting']['crypto_apikey'],
}
})
.then(async (res) => {
const data = res['data']['data'][value]['quote'][pair];
logger.debug(JSON.stringify(data, null, 4));
const point = data['price'].toString().indexOf('.') + 4;
await pool.query(
`INSERT INTO currency (from_currency, conv_currency, rate, date) VALUES ($1, $2, $3, $4)`,
[
value,
pair,
data['price'].toString().slice(0, point),
new Date(data['last_updated']).toISOString().substring(0, 10),
],
);
})
.catch((err) => {
logger.error(err);
});
})
);
}
module.exports = save_crypto;

View file

@ -31,17 +31,6 @@ async function save_fiat() {
const point =
data['to'][0]['mid'].toString().indexOf('.') + 4;
const db = await pool.query(
'SELECT * FROM currency WHERE ' +
'from_currency = $1 AND conv_currency = $2 AND date = $3',
[
value,
pair,
new Date(data['timestamp']).toISOString().substring(0, 10),
],
);
if (db['rows'][0]) return;
await pool.query(
`INSERT INTO currency (from_currency, conv_currency, rate, date) VALUES ($1, $2, $3, $4)`,
[

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,36 +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]
schedule: '30 8 * * *' # Currency collection schedule in crontab format [String]
fiat: # List of fiat currency to save the exchange rate [Array]
- USD
- RUB
- EUR
- UAH
- TRY
- KZT

View file

@ -1,17 +1,19 @@
services:
nginx:
image: nginx:latest
image: nginx:alpine
ports:
- '80:80'
- '443:443'
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./nginx/:/etc/nginx/
- ./CertSSL:/etc/nginx/ssl
- ./robots.txt:/etc/nginx/robots.txt
- ./assets/logo.png:/etc/nginx/assets/logo.png
- ./docs/dist:/etc/nginx/dist
depends_on:
- server
- chart
- web
- docs
server:
build:
@ -22,7 +24,7 @@ services:
- '3000:3000'
volumes:
- './CertSSL:/CertSSL'
- './config.yaml:/config.yaml'
- './config.hjson:/config.hjson'
depends_on:
- postgres
@ -34,18 +36,34 @@ services:
- '3030:3030'
volumes:
- './CertSSL:/CertSSL'
- './config.yaml:/config.yaml'
- './config.hjson:/config.hjson'
depends_on:
postgres:
condition: service_healthy
web:
build:
context: ./
dockerfile: Dockerfile-web
restart: unless-stopped
ports:
- '3050:3050'
volumes:
- './CertSSL:/CertSSL'
- './config.hjson:/config.hjson'
docs:
build:
context: ./docs
dockerfile: Dockerfile
collect-currency:
build:
context: ./
dockerfile: Dockerfile-collect-currency
restart: unless-stopped
volumes:
- './config.yaml:/config.yaml'
- './config.hjson:/config.hjson'
depends_on:
- postgres

21
docs/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

9
docs/Dockerfile Normal file
View file

@ -0,0 +1,9 @@
FROM node:20-alpine
WORKDIR /
COPY ./package*.json .
RUN npm install
CMD ["npm", "run", "build"]

66
docs/astro.config.mjs Normal file
View file

@ -0,0 +1,66 @@
// @ts-check
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
// https://astro.build/config
export default defineConfig({
integrations: [
starlight({
title: 'Kekkai',
social: { github: 'https://github.com/redume/kekkai' },
editLink: { baseUrl: 'https://github.com/redume/kekkai/edit/main/docs/' },
sidebar: [
{
label: 'Getting started',
items: [
{
label: 'Docker',
slug: 'docs/getting-started/docker',
badge: 'recommended',
},
{
label: 'Contributing', slug: 'docs/getting-started/contributing'
},
],
},
{
label: 'Endpoints',
items: [
{
label: 'Endpoints list', slug: 'docs/endpoints/endpoints-list'
},
{
label: 'Get currency rate - /api/getRate',
slug: 'docs/endpoints/getrate'
},
{
label: 'Create charts - /api/getChart',
slug: 'docs/endpoints/create-chart'
},
{
label: 'Get metadata - /api/metadata',
slug: 'docs/endpoints/metadata'
}
],
},
{
label: 'Config',
items: [
{
label: 'Configure .env',
slug: 'docs/config/config-env'
},
{
label: 'Configure config.hjson',
slug: 'docs/config/config-hjson'
},
{
label: 'Configure Nginx',
slug: 'docs/config/config-nginx'
}
],
},
],
}),
],
});

View file

@ -1,27 +0,0 @@
Kekkai is used by `Nginx` to redirect to the correct microservice.
## Change domain
Change localhost to your `domain` or `ipv4` based on your needs. If you need to use Kekkai locally, you don't need to change anything,
??? note
```
...
server_name localhost; # Your domain
...
```
## Change name for SSL
This is where the name of the SSL files changes. This needs to be edited if you have a different one
Change `privkey.pem` and `fullchain.pem` to the names of the files you have.
??? note
```
...
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
...
```

View file

@ -1,24 +0,0 @@
Kekkai can be configured using the `.env` file in the working directory. `.env.example`.
`.env` config is used to configure PosgreSQL running in Docker Compose.
!!! info
If you are not using Docker Compose, you do not need to edit this config
??? note "Example file `.env.example`"
```
# Connection secret for postgres. You should change it to a random password
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
POSTGRES_PASSWORD=my_password
# If you do not know what you are doing, then you should not edit the values below
###################################################################################
POSTGRES_DB=kekkai
DB_HOST=postgres
POSTGRES_USER=postgres
```
This config only edits the password for PosgreSQL.
Please use only the characters `A-Za-z0-9`, without special characters or spaces

View file

@ -1,117 +0,0 @@
Kekkai can be configured using the `config.yaml` file in the working directory. `config.example.yaml`.
??? "Example file `config.example.yaml`"
```
# For more information, see the documentation
# https://kekkai-docs.redume.su/
database:
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'
cert: '/CertSSL/fullchain.pem'
work: true
log:
print: true
level: 'info'
analytics:
plausible_api: 'https://plausible.io/api/event/'
plausible_domain: 'PLAUSIBLE_DOMAIN'
plausible_token: 'PLAUSIBLE_TOKEN'
work: true
currency:
chart:
save: false
collecting:
fiat: true
schedule: '30 8 * * *'
fiat:
- USD
- RUB
- EUR
- UAH
- TRY
- KZT
```
## Database
Kekkai is used as a `PostgreSQL` database.
!!! info
If you installed Kekkai via Docker Compose, then install it in the `database.host` value of `postgres`.
The rest of the data does not have to be filled in. They need to be filled in `.env`.
What should it look like:
```yaml
database:
...
host: 'postgres'
...
...
```
## Server
!!! info
If you installed Kekkai via Docker Compose, then changing `server.host`, `server.ssl` is not recommended.
### SSL
Create a folder CertSSL to store your certificates
```shell
mkdir CertSSL
```
Copy your certificates to the CertSSL folder.
It is recommended to rename the certificates to `privkey.pem` and `fullchain.pem`. If this is not possible, you need to change the SSL name in `nginx.conf` (if using Docker Compose)
## Analytics
Kekkai uses [`Plausbile`](https://plausible.io/) as an analyst. Minimal data is transferred for anilithics. Such as: browser, OS, status code, url, where the user came from. Most of the data is built on User Agent. It is possible to disable analytics in Kekkai.
??? note
```yaml
...
analytics:
plausible_api: 'https://plausible.io/api/event/'
plausible_domain: 'PLAUSIBLE_DOMAIN'
plausible_token: 'PLAUSIBLE_TOKEN'
work: true
...
```
- `plausible_api`: This is where the Plausible instance is specified. The official instance is specified by default.
- `plausible_domain`: Kekkai Instance Domain. It should be added to Plausible first, and then to the config. You can add the domain [here](https://plausible.io/sites/new?flow=provisioning).
- `plausible_token`: Api token for authorization and sending requests. You can create it [here](https://plausible.io/settings/api-keys).
- `work`: Enable or disable analytics.
## Currency
`DuckDuckGo` (fiat currency collection) is used to collect currency rates.
??? note
```yaml
...
currency:
chart:
save: false
collecting:
fiat: true
schedule: '30 8 * * *'
fiat:
- USD
- RUB
- EUR
- UAH
- TRY
- KZT
```
- `currency.chart.save`: Enable or disable saving graphs.
- `currency.collecting`: Enable or disable cryptocurrency or fiat currency exchange rate collection.
- `currency.schedule`: Currency collection interval (Configurable via cron. It is recommended to use [crontab.guru](https://crontab.guru), not supported in `Non-standard format`, like `@daily`).
- `currency.fiat`: A list of fiat currencies that will be saved to the database.

View file

@ -1,136 +0,0 @@
Creating a currency rate chart.
## Creating a graph for a certain period
### Request
=== "Shell"
=== "Curl"
```bash
curl --request GET \
--url https://kekkai-api.redume.su/api/getChart/week?from_currency=RUB&conv_currency=USD
```
=== "Python"
=== "Request"
```python
import requests
res = requests.get('https://kekkai-api.redume.su/api/getChart/week', {
'from_currency': 'USD',
'conv_currency': 'RUB',
}, timeout=3)
print(res.json())
```
=== "Node.JS"
=== "Axios"
```js
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getChart/week', {
timeout: 3000,
'from_currency': 'USD',
'conv_currency': 'RUB',
})
.then((res) => {
console.log(res['data']);
})
.catch((err) => {
console.error(err);
});
```
### Query params
| Parameter | Description |
|----------------|-------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
### URL params
| Parameter | Description |
|---------------|-------------------------------------------------------------------------|
| `period` | Available parameters: `week`, `month`, `quarter`, `year` |
`*` - Required arguments
### Response
!!! info "Output"
```json
{
"status": 201,
"message": "http://kekkai-api.redume.su/static/charts/RUB_USD_20241108_DQVDN7.png"
}
```
## Creating a schedule for specific days
### Request
=== "Shell"
=== "Curl"
```bash
curl --request GET \
--url https://kekkai-api.redume.su/api/getChart/?from_currency=RUB&conv_currency=USD&start_date=2024-10-31&end_date=2024-11-08
```
=== "Python"
=== "Request"
```python
import requests
res = requests.get('https://kekkai-api.redume.su/api/getChart/', {
'from_currency': 'USD',
'conv_currency': 'RUB',
'start_date': '2024-10-31',
'end_date': '2024-11-08'
}, timeout=3)
print(res.json())
```
=== "Node.JS"
=== "Axios"
```js
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getChart/', {
timeout: 3000,
'from_currency': 'USD',
'conv_currency': 'RUB',
'start_date': '2024-10-31',
'end_date': '2024-11-08'
})
.then((res) => {
console.log(res['data']);
})
.catch((err) => {
console.error(err);
});
```
### Query params
| Parameter | Description |
|----------------|-------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
| `start_date`* | Start date of the period in the format `YYYYY-DD-MM` |
| `end_date`* | Period end date in the format `YYYYY-DD-MM` |
`*` - Required arguments
### Response
!!! info "Output"
```json
{
"status": 400,
"message": "http://kekkai-api.redume.su/static/charts/RUB_USD_20241108_1T2RI3.png"
}
```
## What the name of the chart file consists of
Example: ``.../RUB_USD_20241108_DQVDN7.png``
- `RUB_USD` - Name of currencies.
- `20241108` - Schedule request date in `YYYMMDD` format.
- `DQVDN7` - Random file character identifier.
All charts are in the charts folder, which is in the root directory (`./kekkai/chart`)

View file

@ -1,164 +0,0 @@
Currencies are identified by standard three-letter `ISO 4217` currency codes.
## Getting the currency rate for a certain day.
### Request
=== "Shell"
=== "Curl"
```bash
curl --request GET \
--url https://kekkai-api.redume.su/api/getRate/?from_currency=RUB&conv_currency=USD&date=2024-10-16
```
=== "Python"
=== "Requests"
```py
import requests
res = requests.get('https://kekkai-api.redume.su/api/getRate/', {
'from_currency': 'RUB',
'conv_currency': 'USD',
'date': '2024-10-16',
}, timeout=3)
print(res.json())
```
=== "Node.JS"
=== "Axios"
```js
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getRate/', {
timeout: 3000,
'from_currency': 'RUB',
'conv_currency': 'USD',
'date': '2024-10-16',
}
)
.then((res) => {
console.log(JSON.stringify(res.json()));
})
.catch((err) => {
console.error(err);
});
```
### Query Parameters
| Parameter | Description |
|-----------------|------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
| `date`* | Currency rate date in the format `YYYYY-DD-MM` |
| `conv_amount` | Multiplier for number conversion (Optional)
`*` - Required arguments
### Response
!!! info "Output"
```json
[
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-17T00:00:00.000Z"
}
]
```
## Get currency exchange rate for a certain period
Getting the list of the array with currency rate for a certain period of time.
### Request
=== "Shell"
=== "Curl"
```bash
curl --request GET \
--url https://kekkai-api.redume.su/api/getRate/?from_currency=RUB&conv_currency=USD&start_date=2024-10-16&end_date=2024-10-20
```
=== "Python"
=== "Requests"
```py
import requests
res = requests.get('https://kekkai-api.redume.su/api/getRate/', {
'from_currency': 'RUB',
'conv_currency': 'USD',
'start_date': '2024-10-16',
'end_date': '2024-10-20',
}, timeout=3)
print(res.json())
```
=== "Node.JS"
=== "Axios"
```js
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getRate/', {
timeout: 3000,
'from_currency': 'RUB',
'conv_currency': 'USD',
'start_date': '2024-10-16',
'end_date': '2024-10-20',
}
)
.then((res) => {
console.log(res['data']);
})
.catch((err) => {
console.error(err);
});
```
### Query params
| Parameter | Description |
|------------------|-------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
| `start_date`* | Start date of the period in the format `YYYYY-DD-MM` |
| `end_date`* | Period end date in the format `YYYYY-DD-MM` |
`*` - Required arguments
### Response
!!! info "Output"
```json
[
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-17T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-18T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-19T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-20T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-21T00:00:00.000Z"
}
]
```

View file

@ -1,15 +0,0 @@
## API Base URL
All requests to our API should be directed to the URL below:
```
https://kekkai-api.redume.su/api/
```
## API Endpoints
Kekkai has 3 API endpoints: `getRate`, `getChart` and `configurations`. Below you will find a list of parameters that each endpoint requires and a description of what the API does.
| Service | API Endpoint | Description |
|--------------|--------------------------------------------------------|--------------------------------------------------------------------|
| Get Rate | `https://kekkai-api.redume.su/api/getRate/` | Get currency exchange rate for a specific day or period |
| Create Chart | `https://kekkai-api.redume.su/api/getChart` | Creating a chart with exchange rate |

View file

@ -1,76 +0,0 @@
Docker Compose is the recommended method to run Kekkai in production. Below are the steps to deploy Kekkai with Docker Compose.
Kekkai requires Docker Compose version 2.x.
### Steps 1 - Preparing files
```shell
git clone https://github.com/Redume/Kekkai.git
```
```shell
cd Kekkai
```
### Steps 2 - Change config files
??? note "Nginx Configuration"
In `nginx.conf`, you need to specify your domain or ipv4 address
```
...
listen 443 ssl;
server_name localhost; # Your domain
...
```
To set up SSL
```bash
mkdir CertSLL
```
After that, copy the SSL certificates to the `CertSSL` folder, if the names are different,
then change either the name of the certificates or in `nginx.conf`
```
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
```
??? note "The main config is `config.sample.yaml` for Kekkai"
```yaml
database:
user: 'DATABASE_USERNAME'
password: 'DATABASE_PASSWORD'
host: 'DATABASE_HOST'
name: 'DATABASE_NAME'
port: 5432
...
```
Fill in the data in the `database` item, as well as in the `.env` config
??? note "`.env.sample` config for PostgreSQL"
```.env
# Connection secret for postgres. You should change it to a random password
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
POSTGRES_PASSWORD=my_password
# If you do not know what you are doing, then you should not edit the values below
###################################################################################
POSTGRES_DB=kekkai
DB_HOST=postgres
POSTGRES_USER=postgres
```
- Populate custom database information if necessary.
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publically exposed, so this password is only used for - local authentication. To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`.
!!! note
After editing, rename the config files by removing `.sample` in the name
### Steps 3 - Start the containers
```shell title='Start the containers using docker compose command'
docker compose -f "docker-compose.yaml" up -d --build
```

View file

@ -1,72 +0,0 @@
For full use, you need to install `Node.JS v20`, `PostgreSQL v15`, `Python v13.3`, `Nginx`
### Steps 1 - Preparing files
```shell
git clone https://github.com/Redume/Kekkai.git
```
```shell
cd Kekkai
```
### Steps 2 - Change config files
??? note "Nginx Configuration"
In `nginx.conf`, you need to specify your domain or ipv4 address
```nginx
...
listen 443 ssl;
server_name localhost; # Your domain
...
```
??? note "The main config is `config.sample.yaml` for Kekkai"
```yaml
database:
user: 'DATABASE_USERNAME'
password: 'DATABASE_PASSWORD'
host: 'DATABASE_HOST'
name: 'DATABASE_NAME'
port: 5432
...
```
Fill in the data in the `database` item, as well as in the `.env` config
### Steps 3 - Install libs
Install library. In `/shared/logger`, `/shared/config`, `/shared/database`, `/collect-currency`,` /server`,
the required node.JS libraries In each of the directories, you need to write this command
```shell
npm install
```
and install python libs
```shell
pip install -r requirements.txt
```
Start the nginx service
```shell
sudo systemctl start nginx.service
```
### Steps 4 - Launch Services
Launch each of the services
There are all three services `MainService`, `Collect-currency`, `Chart`
- `MainService` is an API for getting the exchange rate
```shell
cd server & node .
```
- `Collect-Currency` is a service for collecting and save the rate in a database
```shell
cd collect-currency/src/ && node .
```
- `ChartService` is a service for creating currency rate charts
```shell
python3 main.py
```

6933
docs/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

17
docs/package.json Normal file
View file

@ -0,0 +1,17 @@
{
"name": "docs",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/starlight": "^0.32.1",
"astro": "^5.1.5",
"sharp": "^0.32.5"
}
}

View file

@ -1,12 +1,12 @@
<svg width="157" height="123" viewBox="0 0 157 123" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="157" height="122" viewBox="0 0 157 122" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_179_28)">
<path d="M54.8999 97.6014V8.00143H79.7319V44.3534L110.58 8.00143H140.02L106.1 46.5294L141.172 97.6014H111.348L88.8199 64.3214L79.7319 74.4334V97.6014H54.8999Z" fill="#297BCD"/>
<path d="M54.8999 97V7.39999H79.7319V43.752L110.58 7.39999H140.02L106.1 45.928L141.172 97H111.348L88.8199 63.72L79.7319 73.832V97H54.8999Z" fill="#297BCD"/>
</g>
<g opacity="0.7" filter="url(#filter1_dd_179_28)">
<path d="M38.76 110.544V97.744C25.6187 96.3787 14.0987 92.1547 4.2 85.072L15.72 68.048C23.912 73.7653 31.848 77.264 39.528 78.544V63.184C28.776 60.7093 20.8827 57.3813 15.848 53.2C10.8987 49.0187 8.424 43.0453 8.424 35.28C8.424 27.6 11.112 21.3707 16.488 16.592C21.864 11.728 29.3733 8.95466 39.016 8.272V0.975998L52.328 0.975998V8.656C62.1413 9.76533 71.144 13.0933 79.336 18.64L68.84 35.792C63.8053 32.208 58.088 29.648 51.688 28.112V42.96C62.2693 45.4347 69.992 48.8053 74.856 53.072C79.8053 57.3387 82.28 63.3547 82.28 71.12C82.28 78.8 79.5493 84.9867 74.088 89.68C68.712 94.3733 61.3733 97.1467 52.072 98V110.544H38.76ZM51.56 79.312C57.2773 78.7147 60.136 76.4533 60.136 72.528C60.136 70.736 59.496 69.328 58.216 68.304C57.0213 67.1947 54.8027 66.1707 51.56 65.232V79.312ZM39.656 40.784V26.96C33.8533 27.472 30.952 29.6907 30.952 33.616C30.952 35.3227 31.5493 36.7307 32.744 37.84C34.024 38.864 36.328 39.8453 39.656 40.784Z" fill="#297BCD"/>
<path d="M38.76 109.905V97.1055C25.6187 95.7401 14.0987 91.5161 4.2 84.4334L15.72 67.4094C23.912 73.1268 31.848 76.6254 39.528 77.9054V62.5454C28.776 60.0708 20.8827 56.7428 15.848 52.5614C10.8987 48.3801 8.424 42.4068 8.424 34.6414C8.424 26.9614 11.112 20.7321 16.488 15.9534C21.864 11.0894 29.3733 8.31611 39.016 7.63345V0.337448L52.328 0.337448V8.01745C62.1413 9.12678 71.144 12.4548 79.336 18.0014L68.84 35.1534C63.8053 31.5694 58.088 29.0095 51.688 27.4734V42.3214C62.2693 44.7961 69.992 48.1668 74.856 52.4334C79.8053 56.7001 82.28 62.7161 82.28 70.4814C82.28 78.1614 79.5493 84.3481 74.088 89.0414C68.712 93.7348 61.3733 96.5081 52.072 97.3615V109.905H38.76ZM51.56 78.6734C57.2773 78.0761 60.136 75.8148 60.136 71.8894C60.136 70.0974 59.496 68.6894 58.216 67.6654C57.0213 66.5561 54.8027 65.5321 51.56 64.5934V78.6734ZM39.656 40.1454V26.3214C33.8533 26.8334 30.952 29.0521 30.952 32.9774C30.952 34.6841 31.5493 36.0921 32.744 37.2014C34.024 38.2254 36.328 39.2068 39.656 40.1454Z" fill="#297BCD"/>
</g>
<defs>
<filter id="filter0_d_179_28" x="54.8999" y="8.00146" width="101.372" height="101.7" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<filter id="filter0_d_179_28" x="54.8999" y="7.40002" width="101.372" height="101.7" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="10" dy="7"/>
@ -16,7 +16,7 @@
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_179_28"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_179_28" result="shape"/>
</filter>
<filter id="filter1_dd_179_28" x="0.199951" y="0.975952" width="97.1801" height="121.668" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<filter id="filter1_dd_179_28" x="0.199951" y="0.337402" width="97.1801" height="121.668" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="10" dy="7"/>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before After
Before After

View file

@ -1,14 +0,0 @@
{
"yaml.schemas": {
"https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml"
},
"yaml.customTags": [
"!ENV scalar",
"!ENV sequence",
"!relative scalar",
"tag:yaml.org,2002:python/name:material.extensions.emoji.to_svg",
"tag:yaml.org,2002:python/name:material.extensions.emoji.twemoji",
"tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format",
"tag:yaml.org,2002:python/object/apply:pymdownx.slugs.slugify mapping"
]
}

View file

@ -0,0 +1,7 @@
import { defineCollection } from 'astro:content';
import { docsLoader } from '@astrojs/starlight/loaders';
import { docsSchema } from '@astrojs/starlight/schema';
export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
};

View file

@ -0,0 +1,31 @@
---
title: Configure .env
---
import { Aside, Code } from '@astrojs/starlight/components';
Kekkai can be configured using the `.env` file in the working directory. `.env.example`.
`.env` config is used to configure PosgreSQL running in Docker Compose.
<Aside>
If you are not using Docker Compose, you do not need to edit this config
</Aside>
<Code code=
'
# Connection secret for postgres. You should change it to a random password
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
POSTGRES_PASSWORD=my_password
# If you do not know what you are doing, then you should not edit the values below
###################################################################################
POSTGRES_DB=kekkai
DB_HOST=postgres
POSTGRES_USER=postgres
' title='.env.example'/>
This config only edits the password for PosgreSQL.
Please use only the characters `A-Za-z0-9`, without special characters or spaces

View file

@ -0,0 +1,182 @@
---
title: Configure config.hjson
---
import { Aside } from '@astrojs/starlight/components';
Kekkai can be configured using the `config.hjson` file in the working directory.
`config.example.hjson`.
```hjson
# For more information, see the documentation
# https://kekkai-docs.redume.su/
{
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
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
]
}
}
```
## Database
Kekkai is used as a `PostgreSQL` database.
<Aside>
If you installed Kekkai via `Docker Compose`,
set it to `database.host` for postgres.
The password (database.password) should be the same as in `.env`,
the rest of the data doesn't need to be filled in,
it should be in `.env`
What should it look like:
```hjson
database:
{
...
password: PASSWORD_FROM_ENV
host: postgres
...
}
...
```
</Aside>
## Server
<Aside>
If you installed Kekkai via Docker Compose, then changing `server.host`, `
server.ssl` is not recommended.
</Aside>
### SSL
Create a folder CertSSL to store your certificates
```shell
mkdir CertSSL
```
Copy your certificates to the CertSSL folder.
It is recommended to rename the certificates to `privkey.pem` and `fullchain.pem`.
If this is not possible,
you need to change the SSL name in `nginx.conf` (if using Docker Compose)
## Analytics
Kekkai uses [`Plausbile`](https://plausible.io/) as an analyst.
Minimal data is transferred for anilithics.
Such as: browser, OS, status code, url, where the user came from.
Most of the data is built on User Agent.
It is possible to disable analytics in Kekkai.
```yaml
...
analytics:
plausible_api: 'https://plausible.io/api/event/'
plausible_domain: 'PLAUSIBLE_DOMAIN'
plausible_token: 'PLAUSIBLE_TOKEN'
enabled: true
...
```
- `plausible_api`: This is where the Plausible instance is specified.
The official instance is specified by default.
- `plausible_domain`: Kekkai Instance Domain.
It should be added to Plausible first, and then to the config.
You can add the domain [here](https://plausible.io/sites/new?flow=provisioning).
- `plausible_token`: Api token for authorization and sending requests.
You can create it [here](https://plausible.io/settings/api-keys).
- `enabled`: Enable or disable analytics.
## Currency
`DuckDuckGo` (fiat currency collection) and `CoinMarketCap` (cryptocurrency collection)
are used to collect currency rates.
```yaml
...
currency:
{
collecting:
{
fiat: true
crypto: false
schedule: 30 8 * * *
crypto_apikey: TOKEN
}
fiat:
[
USD
RUB
EUR
UAH
TRY
KZT
]
crypto:
[
ETH
TON
USDT
BTC
]
}
```
- `currency.chart.save`: Enable or disable saving graphs.
- `currency.collecting`: Enable or disable cryptocurrency
or fiat currency exchange rate collection.
- `currency.schedule`: Currency collection interval (Configurable via cron.
It is recommended to use [crontab.guru](https://crontab.guru),
not supported in `Non-standard format`, like `@daily`).
- `crypto.crypto_apiKey`: API-key from CoinMarketCap service
- `currency.fiat`: A list of fiat currencies that will be saved to the database.
- `currency.crypto`: A list of crypto currencies that will be saved to the database.

View file

@ -0,0 +1,32 @@
---
title: Nginx configuration
---
import { Code } from '@astrojs/starlight/components';
Kekkai is used by `Nginx` to redirect to the correct microservice.
## Change domain
Change localhost to your `domain` or `ipv4` based on your needs.
If you need to use Kekkai locally, you don't need to change anything,
<Code code='
...
server_name localhost; # Your domain
...
'title='nginx.conf'/>
## Change name for SSL
This is where the name of the SSL files changes. This needs to be edited if you have a different one
Change `privkey.pem` and `fullchain.pem` to the names of the files you have.
<Code code=
'
...
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
...
' title='nginx.conf'/>

View file

@ -0,0 +1,172 @@
---
title: Create Charts - /api/getChart
---
Creating a currency rate chart.
import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';
## Creating a graph for a certain period
### Request
<Tabs>
<TabItem label='Shell'>
<Tabs>
<TabItem label='curl'>
```shell
curl --request GET --url https://kekkai-api.redume.su/api/getChart/week?from_currency=RUB&conv_currency=USD
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Python'>
<Tabs>
<TabItem label='requests'>
```python
import requests
res = requests.get('https://kekkai-api.redume.su/api/getChart/week', {
'from_currency': 'USD',
'conv_currency': 'RUB',
}, timeout=3)
print(res.json())
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Node.JS'>
<Tabs>
<TabItem label='axios'>
```javascript
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getChart/week', {
timeout: 3000,
'from_currency': 'USD',
'conv_currency': 'RUB',
})
.then((res) => {
console.log(res['data']);
})
.catch((err) => {
console.error(err);
});
```
</TabItem>
</Tabs>
</TabItem>
</Tabs>
### Query params
| Parameter | Description |
|-------------------|-------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
### URL params
| Parameter | Description |
|---------------|-------------------------------------------------------------------------|
| `period` | Available parameters: `week`, `month`, `quarter`, `year` |
`*` - Required arguments
### Response
<Aside title='Output'>
```json
{
"status": 201,
"message": "http://kekkai-api.redume.su/static/charts/RUB_USD_20241108_DQVDN7.png"
}
```
</Aside>
## Creating a schedule for specific days
### Request
<Tabs>
<TabItem label='Shell'>
<Tabs>
<TabItem label='curl'>
```shell
curl --request GET --url https://kekkai-api.redume.su/api/getChart/?from_currency=RUB&conv_currency=USD&start_date=2024-10-31&end_date=2024-11-08
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Python'>
<Tabs>
<TabItem label='requests'>
```python
import requests
res = requests.get('https://kekkai-api.redume.su/api/getChart/', {
'from_currency': 'USD',
'conv_currency': 'RUB',
'start_date': '2024-10-31',
'end_date': '2024-11-08'
}, timeout=3)
print(res.json())
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Node.JS'>
<Tabs>
<TabItem label='axios'>
```javascript
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getChart/', {
timeout: 3000,
'from_currency': 'USD',
'conv_currency': 'RUB',
'start_date': '2024-10-31',
'end_date': '2024-11-08'
})
.then((res) => {
console.log(res['data']);
})
.catch((err) => {
console.error(err);
});
```
</TabItem>
</Tabs>
</TabItem>
</Tabs>
### Query params
| Parameter | Description |
|------------------|------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
| `start_date`* | Start date of the period in the format `YYYYY-DD-MM` |
| `end_date`* | Period end date in the format `YYYYY-DD-MM` |
`*` - Required arguments
### Response
<Aside title='Output'>
```json
{
"status": 201,
"message": "http://kekkai-api.redume.su/static/charts/RUB_USD_20250226_RX02RN.png"
}
```
</Aside>
## What the name of the chart file consists of
Example: ``.../RUB_USD_20241108_DQVDN7.png``
- `RUB_USD` - Name of currencies.
- `20241108` - Schedule request date in `YYYMMDD` format.
- `DQVDN7` - Random file character identifier.
All charts are in the charts folder, which is in the root directory (`/chart`)

View file

@ -0,0 +1,21 @@
---
title: Endpoints list
---
## API Base URL
All requests to our API should be directed to the URL below:
```
https://kekkai.redume.su/api/
```
## API Endpoints
Kekkai has 3 API endpoints: `getRate`, `getChart` and `metadata`.
Below you will find a list of parameters that each endpoint requires and a description of what the API does.
| Service | API Endpoint | Description |
|--------------|----------------------------------------------|---------------------------------------------------------------------------------------------|
| Get Rate | `https://kekkai-api.redume.su/api/getRate/` | Get currency exchange rate for a specific day or period |
| Create Chart | `https://kekkai-api.redume.su/api/getChart/` | Creating a chart with exchange rate |
| Metadata | `https://kekkai-api.redume.su/api/metadata/` | Shows the last and first dates of currency rate collection, as well as available currencies |

View file

@ -0,0 +1,199 @@
---
title: Get currency rate - /api/getRate
---
Currencies are identified by standard three-letter `ISO 4217` codes.
import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';
## Getting the currency rate for a certain day.
### Request
<Tabs>
<TabItem label='Shell'>
<Tabs>
<TabItem label='curl'>
```shell
curl --request GET --url https://kekkai-api.redume.su/api/getRate/?from_currency=RUB&conv_currency=USD&date=2024-10-16
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Python'>
<Tabs>
<TabItem label='requests'>
```python
import requests
res = requests.get('https://kekkai-api.redume.su/api/getRate/', {
'from_currency': 'RUB',
'conv_currency': 'USD',
'date': '2024-10-16',
}, timeout=3)
print(res.json())
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Node.JS'>
<Tabs>
<TabItem label='axios'>
```javascript
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getRate/', {
timeout: 3000,
'from_currency': 'RUB',
'conv_currency': 'USD',
'date': '2024-10-16',
}
)
.then((res) => {
console.log(JSON.stringify(res.json()));
})
.catch((err) => {
console.error(err);
});
```
</TabItem>
</Tabs>
</TabItem>
</Tabs>
### Query Parameters
| Parameter | Description |
|-------------------|------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
| `date`* | Currency rate date in the format `YYYYY-DD-MM` |
| `conv_amount` | Multiplier for number conversion (Optional) |
`*` - Required arguments
### Response
<Aside title='Output'>
```json
[
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-17T00:00:00.000Z"
}
]
```
</Aside>
## Get currency exchange rate for a certain period
Getting the list of the array with currency rate for a certain period of time.
### Request
<Tabs>
<TabItem label='Shell'>
<Tabs>
<TabItem label='curl'>
```shell
curl --request GET --url https://kekkai-api.redume.su/api/getRate/?from_currency=RUB&conv_currency=USD&start_date=2024-10-16&end_date=2024-10-20
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Python'>
<Tabs>
<TabItem label='requests'>
```python
import requests
res = requests.get('https://kekkai-api.redume.su/api/getRate/', {
'from_currency': 'RUB',
'conv_currency': 'USD',
'start_date': '2024-10-16',
'end_date': '2024-10-20',
}, timeout=3)
print(res.json())
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Node.JS'>
<Tabs>
<TabItem label='axios'>
```javascript
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/getRate/', {
timeout: 3000,
'from_currency': 'RUB',
'conv_currency': 'USD',
'start_date': '2024-10-16',
'end_date': '2024-10-20',
}
)
.then((res) => {
console.log(JSON.stringify(res.json()));
})
.catch((err) => {
console.error(err);
});
```
</TabItem>
</Tabs>
</TabItem>
</Tabs>
### Query parameters
| Parameter | Description |
|------------------|-------------------------------------------------------------------------|
| `from_currency`* | `ISO 4217` code of the currency from which the conversion takes place |
| `conv_currency`* | `ISO 4217` code of the currency to which the conversion is performed |
| `start_date`* | Start date of the period in the format `YYYYY-DD-MM` |
| `end_date`* | Period end date in the format `YYYYY-DD-MM` |
`*` - Required arguments
### Response
<Aside title='Output'>
```json
[
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-17T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-18T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-19T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-20T00:00:00.000Z"
},
{
"from_currency": "RUB",
"conv_currency": "USD",
"rate": 0.01,
"date": "2024-10-21T00:00:00.000Z"
}
]
```
</Aside>

View file

@ -0,0 +1,79 @@
---
title: Get metadata - /api/metadata
---
Currencies are identified by standard three-letter `ISO 4217` currency codes.
import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';
## Get data on available dates and currencies.
### Request
<Tabs>
<TabItem label='Shell'>
<Tabs>
<TabItem label='curl'>
```shell
curl --request GET --url https://kekkai-api.redume.su/api/metadata/
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Python'>
<Tabs>
<TabItem label='requests'>
```python
import requests
res = requests.get('https://kekkai-api.redume.su/api/metadata/', timeout=3)
print(res.json())
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label='Node.JS'>
<Tabs>
<TabItem label='axios'>
```javascript
const axios = require('axios');
axios.get('https://kekkai-api.redume.su/api/metadata/')
.then((res) => {
console.log(JSON.stringify(res.json()));
})
.catch((err) => {
console.error(err);
});
```
</TabItem>
</Tabs>
</TabItem>
</Tabs>
### Response
<Aside title='Output'>
```json
{
"first_date": "2024-11-26T21:00:00.000Z",
"last_date": "2025-01-01T21:00:00.000Z",
"currencies": {
"crypto": [
"USDT",
"TON",
"BTC",
"ETH"
],
"fiat": [
"USD",
"RUB",
"EUR",
"UAH",
"TRY",
"KZT"
]
}
}
```
</Aside>

View file

@ -1,7 +1,14 @@
If you have any questions, you can write to the [mail](mailto:redddume@gmail.com) or [Telegram](https://t.me/Redddume)
---
title: Contributing
---
If you have any questions, you can write to the
[mail](mailto:redddume@gmail.com)
or [Telegram](https://t.me/Redddume)
### Fork and clone your repository
1. Fork the repository ([click here to fork now](https://github.com/Redume/Kekkai/fork))
1. Fork the repository
([click here to fork now](https://github.com/Redume/Kekkai/fork))
2. Clone your forked code
```bash
git clone https://github.com/<nickname>/Kekkai.git
@ -18,16 +25,20 @@ git checkout <name_new_branch>
6. Submit a new Pull Request
### Testing
Before sending a Pull Request, test the functionality. Everything should work both in Docker Compose and without it.
Before sending a Pull Request, test the functionality.
Everything should work both in Docker Compose and without it.
It is recommended to use Debugger and Debug log for testing. The logging level is changed in `config.yaml`
It is recommended to use Debugger and Debug log for testing.
The logging level is changed in `config.yaml`
### Code Style
[`Pylint`][pylint], [`mypy`][mypy], [`eslint`][eslint] and [`prettier`][prettier] are used as code syntax checks
[`Pylint`][pylint], [`mypy`][mypy],
[`eslint`][eslint] and [`prettier`][prettier] are used as code syntax checks
#### Checking the Node.JS code
To check the code, you must first download the necessary libraries, which are located at the root of the project
To check the code, you must first download the necessary libraries,
which are located at the root of the project
```bash
npm install
```

View file

@ -0,0 +1,84 @@
---
title: Docker
---
Docker Compose is the recommended method to run Kekkai in production.
Below are the steps to deploy Kekkai with Docker Compose.
Kekkai requires Docker Compose version 2.x.
import { Steps, Code } from '@astrojs/starlight/components';
<Steps>
1. Preparing files
```
git clone https://github.com/redume/Kekkai
```
```
cd Kekkai
```
2. Change config files
In nginx.conf, you need to specify your domain or ipv4 address
<Code code='
...
listen 443 ssl;
server_name localhost; # Your domain
...
' lang='txt' title='nginx.conf' />
To set up SSL. Create folder `CertSSL`
```
mkdir CertSLL
```
After that, copy the SSL certificates to the CertSSL folder,
if the names are different,
then change either the name of the certificates or in `nginx.conf`
<Code code='
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
' lang='txt' title='nginx.conf' />
<Code code=
"
database:
user: 'DATABASE_USERNAME'
password: 'DATABASE_PASSWORD'
host: 'DATABASE_HOST'
name: 'DATABASE_NAME'
port: 5432
...
" lang='yaml' title='config.sample.yaml' />
Fill in the data in the database item, as well as in the .env config
<Code code='
# Connection secret for postgres. You should change it to a random password
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
POSTGRES_PASSWORD=my_password
# If you do not know what you are doing, then you should not edit the values below
###################################################################################
POSTGRES_DB=kekkai
DB_HOST=postgres
POSTGRES_USER=postgres
' lang='txt' title='.env.sample' />
- Populate custom database information if necessary.
- Consider changing `DB_PASSWORD` to a custom value.
Postgres is not publically exposed,
so this password is only used for - local authentication.
To avoid issues with Docker parsing this value,
it is best to use only the characters `A-Za-z0-9`.
3. Start the containers
```shell
docker compose up -d
```
</Steps>

View file

@ -1,10 +1,17 @@
---
title: Home
description: The first free Open-Source Tool for Saving Historical Currency data
---
## What is Kekkai?
Kekkai — The first free Open-Source Tool for Saving Historical Currency data
It is a simple tool for collecting historical currency data from open sources, with the ability to create currency exchange rate charts.
It is a simple tool for collecting historical currency data from open sources,
with the ability to create currency exchange rate charts.
## Why Kekkai?
- Free & Open-Source
- The ability to create graphs
- Plausible support. Anonymous data collection with the ability to disable it. Available only to server owners
- Plausible support. Anonymous data collection with the ability to disable it.
Available only to server owners

5
docs/tsconfig.json Normal file
View file

@ -0,0 +1,5 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}

View file

@ -1,61 +0,0 @@
site_name: Kekkai
site_url: https://kekkai-docs.redume.su
site_author: Redume
site_description: The first free Open-Source Tool for Saving Historical Currency data
repo_name: Redume/Kekkai
repo_url: https://github.com/Redume/Kekkai
edit_uri: ''
copyright: Copyright &copy; 2024 Redume
nav:
- Getting started:
- Docker [Recommended]: getting-started/docker.md
- Manual: getting-started/manual.md
- Contributing: getting-started/contributing.md
- Endpoints:
- Endpoints list: endpoints/list-endpoints.md
- Get currency rate - /api/getRate: endpoints/get-rate.md
- Create Charts - /api/getChart: endpoints/create-chart.md
- Config:
- Configure config.yaml: config/config-yaml.md
- Configure .env: config/config-env.md
- Configure nginx.conf: config/conf-nginx.md
theme:
logo: assets/logo.svg
favicon: assets/logo.svg
language: en
name: material
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
toggle:
icon: material/brightness-7
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
toggle:
icon: material/brightness-4
name: Switch to system preference
features:
- content.code.copy
- content.tabs.link
markdown_extensions:
- pymdownx.tabbed:
alternate_style: true
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
- admonition
- pymdownx.details
- pymdownx.superfences
- tables

View file

@ -1,127 +0,0 @@
events { }
http {
limit_req_zone $binary_remote_addr zone=kekkai:10m rate=10r/s;
# Change the number '10' to your own, to change the threshold number for rate limit
upstream server_backend {
server server:3000;
}
upstream chart_backend {
server chart:3030;
}
server {
listen 80;
server_name localhost; # Your domain
limit_req zone=kekkai;
location / {
limit_req zone=kekkai burst=4;
proxy_pass http://server_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/configuration {
limit_req zone=kekkai burst=4;
proxy_pass http://server_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/getChart {
limit_req zone=kekkai burst=4;
proxy_pass http://chart_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/chart {
limit_req zone=kekkai burst=4;
proxy_pass http://chart_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /robots.txt {
alias /etc/nginx/robots.txt;
}
location /favicon.ico {
alias /etc/nginx/assets/logo.png;
}
}
server {
listen 443 ssl;
server_name localhost; # Your domain
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
limit_req zone=kekkai;
location / {
limit_req zone=kekkai burst=4;
proxy_pass http://server_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/configuration {
limit_req zone=kekkai burst=4;
proxy_pass http://server_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/getChart {
limit_req zone=kekkai burst=4;
proxy_pass http://chart_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/chart {
limit_req zone=kekkai burst=4;
proxy_pass http://chart_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /robots.txt {
alias /etc/nginx/robots.txt;
}
location /favicon.ico {
alias /etc/nginx/assets/logo.png;
}
}
}

2
nginx/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
ssl
assets

17
nginx/chart/nginx.conf Normal file
View file

@ -0,0 +1,17 @@
location /api/getChart {
limit_req zone=kekkai burst=4;
proxy_pass http://chart:3030;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/chart {
proxy_pass http://chart:3030;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

4
nginx/docs/nginx.conf Normal file
View file

@ -0,0 +1,4 @@
location ~ ^/(_astro|assets|docs|pagefind)/ {
root /etc/nginx/dist;
index index.html;
}

99
nginx/mime.types Normal file
View file

@ -0,0 +1,99 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/avif avif;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/wasm wasm;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

28
nginx/nginx.conf Normal file
View file

@ -0,0 +1,28 @@
events { }
http {
include /etc/nginx/mime.types;
limit_req_zone $binary_remote_addr zone=kekkai:10m rate=10r/s;
server {
listen 80;
server_name localhost; # Your domain
return 301 https://$host$request_uri$is_args$args;
}
server {
listen 443 ssl;
server_name localhost; # Your domain
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
include chart/nginx.conf;
include server/nginx.conf;
include web/nginx.conf;
include docs/nginx.conf;
}
}

19
nginx/server/nginx.conf Normal file
View file

@ -0,0 +1,19 @@
location /api/getRate/ {
limit_req zone=kekkai burst=4;
proxy_pass http://server:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/metadata/ {
limit_req zone=kekkai burst=4;
proxy_pass http://server:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

15
nginx/web/nginx.conf Normal file
View file

@ -0,0 +1,15 @@
location / {
proxy_pass http://web:3050;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /robots.txt {
alias robots.txt;
}
location /favicon.ico {
alias assets/logo.png;
}

View file

@ -2,10 +2,6 @@
"name": "kekkai",
"version": "1.0.0",
"description": "Historical data on the rate of fiat and crypto currencies",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Redume/Kekkai.git"
@ -15,7 +11,7 @@
"bugs": {
"url": "https://github.com/Redume/Kekkai/issues"
},
"homepage": "https://github.com/Redume/Kekkai#readme",
"homepage": "https://kekkai.redume.su/",
"devDependencies": {
"@eslint/js": "^8.57.0",
"eslint": "^8.57.0",

View file

@ -1,96 +1,106 @@
const logger = require("../shared/logger/src/main.js");
const config = require("../shared/config/src/main.js")();
const logger = require('../shared/logger/src/main.js');
const config = require('../shared/config/src/main.js')();
const fs = require("fs");
const axios = require("axios");
const UAParser = require("ua-parser-js");
const fs = require('fs');
const axios = require('axios');
const UAParser = require('ua-parser-js');
require("../shared/database/src/create_table.js")();
require('../shared/database/src/create_table.js')();
const fastify = require("fastify")({
logger: config["server"]["log"]["print"] ? logger : false,
...(config["server"]["ssl"]["work"]
const fastify = require('fastify')({
logger: config['server']['log']['level'] !== 'none' ? logger : false,
...(config['server']['ssl']['enabled']
? {
https: {
key: fs.readFileSync(config["server"]["ssl"]["private_key"], "utf8"),
cert: fs.readFileSync(config["server"]["ssl"]["cert"], "utf8"),
},
}
https: {
key: fs.readFileSync(
config['server']['ssl']['private_key'],
'utf8',
),
cert: fs.readFileSync(
config['server']['ssl']['cert'],
'utf8',
),
},
}
: false),
});
const getRateRoute = require("./routes/getRate.js");
const HomeRoute = require("./routes/home.js");
const getRateRoute = require('./routes/getRate.js');
const getMetadata = require('./routes/metadata.js');
fastify.register(getRateRoute);
fastify.register(HomeRoute);
fastify.register(getMetadata);
fastify.setNotFoundHandler(function (res, reply) {
return reply.status(404).send({
status: 404,
message: "Page not found!",
documentation: "https://kekkai-docs.redume.su/",
message: 'Page not found!',
documentation: 'https://kekkai-docs.redume.su/',
});
});
fastify.addHook("onResponse", async (request, reply) => {
const routePart = request.raw.url.split("/");
fastify.addHook('onResponse', async (request, reply) => {
const routePart = request.raw.url.split('/');
const routePartFiltered = routePart
.filter((part) => part !== "")
.filter((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;
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 browser = parser.getBrowser();
const os = parser.getOS();
const formattedOS =
os.name && os.version ? `${os.name} ${os.version}` : "N/A";
os.name && os.version ? `${os.name} ${os.version}` : 'N/A';
const formattedBrowser =
browser.name && browser.version
? `${browser.name} ${browser.version}`
: "N/A";
: 'N/A';
const event = {
domain: config["analytics"]["plausible_domain"],
domain: config['analytics']['plausible_domain'],
name: request.routeOptions.url
? request.routeOptions.url
: "404 - Not Found",
: '404 - Not Found',
url: request.raw.url,
props: {
method: request.method,
statusCode: reply.statusCode,
browser: formattedBrowser,
os: formattedOS,
source: request.headers["referer"]
? request.headers["referer"]
: "direct",
source: request.headers['referer']
? request.headers['referer']
: 'direct',
},
};
try {
await axios.post(config["analytics"]["plausible_api"], event, {
headers: {
Authorization: `Bearer ${config["analytics"]["plausible_token"]}`,
"Content-Type": "application/json",
"User-Agent": userAgent,
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,
},
},
});
);
} catch (error) {
fastify.log.error("Error sending event to Plausible:", error.message);
fastify.log.error('Error sending event to Plausible:', error.message);
}
});
fastify.listen(
{
port: 3000,
host: config["server"]["host"] ? config["server"]["host"] : "localhost",
host: config['server']['host'] ? config['server']['host'] : 'localhost',
},
(err) => {
if (err) {

View file

@ -18,7 +18,7 @@ module.exports = async function getRateRoute(fastify) {
query['from_currency'],
query['conv_currency'],
query['date'],
query['conv_amount'] ? query['conv_amount'] : 1
query['conv_amount'] ? query['conv_amount'] : 0
);
else if (query['start_date'] && query['end_date'])
rate_res = await rate.getPeriod(

View file

@ -1,8 +0,0 @@
module.exports = async function getRateRoute(fastify) {
fastify.get('/', async function (req, res) {
return res.status(200).send({
message: 'Hello World!',
documentation: 'https://kekkai-docs.redume.su/',
});
});
};

26
server/routes/metadata.js Normal file
View file

@ -0,0 +1,26 @@
const pool = require('../../shared/database/src/postgresql.js');
const config = require('../../shared/config/src/main.js')();
module.exports = async function metadata(fastify) {
fastify.get('/api/metadata/', async function (req, res) {
const first_date = await pool.query(
'SELECT * FROM currency ORDER BY date LIMIT 1',
);
const last_date = await pool.query(
'SELECT * FROM currency ORDER BY date DESC LIMIT 1',
);
return res.status(200).send({
first_date: first_date.rows[0]?.date
? first_date.rows[0]?.date
: 'None',
last_date: last_date.rows[0]?.date
? last_date.rows[0]?.date
: 'None',
currencies: {
crypto: config['currency']['crypto'],
fiat: config['currency']['fiat'],
},
});
});
};

View file

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

View file

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

View file

@ -1,10 +1,10 @@
const fs = require('fs');
const yaml = require('yaml');
const hjson = require('hjson');
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;

View file

@ -17,9 +17,7 @@ async function getDay(from_currency, conv_currency, date, conv_amount) {
if (conv_amount) {
let conv_rate = data?.['rows'][0]['rate'] * conv_amount;
const point = conv_rate.toString().indexOf('.') + 4;
data['rows'][0]['conv_amount'] = Number(conv_rate.toString().slice(0, point));
data['rows'][0]['conv_amount'] = Number(conv_rate.toFixed(2));
}
logger.debug(data['rows'][0]);

43
web/main.js Normal file
View file

@ -0,0 +1,43 @@
const logger = require('../shared/logger');
const config = require('../shared/config/src/main.js')();
const fs = require('fs');
const path = require('node:path');
const fastify = require('fastify')({
logger: config['server']['log']['level'] !== 'none' ? logger : false,
...(config['server']['ssl']['enabled']
? {
https: {
key: fs.readFileSync(
config['server']['ssl']['private_key'],
'utf8',
),
cert: fs.readFileSync(
config['server']['ssl']['cert'],
'utf8',
),
},
}
: false),
});
fastify.register(require('@fastify/static'), {
root: path.join(__dirname, 'src/static'),
prefix: '/static/',
});
fastify.register(require('./routes/home.js'));
fastify.listen(
{
port: 3050,
host: config['server']['host'] ? config['server']['host'] : 'localhost',
},
(err) => {
if (err) {
fastify.log.error(err);
process.exit(1);
}
},
);

1146
web/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

19
web/package.json Normal file
View file

@ -0,0 +1,19 @@
{
"name": "web",
"version": "1.0.0",
"description": "",
"repository": {
"type": "git",
"url": "https://github.com/Redume/Kekkai/issues"
},
"license": "GPL-3.0-or-later",
"author": "Redume",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"@fastify/static": "^7.0.4",
"fastify": "^4.28.1"
}
}

7
web/routes/home.js Normal file
View file

@ -0,0 +1,7 @@
const fs = require("node:fs");
module.exports = async function homeRoute(fastify) {
fastify.get('/', (req, res) => {
res.send(fs.createReadStream('./src/html/index.html'));
});
}

58
web/src/html/index.html Normal file
View file

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kekkai</title>
<meta name="author" content="Redume">
<meta name="description" content="API providing historical currency data">
<meta name="keywords" content="kekkai, currency, exchange rate, api exchange rate, cryptocurrency api rate, fiat api rate">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link type="text/css" rel="stylesheet" href="/static/styles/index.css">
</head>
<body>
<div class="banner">
<div class="banner-img">
<img src="/static/assets/kekkai.svg" alt="Kekkai title">
</div>
<p class="desc">The first <a style="color: #69C9FF">free Open-Source</a> Tool for Saving Historical Currency data</p>
</div>
<p class="desc-xl">It is a simple tool for collecting historical currency data from open sources, with the ability to create currency exchange rate charts. Cryptocurrencies and fiat currency are supported</p>
<div class="features-container">
<div class="features">
<svg width="64px" height="64px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#ffffff">
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
<g id="SVGRepo_iconCarrier"> <g> <path fill="none" d="M0 0H24V24H0z"></path>
<path d="M12 2c5.523 0 10 4.477 10 10 0 4.4-2.841 8.136-6.789 9.473l-.226.074-2.904-7.55C13.15 13.95 14 13.054 14 12c0-1.105-.895-2-2-2s-2 .895-2 2c0 1.077.851 1.955 1.917 1.998l-2.903 7.549-.225-.074C4.84 20.136 2 16.4 2 12 2 6.477 6.477 2 12 2zm0 2c-4.418 0-8 3.582-8 8 0 2.92 1.564 5.475 3.901 6.872l1.48-3.849C8.534 14.29 8 13.207 8 12c0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.207-.535 2.29-1.38 3.023.565 1.474 1.059 2.757 1.479 3.85C18.435 17.475 20 14.92 20 12c0-4.418-3.582-8-8-8z"></path> </g> </g>
</svg>
<p class="text">Free & Open-Source</p>
</div>
<div class="features">
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#ffffff">
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path d="M12 18V12M12 12L16 7M12 12L8 7M16 12H8M15.5 15H8.5M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</svg>
<p class="text">Support for fiat currency and cryptocurrencies</p>
</div>
<div class="features">
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M16 11V16M8 11V16M12 8V16M7 21H17C19.2091 21 21 19.2091 21 17V7C21 4.79086 19.2091 3 17 3H7C4.79086 3 3 4.79086 3 7V17C3 19.2091 4.79086 21 7 21Z" stroke="#ffffff" stroke-width="2" stroke-linecap="round"/> </g>
</svg>
<p class="text">Creating graphs</p>
</div>
</div>
<div class="buttons-container">
<div class="pr-button" onclick="window.open('https://github.com/Redume/Kekkai')">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M4.0744 2.9938C4.13263 1.96371 4.37869 1.51577 5.08432 1.15606C5.84357 0.768899 7.04106 0.949072 8.45014 1.66261C9.05706 1.97009 9.11886 1.97635 10.1825 1.83998C11.5963 1.65865 13.4164 1.65929 14.7213 1.84164C15.7081 1.97954 15.7729 1.97265 16.3813 1.66453C18.3814 0.651679 19.9605 0.71795 20.5323 1.8387C20.8177 2.39812 20.8707 3.84971 20.6494 5.04695C20.5267 5.71069 20.5397 5.79356 20.8353 6.22912C22.915 9.29385 21.4165 14.2616 17.8528 16.1155C17.5801 16.2574 17.3503 16.3452 17.163 16.4167C16.5879 16.6363 16.4133 16.703 16.6247 17.7138C16.7265 18.2 16.8491 19.4088 16.8973 20.4002C16.9844 22.1922 16.9831 22.2047 16.6688 22.5703C16.241 23.0676 15.6244 23.076 15.2066 22.5902C14.9341 22.2734 14.9075 22.1238 14.9075 20.9015C14.9075 19.0952 14.7095 17.8946 14.2417 16.8658C13.6854 15.6415 14.0978 15.185 15.37 14.9114C17.1383 14.531 18.5194 13.4397 19.2892 11.8146C20.0211 10.2698 20.1314 8.13501 18.8082 6.83668C18.4319 6.3895 18.4057 5.98446 18.6744 4.76309C18.7748 4.3066 18.859 3.71768 18.8615 3.45425C18.8653 3.03823 18.8274 2.97541 18.5719 2.97541C18.4102 2.97541 17.7924 3.21062 17.1992 3.49805L16.2524 3.95695C16.1663 3.99866 16.07 4.0147 15.975 4.0038C13.5675 3.72746 11.2799 3.72319 8.86062 4.00488C8.76526 4.01598 8.66853 3.99994 8.58215 3.95802L7.63585 3.49882C7.04259 3.21087 6.42482 2.97541 6.26317 2.97541C5.88941 2.97541 5.88379 3.25135 6.22447 4.89078C6.43258 5.89203 6.57262 6.11513 5.97101 6.91572C5.06925 8.11576 4.844 9.60592 5.32757 11.1716C5.93704 13.1446 7.4295 14.4775 9.52773 14.9222C10.7926 15.1903 11.1232 15.5401 10.6402 16.9905C10.26 18.1319 10.0196 18.4261 9.46707 18.4261C8.72365 18.4261 8.25796 17.7821 8.51424 17.1082C8.62712 16.8112 8.59354 16.7795 7.89711 16.5255C5.77117 15.7504 4.14514 14.0131 3.40172 11.7223C2.82711 9.95184 3.07994 7.64739 4.00175 6.25453C4.31561 5.78028 4.32047 5.74006 4.174 4.83217C4.09113 4.31822 4.04631 3.49103 4.0744 2.9938Z" fill="#ffffff"></path> <path d="M3.33203 15.9454C3.02568 15.4859 2.40481 15.3617 1.94528 15.6681C1.48576 15.9744 1.36158 16.5953 1.66793 17.0548C1.8941 17.3941 2.16467 17.6728 2.39444 17.9025C2.4368 17.9449 2.47796 17.9858 2.51815 18.0257C2.71062 18.2169 2.88056 18.3857 3.05124 18.5861C3.42875 19.0292 3.80536 19.626 4.0194 20.6962C4.11474 21.1729 4.45739 21.4297 4.64725 21.5419C4.85315 21.6635 5.07812 21.7352 5.26325 21.7819C5.64196 21.8774 6.10169 21.927 6.53799 21.9559C7.01695 21.9877 7.53592 21.998 7.99999 22.0008C8.00033 22.5527 8.44791 23.0001 8.99998 23.0001C9.55227 23.0001 9.99998 22.5524 9.99998 22.0001V21.0001C9.99998 20.4478 9.55227 20.0001 8.99998 20.0001C8.90571 20.0001 8.80372 20.0004 8.69569 20.0008C8.10883 20.0026 7.34388 20.0049 6.67018 19.9603C6.34531 19.9388 6.07825 19.9083 5.88241 19.871C5.58083 18.6871 5.09362 17.8994 4.57373 17.2891C4.34391 17.0194 4.10593 16.7834 3.91236 16.5914C3.87612 16.5555 3.84144 16.5211 3.80865 16.4883C3.5853 16.265 3.4392 16.1062 3.33203 15.9454Z" fill="#ffffff"></path> </g></svg>
<a>Source Code</a>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,145 @@
body {
background: #4F5574;
color: white;
font: medium "Gotham Pro", sans-serif;
margin: 0;
padding: 0;
}
.banner {
background: rgba(0, 0, 0, 0.39);
border-radius: 3vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2%;
max-width: 95vw;
margin: .5% auto;
}
.banner-img {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.banner-img img {
width: 100%;
max-width: 450px;
height: auto;
margin: 1.5%;
}
.desc {
width: 100%;
max-width: 600px;
text-align: center;
margin: 1% auto;
word-wrap: break-word;
padding: 0 2%;
box-sizing: border-box;
font-size: 16px;
}
.desc-xl {
text-align: center;
display: flex;
margin: 1% auto;
word-wrap: break-word;
width: 100%;
max-width: 500px;
font-weight: bold;
padding: 10px 2%;
box-sizing: border-box;
}
.features-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 40px;
padding: 80px 20px;
max-width: 1200px;
margin: auto;
}
.features {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
max-width: 300px;
}
.features svg {
margin-bottom: 15px;
}
.text {
font-size: 22px;
font-weight: bold;
}
.buttons-container {
display: flex;
justify-content: center;
}
.pr-button {
display: inline-flex;
align-items: center;
background-color: #297BCD;
border-radius: 12px;
padding: 15px 25px;
font-size: 1.2rem;
font-weight: bold;
transition: background-color 0.3s ease;
cursor: pointer;
}
.pr-button svg {
width: 30px;
height: 30px;
margin-right: 10px;
}
.pr-button a {
color: white;
text-decoration: none;
}
.pr-button:hover {
background-color: #2b6ea3;
}
.pr-button {
margin-bottom: 130px;
}
@media (min-width: 768px) {
.desc {
font-size: 20px;
}
}
@media (max-width: 768px) {
.footer-wrapper {
position: relative;
min-height: 100%;
}
}
@media (min-width: 1200px) {
.desc {
font-size: 24px;
}
.features-container {
flex-direction: row;
align-items: center;
gap: 200px;
}
}