Skip to content

Commit

Permalink
Merge pull request #158 from SeaweedbrainCY/dev
Browse files Browse the repository at this point in the history
- Add healthcheck endpoint
- add github link as static link
  • Loading branch information
SeaweedbrainCY authored Jan 5, 2025
2 parents d5d76e4 + bab6b18 commit 0ec4050
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ jobs:
- uses: actions/checkout@v3
- name: Login to Github
run : echo ${{ secrets.TOKEN_GITHUB }} | docker login ghcr.io -u ${{ secrets.USERNAME_GITHUB }} --password-stdin
- name: Populating API version
working-directory: ./api
run: echo ${{ github.ref_name }} > ./VERSION
- name: Populating API version
working-directory: ./api
run: echo ${{ github.ref_name }} > ./VERSION && echo ${{ github.sha}} | cut -c1-7 >> ./VERSION
- name: Build Docker image
working-directory: ./api
run: current_version=$(echo ${{ github.ref_name }} | sed 's/\([0-9]*\.[0-9]*\).*$/\1/') && docker build -t ghcr.io/${{ secrets.USERNAME_GITHUB }}/$REPO/api:latest .
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v3
- name: Login to Docker Hub
run : docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASS }}
- name: Create environment file
- name: Create frontend environment file
working-directory: ./frontend
run: bash generate-env.sh '${{ github.ref_name }}' '${{secrets.RSA_PUBLIC_KEY}}'
- name: Generate sitemap.xml
Expand All @@ -45,6 +45,12 @@ jobs:
- uses: actions/checkout@v3
- name: Login to Docker Hub
run : docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASS }}
- name: Populating API version
working-directory: ./api
run: echo ${{ github.ref_name }} > ./VERSION
- name: Populating API version
working-directory: ./api
run: echo ${{ github.ref_name }} > ./VERSION && echo ${{ github.sha}} | cut -c1-7 >> ./VERSION
- name: Build Docker image
working-directory: ./api
run: current_version=$(echo ${{ github.ref_name }} | sed 's/\([0-9]*\.[0-9]*\).*$/\1/') && docker build -t $REPO:latest -t $REPO:$current_version .
Expand Down
4 changes: 3 additions & 1 deletion api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ WORKDIR /api
RUN apt-get clean \
&& apt-get -y update

RUN apt-get -y install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools gcc default-libmysqlclient-dev pkg-config
RUN apt-get -y install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools gcc default-libmysqlclient-dev pkg-config jq curl
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
EXPOSE 8080

RUN chmod +x ./start.sh
RUN chmod +x ./docker-healthcheck.sh
RUN mkdir /var/log/api/ && touch /var/log/api/gunicorn_error.log && touch /var/log/api/api.log && touch /var/log/api/gunicorn_access.log
RUN chown -R 1001:1001 /var/log/api/
USER 1001:1001
CMD ["./start.sh"]
HEALTHCHECK --interval=1m --timeout=30s --start-period=10s --retries=2 CMD ./docker-healthcheck.sh || exit 1
4 changes: 4 additions & 0 deletions api/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0.0.0
0000000
WARNING
This file will be overridden by the build process. Modifications will not be taken into account.
15 changes: 4 additions & 11 deletions api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,6 @@ def clean_rate_limiting_from_db():
count += 1
logging.info(f"Deleted {count} expired session tokens at {dt.datetime.now(dt.UTC).isoformat()}")

# DEPRECATED
#@flask.before_request
#def before_request():
# if not conf.database.are_all_tables_created:
# with app.app.app_context():
# db.create_all()
# db.session.commit()
# logging.info("✅ Tables created")
# logging.info(db.metadata.tables.keys())
# conf.database.are_all_tables_created = True


@flask.after_request
Expand All @@ -121,7 +111,10 @@ def after_request(response):
elif response.status_code >= 400:
logging.warning(f"Completed request ip={utils.get_ip(request)} gw={request.remote_addr} url={request.url} method={request.method} status={response.status_code}")
else :
logging.info(f"Completed request ip={utils.get_ip(request)} gw={request.remote_addr} url={request.url} method={request.method} status={response.status_code}")
if request.path == "/api/v1/healthcheck":
logging.debug(f"Completed healthcheck. If healthcheck failed, please check the logs above.")
else:
logging.info(f"Completed request ip={utils.get_ip(request)} gw={request.remote_addr} url={request.url} method={request.method} status={response.status_code}")
return response


Expand Down
9 changes: 9 additions & 0 deletions api/config/config-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ api:
## Default: 1day
# refresh_token_validity: 86400

## Health check endpoint used to check if the API is running correctly.
# health_check:
## Node name check. This check is used to determine which api node is answering API calls.
## This is useful for load balancing.
## The API will return the SHA256-HMAC(node_name, node_name_hmac_secret) in the response.
# node_check_enabled: false
# node_name: "example"
# node_name_hmac_secret: "changeme"




Expand Down
30 changes: 29 additions & 1 deletion api/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
from database.db import db
import threading
from uuid import uuid4
import hmac
import hashlib
from sqlalchemy import text





Expand Down Expand Up @@ -891,4 +896,27 @@ def auth_refresh_token(ip, *args, **kwargs):
new_session_token, new_refresh_token = refresh_token_func.refresh_token_flow(refresh=refresh, session=session, ip=ip)
response = Response(status=200, mimetype="application/json", response=json.dumps({"challenge":"ok"}))
response.set_auth_cookies(new_session_token, new_refresh_token)
return response
return response


