16 Commits
Author SHA1 Message Date
Mark acf1691fd0 Merge pull request #14 from uw935/fix/reqs
v1.3-hotfix: Requirements
2024-04-22 00:15:49 +03:00
te5154c 7cf015be22 fix: requirements 2024-04-22 00:14:56 +03:00
Mark b22879474b Merge pull request #13 from uw935/feature/fastapi
v1.3: Backend rewrite to FastAPI
2024-04-22 00:06:18 +03:00
te5154c f7f76b1cfd add: new reqs && cmd docker 2024-04-22 00:04:58 +03:00
te5154c 1cc1772c4b fix: 404 error 2024-04-22 00:03:50 +03:00
te5154c b4d0c388ef add: fastapi 2024-04-21 23:57:42 +03:00
Mark f4230e1bf7 Merge pull request #12 from uw935/feature/deploy-by-package
added deploy using github packages
2024-04-14 20:22:55 +03:00
chydo 9ba02586ad fixed: repo name changed 2024-04-14 15:54:17 +07:00
chydo 4a627cc8b0 added: deploy using github packages 2024-04-14 15:53:03 +07:00
Mark ea704ca5af Merge pull request #11 from uw935/fix/iphone-display
Fixed first appearing on small screens
2024-03-02 16:04:43 +03:00
te5154c 4ef47c6ce5 fix first display on apple iphones 2024-03-02 16:02:36 +03:00
Mark f860de470f Merge pull request #10 from uw935/feature/borders
New borders
2024-03-02 02:08:09 +03:00
uw935 2d1e4e6f9f refactor: html style fixed && add: border footer & header changed 2024-03-02 02:06:30 +03:00
Mark 96175d2af5 Merge pull request #9 from uw935/hotfix/userid
v1.2.1 — hotfix #1
2024-02-28 00:57:33 +03:00
te5154c bc8ca01729 add: notification in viewer when error appears && api requests exception && new map-style (preparing for new update) && buttons color change 2024-02-28 00:51:08 +03:00
te5154c ce41b31331 hotfix: userid 2024-02-27 23:58:57 +03:00
10 changed files with 227 additions and 88 deletions
+34 -14
View File
@@ -1,23 +1,43 @@
name: deployment on server name: Create and publish a Docker image
on: on:
push: push:
branches: [ master ] branches:
- master
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs: jobs:
build: build-and-push-image:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: permissions:
- uses: actions/checkout@v3 contents: read
packages: write
- name: update bot steps:
uses: appleboy/ssh-action@v1.0.3 - name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with: with:
host: ${{ secrets.SERVER_HOST }} registry: ${{ env.REGISTRY }}
username: ${{ secrets.SERVER_USER }} username: ${{ github.actor }}
key: ${{ secrets.SSH_PRIVATE_KEY }} password: ${{ secrets.GITHUB_TOKEN }}
port: 22
script: | - name: Extract metadata (tags, labels) for Docker
cd /home/deploy/vatsimfox && git pull origin master --force && cd /home/deploy/vatsimfox/src id: meta
docker compose up -d --build uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: src/.
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+4 -2
View File
@@ -1,7 +1,7 @@
<br> <br>
<p align="center"> <p align="center">
<img align="center" src="media/cover.png"> <img align="center" src="media/cover.png">
<h3 align="center">VATSIM Fox</h3> <h3 align="center">VATSIM Fox — v1.2</h3>
<p align="center">Website to find out your detailed VATSIM stastic</p> <p align="center">Website to find out your detailed VATSIM stastic</p>
</p> </p>
<br> <br>
@@ -14,7 +14,7 @@ Project based on [Virtual Air Traffic Simulation Network (VATSIM) API](https://v
Written in Python 3.8.18 + JS (JQuery framework) + Boostrap Written in Python 3.8.18 + JS (JQuery framework) + Boostrap
## Startup ## Startup
For run application on your own machine, you must install docker. Then just one command in the "src" folder with the project source code: To run application on your own machine, you must install docker. Then just one command in the "src" folder with the project source code:
```bash ```bash
docker compose up docker compose up
@@ -37,6 +37,8 @@ python -m venv venv
# downloading requirements && upgrading pip # downloading requirements && upgrading pip
python -m pip install --upgrade pip python -m pip install --upgrade pip
python -m pip install -r requirements.txt python -m pip install -r requirements.txt
# starting application
python -m flask run --host 0.0.0.0 --debug
``` ```
## Feature ## Feature
+1 -1
View File
@@ -9,4 +9,4 @@ RUN pip install -r requirements.txt
COPY . /build/ COPY . /build/
CMD ["python3", "-m", "flask", "run", "--host=0.0.0.0"] CMD ["python3", "app.py"]
+97 -19
View File
@@ -1,38 +1,116 @@
import uvicorn
import requests import requests
from flask import Flask, render_template, abort, request, jsonify
from fastapi import (
FastAPI,
Request,
HTTPException,
)
from fastapi.responses import FileResponse
from starlette.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = Flask(__name__)
VATSIM_API_URL = "https://api.vatsim.net/v2/" VATSIM_API_URL = "https://api.vatsim.net/v2/"
app = FastAPI(
@app.route("/") title="VATSIM Fox",
def index_page(): docs_url=None,
return render_template("index.html") redoc_url=None,
openapi_url=None,
redirect_slashes=True
)
templates = Jinja2Templates(directory="templates")
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.errorhandler(404) @app.get("/")
def errorhandler_page(_): async def get_index_page(request: Request):
return render_template("4O4.html"), 404 """
Index page
:param request: FastAPI request
:return: HTML template
"""
return templates.TemplateResponse(
name="index.html",
context={
"request": request,
}
)
@app.route("/api/request") @app.get("/favicon.ico", include_in_schema=False)
def request_handler(): async def get_favicon():
if request_string := request.args.get("request_string"): """
return requests.get(f"{VATSIM_API_URL}{request_string}").json() Get favicon
return jsonify({"result": "not found"}) :return: Icon file
"""
return FileResponse("./static/icon.ico")
@app.route("/<int:cid>/") @app.get("/{cid}/")
def viewer_page(cid): async def get_viewer_page(request: Request, cid: str = None):
"""
Viewer page
:param request: FastAPI request
:param cid: User CID
:return: HTML template
"""
try:
user = requests.get(f"{VATSIM_API_URL}members/{cid}").json() user = requests.get(f"{VATSIM_API_URL}members/{cid}").json()
except requests.exceptions.JSONDecodeError:
raise HTTPException(404)
if "detail" in user and user["detail"] == "Not Found": if "detail" in user and user["detail"] == "Not Found":
return abort(404) raise HTTPException(404)
return render_template("viewer.html", data=(user, )) return templates.TemplateResponse(
name="viewer.html",
context={
"user": user,
"request": request,
}
)
@app.exception_handler(404)
async def error_handler(request: Request, _):
"""
Handler to the 404 HTTP error
:param request: FastAPI request
:return: HTML template
"""
return templates.TemplateResponse(
name="4O4.html",
context={
"request": request,
}
)
@app.get("/api/request")
def request_handler(request_string: str):
"""
Wrapper to the VATSIM API functions
:param request_string: String that will fetched from VATSIM API
"""
if request_string:
try:
return requests.get(f"{VATSIM_API_URL}{request_string}").json()
except requests.exceptions.JSONDecodeError:
return {"result": "error", "message": "Something went wrong. Report that error please"}
return {"result": "not found", "message": "Not found"}
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", port=80) uvicorn.run(app=app, host="0.0.0.0", port=80)
+3 -1
View File
@@ -1,3 +1,5 @@
Flask==2.2.5 fastapi==0.110.2
requests==2.31.0 requests==2.31.0
uvicorn==0.29.0
flake8==5.0.4 flake8==5.0.4
Jinja2==3.1.3
+4
View File
@@ -6,3 +6,7 @@
.fox_active, .fox_atc_active { .fox_active, .fox_atc_active {
background-color: #dee2e6 !important; background-color: #dee2e6 !important;
} }
#notification_wrapper {
display: none;
}
+13 -5
View File
@@ -2,8 +2,10 @@
let userSession = {}; let userSession = {};
fetch(ATC_API_URL, {}).then(response => response.json()).then(result => { fetch(ATC_API_URL, {}).then(response => response.json()).then(result => {
if (result && result["result"] == "not found") if (result && (result["result"] == "not found" || result["result"] == "error")) {
return showAlert("Error while getting ATC information", result["message"]);
return;
}
for (let session of result["items"]) { for (let session of result["items"]) {
// Separate each session by it's connection ID // Separate each session by it's connection ID
@@ -72,13 +74,13 @@ $.getJSON("/static/data/airports.json", function(json) {
// Map creation // Map creation
const MAP_ELEMENT = L.map("fox_map"); const MAP_ELEMENT = L.map("fox_map");
const MAP_DEFAULT_TARGET = L.latLng("47.50737", "19.04611"); const MAP_DEFAULT_ZOOM_VIEW = 2;
const MAP_DEFAULT_ZOOM_VIEW = 14; const MAP_DEFAULT_TARGET = L.latLng("0", "0");
let line = null; let line = null;
MAP_ELEMENT.setView(MAP_DEFAULT_TARGET, MAP_DEFAULT_ZOOM_VIEW); MAP_ELEMENT.setView(MAP_DEFAULT_TARGET, MAP_DEFAULT_ZOOM_VIEW);
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", { L.tileLayer("https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png", {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors', attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
subdomains: "abcd", subdomains: "abcd",
maxZoom: 20, maxZoom: 20,
@@ -153,3 +155,9 @@ function showSession(session) {
$(".atc_transfers").text(session["handoffsinitiated"]); $(".atc_transfers").text(session["handoffsinitiated"]);
$(".atc_received").text(session["handoffsreceived"]); $(".atc_received").text(session["handoffsreceived"]);
} }
function showAlert(title, message) {
$("#notification_wrapper").css("display", "block");
$("#notification_title").text(title);
$("#notification_message").text(message);
}
+23 -7
View File
@@ -10,33 +10,49 @@
<meta content="yes" name="apple-touch-fullscreen" /> <meta content="yes" name="apple-touch-fullscreen" />
<meta name="apple-mobile-web-app-status-bar-style" content="#FA8232"> <meta name="apple-mobile-web-app-status-bar-style" content="#FA8232">
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width = 320, initial-scale = 2.3, user-scalable = no"> <link rel="icon" href="{{ url_for('static', path='icon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='icon.ico') }}" type="image/x-icon">
<title>{% block title %}{% endblock %}</title> <title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
{% block header_css %}{% endblock %} {% block header_css %}{% endblock %}
</head> </head>
<style> <style>
.btn-outline-primary:hover, .btn-outline-primary:active {
color: white !important;
background-color: var(--main-rgb) !important;
border-color: var(--main-rgb) !important;
}
.btn-outline-primary {
border-color: var(--main-rgb);
color: var(--main-rgb);
}
:root { :root {
--main-rgb: #FA8232;
--bs-primary-rgb: 250, 130, 50 --bs-primary-rgb: 250, 130, 50
} }
</style> </style>
<body> <body>
<main> <main>
<header class="border-bottom">
<div class="container py-4"> <div class="container py-4">
<header class="pb-3 mb-4 border-bottom">
<a href="/" class="d-flex align-items-center text-dark text-decoration-none"> <a href="/" class="d-flex align-items-center text-dark text-decoration-none">
<span class="fs-4">🦊 VATSIM <span class="text-primary">Fox</span></span> <span class="fs-4">🦊 VATSIM <span class="text-primary">Fox</span></span>
</a> </a>
</div>
</header> </header>
<div class="container py-4">
{% block content %}{% endblock %} {% block content %}{% endblock %}
<footer class="pt-3 mt-4 text-muted border-top">
created by <a href="https://uw935.t.me/" style="text-decoration: underline; cursor: pointer;">uw935</a> (1606255)
</footer>
</div> </div>
<footer class="text-muted border-top">
<div class="container py-4">
created by
<a href="https://uw935.com/" style="text-decoration: underline; cursor: pointer;">uw935</a> (1606255)
</div>
</footer>
</main> </main>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
+3 -1
View File
@@ -7,7 +7,7 @@
<p class="col-md-8 fs-4">See your detailed profile statistic</p> <p class="col-md-8 fs-4">See your detailed profile statistic</p>
<div class="d-flex" role="search"> <div class="d-flex" role="search">
<input class="fox_input form-control me-2" type="number" placeholder="100000" aria-label="CID"> <input class="fox_input form-control me-2" type="number" placeholder="100000" aria-label="CID">
<button class="fox_submit btn btn-outline-success" type="submit">Search</button> <button class="fox_submit btn btn-outline-primary" type="submit">Search</button>
</div> </div>
<p class="fox_message text-primary col-md-8 fs-4"></p> <p class="fox_message text-primary col-md-8 fs-4"></p>
</div> </div>
@@ -20,10 +20,12 @@
<p>VATSIM Fox is an open-source project to make it easier for users to see their detailed profile statistic</p> <p>VATSIM Fox is an open-source project to make it easier for users to see their detailed profile statistic</p>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="h-100 p-5 bg-light border rounded-3"> <div class="h-100 p-5 bg-light border rounded-3">
<h2>Open source project</h2> <h2>Open source project</h2>
<p>This project is fully opened, so you can contribute in it if you want</p> <p>This project is fully opened, so you can contribute in it if you want</p>
<a href="https://github.com/uw935/vatsimfox"> <a href="https://github.com/uw935/vatsimfox">
<button class="btn btn-outline-secondary" type="button">GitHub</button> <button class="btn btn-outline-secondary" type="button">GitHub</button>
</a> </a>
+14 -7
View File
@@ -1,12 +1,19 @@
{% extends "/include/template.html" %} {% extends "/include/template.html" %}
{% block title %}User {{user_cid}} statistic — VATSIM Fox{% endblock %} {% block title %}User {{user["id"]}} statistic — VATSIM Fox{% endblock %}
{% block header_css %} {% block header_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='viewer.css') }}"> <link rel="stylesheet" href="{{ url_for('static', path='viewer.css') }}">
<link href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" rel="stylesheet"/> <link href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" rel="stylesheet"/>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="mb-5"> <div class="mb-5">
<h2 class="text-center text-primary mb-4">{{data[0]["id"]}}</h2> <div id="notification_wrapper">
<div class="mb-4 alert alert-warning" role="alert">
<h4 id="notification_title"></h4>
<span id="notification_message"></span>
</div>
</div>
<h2 class="text-center text-primary mb-4">{{user["id"]}}</h2>
<p class="fs-4 text-secondary">Date of registration: <span class="date_registration"></span></p> <p class="fs-4 text-secondary">Date of registration: <span class="date_registration"></span></p>
</div> </div>
@@ -91,7 +98,7 @@
<div class="col-md" style="height: 321px;"> <div class="col-md" style="height: 321px;">
<div class="h-auto p-5 bg-light border rounded-3"> <div class="h-auto p-5 bg-light border rounded-3">
<h2 class="atc_callsign">UUEE_DEL</h2> <h2 class="atc_callsign">Callsign</h2>
<span> <span>
<b>Start time:</b> <b>Start time:</b>
<span class="atc_start_time"></span> <span class="atc_start_time"></span>
@@ -133,11 +140,11 @@
{% block javascript_code %} {% block javascript_code %}
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script> <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script>
<script type="text/javascript"> <script type="text/javascript">
const USER_CID = '{{data[0]["id"]}}'; const USER_CID = '{{user["id"]}}';
const FLIGHTPLANS_API_URL = "/api/request?request_string=members/" + USER_CID + "/flightplans"; const FLIGHTPLANS_API_URL = "/api/request?request_string=members/" + USER_CID + "/flightplans";
const ATC_API_URL = "/api/request?request_string=members/" + USER_CID + "/atc"; const ATC_API_URL = "/api/request?request_string=members/" + USER_CID + "/atc";
$(".date_registration").text("{{data[0]['reg_date']}}".replace("T", " ")); $(".date_registration").text("{{user['reg_date']}}".replace("T", " "));
</script> </script>
<script type="text/javascript" src="{{ url_for('static', filename='viewer.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', path='viewer.js') }}"></script>
{% endblock %} {% endblock %}