def health_check():
health_status = {}
if conf.api.node_check_enabled:
health_status["node"] = hmac.new(conf.api.node_name_hmac_secret.encode('utf-8'), conf.api.node_name.encode('utf-8'), hashlib.sha256).hexdigest()

try:
db.session.execute(text('SELECT 1'))
health_status["database"] = "OK"
except Exception as e:
logging.warning("Database healthcheck failed : " + str(e))
health_status["database"] = "NOT OK"
health_status["version"] = conf.api.version
health_status["build"] = conf.api.build

if health_status["database"] == "OK":
health_status["health"] = "OK"
else:
health_status["health"] = "NOT OK"

return health_status, 200

6 changes: 6 additions & 0 deletions api/docker-healthcheck.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
health=$(curl --fail --silent http://127.0.0.1:8080/api/v1/healthcheck | jq .health)
if [ "$health" = "\"OK\"" ]; then
exit 0
else
exit 1
fi
45 changes: 36 additions & 9 deletions api/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA512
import ipaddress
import re

class EnvironmentConfig:
required_keys = ["type", "config_version", "domain"]
Expand Down Expand Up @@ -78,15 +79,9 @@ def __init__(self, data, config_version):
for key in self.option_config:
if key not in data:
logging.warning(f"api.{key} is not set. Ignoring it ...")
if "port" not in data:
logging.warning(f"api.'port' is not set. Using default value: 8080")
data["port"] = 8080

try:
self.port = int(data["port"])
except:
logging.warning("api.port is not valid. Ignoring it. Setting default value: 8080")
self.port = 8080

logging.info("API will listen on port 8080")
self.port = 8080

self.jwt_secret = data["jwt_secret"]
self.private_key_path = data["private_key_path"]
Expand Down Expand Up @@ -130,6 +125,38 @@ def __init__(self, data, config_version):
except Exception as e:
logging.error(f"[FATAL] Load config fail. api.refresh_token_validity is not valid. {e}")
exit(1)

if "health_check" in data:
if 'node_check_enabled':
self.node_check_enabled = data["health_check"]["node_check_enabled"]
if self.node_check_enabled:
required_node_health_check_keys = ["node_name", "node_name_hmac_secret"]
for key in required_node_health_check_keys:
if key not in data["health_check"]:
logging.error(f"[FATAL] Load config fail. api.health_check.node_check_enabled is True so api.health_check require the key {key} to exist.")
exit(1)
self.node_name = data["health_check"]["node_name"]
self.node_name_hmac_secret = data["health_check"]["node_name_hmac_secret"]
else:
logging.error(f"[FATAL] Load config fail. api.health_check require the key node_check_enabled to exist. {e}")
exit(1)
else:
self.node_check_enabled = False

self.version = "0.0.0"
self.build = "0000000"
with open("VERSION", "r") as f:
version = f.readline().strip()
if re.match(r"^b?v?\d+\.\d+\.\d+(-[a-zA-Z0-9]+)?$", version) or version == "dev":
self.version = version
else:
logging.warning(f"VERSION file is not in the correct format. Using default value: 0.0.0")
build = f.readline().strip() # github short sha1
if re.match(r"^[a-f0-9]{7}$", build):
self.build = build
else:
logging.warning(f"VERSION file is not in the correct format. Using default build: 0000000")



class DatabaseConfig:
Expand Down
12 changes: 12 additions & 0 deletions api/openAPI/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,18 @@ paths:
example: 1723399133
"500":
description: Internal server error
/healthcheck:
get:
operationId: controllers.health_check
tags:
- General
summary: Check the health of the API
security: []
responses:
"200":
description: API is healthy
"500":
description: API is unhealthy
components:
securitySchemes:
SessionToken:
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/app/changelog/changelog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ export class ChangelogComponent {
imageHash = environment.imageHash;

changelogs = [
{
date: "05/12/2025",
version: "b5.2.0",
added: [
"A healthcheck endpoint to enable a better monitoring of the API",
],
fixed:[
]
},
{
date: "02/12/2025",
version: "b5.1.0",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/assets/github.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@


<body>
<div id="static_links">
<a href="https://github.com/seaweedbrainCY/zero-totp" class="github-button" target="_blank" style=" z-index: 99 !important;" rel="noopener noreferrer">
<img src="assets/github.svg" alt="Zero-TOTP Github logo">
</a><br>
<a href="https://www.buymeacoffee.com/seaweedbrainCY" class="coffee-button" target="_blank" style=" z-index: 99 !important;" rel="noopener noreferrer">
<img src="assets/coffee_cup.svg" alt="Buy Me a Coffee">
</a>
</div>
<noscript>
<section class="hero is-halfheight mt-6 " style="min-height: 70vh;">
<article class="hero hero-body is-flex is-justify-content-center is-flex-direction-column">
Expand Down
24 changes: 22 additions & 2 deletions frontend/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,33 @@ video {
cursor: pointer;
}

.coffee-button {
#static_links{
position: fixed;
bottom: 18px;
right: 18px;
z-index: 99 !important;
}

.coffee-button {
width: 60px;
height: 60px;
background-color: #ffdd57; /* Background color for the button */
border-radius: 50%;

display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
text-decoration: none;
}

.github-button {
width: 60px;
height: 60px;
padding: 10px;
border-radius: 50%;
background-color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
Expand All @@ -69,7 +88,8 @@ video {
height: 30px;
}

.coffee-button:hover {

.coffee-button:hover, .github-button:hover {
transform: scale(1.1); /* Animation on hover */
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
}
Expand Down

0 comments on commit 0ec4050

Please sign in to comment.