From 2e3553044cdb1f2a57267f79c0070525acd13db2 Mon Sep 17 00:00:00 2001 From: Jan Felix Wiebe Date: Fri, 13 Jun 2025 22:53:29 +0200 Subject: [PATCH] initial commit --- .env.example | 1 + .gitignore | 1 + Dockerfile | 24 +++++++++ __pycache__/spaceapi.cpython-313.pyc | Bin 0 -> 2209 bytes app.py | 77 +++++++++++++++++++++++++++ requirements.txt | 3 ++ spaceapi.py | 39 ++++++++++++++ 7 files changed, 145 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 __pycache__/spaceapi.cpython-313.pyc create mode 100644 app.py create mode 100644 requirements.txt create mode 100644 spaceapi.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6b4d0b0 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +SPACEAPI_TOKEN=your-secret-token-here \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dcb26ea --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Use Python 3.11 slim image as base +FROM python:3.11-slim + +# Set working directory +WORKDIR /app + +# Copy requirements first to leverage Docker cache +COPY requirements.txt . + +# Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application +COPY . . + +# Expose the port the app runs on +EXPOSE 5000 + +# Set environment variables +ENV FLASK_APP=app.py +ENV FLASK_ENV=production + +# Command to run the application +CMD ["flask", "run", "--host=0.0.0.0"] \ No newline at end of file diff --git a/__pycache__/spaceapi.cpython-313.pyc b/__pycache__/spaceapi.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b47d14fdfe62bf0fa69ffc94dded63f844d3b7e GIT binary patch literal 2209 zcmaJ?O>Y}T7@pmo{q{%P1gC0MDO72}d?bf95=d1^f>=lac)FJM+xWzVo~vo)n7~ zfuVfY*!xi?!E8X=z|7Cl*D^QJLP=YC1O>aofWWjWk&1y4UG2AG}dQF9nFB z$D~;^xZ%2y*YjPMTdv!SJNqHp1=syz-wSi5?7F)FOVTii{3r%mNm9mP)r+Dy6(31p zRox9^IQtdXO>nX8dTGjno&D4YmX*MZ3H%7JukXb@e;veOL3*&>XYpr#J59g{?tvWZ zN#ASxUO#v{@!36}{qKCOf51#QPyk*ssQ`RKo)s4cjiFf@)Bz6bY39Pc6E?Z--;uMcRDJ-X+N*BOLl0YWv|n+R7C z=EdtW5(p?&f*A<$vLIf6RxcjCb?c~l`+og_@`T>b0){(Zk`iUaF?j>vjDVeFr$ZqV z)tbibMi71OcN{FD5#|mz{HW89QA+(weGE)K`H_9JxcY>y zW>Ld4K^gnxkI7FMrWwO@=308t529}7H8xYGBcP~RlRf3SaI=K0Xs=nK-N^nxHrabX zU@a%#1B?|(lAe;+pORPqCYSyq_A|+PE-O-bOaLYdk?paVT3|hwskAUA06Brvf*@q+ ieQ8V(b4n~2Iafida>7)`8DUCS#sokR|H@zqhyMflfXQ0` literal 0 HcmV?d00001 diff --git a/app.py b/app.py new file mode 100644 index 0000000..e5581b6 --- /dev/null +++ b/app.py @@ -0,0 +1,77 @@ +from flask import Flask, jsonify, request +from spaceapi import SpaceAPI, Location, Contact, State, LinkedSpace +import json +from dotenv import load_dotenv +import os + +# Load environment variables from .env file +load_dotenv() + +app = Flask(__name__) + +# Global variable to store the current state +current_state = {} + +def require_token(f): + def decorated(*args, **kwargs): + token = request.headers.get('X-SpaceAPI-Token') + if not token or token != os.getenv('SPACEAPI_TOKEN'): + return jsonify({"error": "Invalid or missing token"}), 401 + return f(*args, **kwargs) + decorated.__name__ = f.__name__ + return decorated + +@app.route('/') +def hello_world(): + space = SpaceAPI( + api_compatibility=["14", "15"], + space="Leitstelle511 - Chaos Computer Club Hannover", + logo="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Logo_CCC.svg/330px-Logo_CCC.svg.png", + url="http://hannover.ccc.de", + location=Location( + address="Klaus-Müller-Kilian-Weg 2, 30167 Hannover, Germany", + lat=52.3881, + lon=9.7181 + ), + contact=Contact( + email="kontakt@hannover.ccc.de", + irc="irc://hackint.eu/leitstelle511", + ml="511@hannover.ccc.de", + matrix="#leitstelle511-public:leitstelle511.net" + ), + state=current_state, + projects=[ + "https://hannover.ccc.de/projekte/schule/", + "https://hackover.de" + ], + linked_spaces=[ + LinkedSpace( + endpoint="https://leinelab.org/api/spaceapi.json", + website="https://leinelab.org" + ) + ] + ) + return jsonify(space.__dict__) + +@app.route('/state', methods=['POST']) +@require_token +def update_state(): + global current_state + try: + data = request.get_json() + if not data or 'open' not in data: + return jsonify({"error": "Invalid state data. 'open' field is required"}), 400 + + # Create new state with provided open status + # lastchange will be automatically set to current timestamp + current_state = State(open=data['open']) + + return jsonify({ + "message": "State updated successfully", + "state": current_state.__dict__ + }) + except Exception as e: + return jsonify({"error": str(e)}), 400 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..af44043 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask==3.0.2 +python-dotenv==1.0.1 +spaceapi==0.1.0 \ No newline at end of file diff --git a/spaceapi.py b/spaceapi.py new file mode 100644 index 0000000..5ae977f --- /dev/null +++ b/spaceapi.py @@ -0,0 +1,39 @@ +from dataclasses import dataclass, field +from typing import List, Optional +from datetime import datetime +import time + +@dataclass +class Location: + address: str + lat: float + lon: float + +@dataclass +class Contact: + email: str + irc: str + ml: str + matrix: str + +@dataclass +class State: + open: bool + lastchange: int = field(default_factory=lambda: int(time.time())) + +@dataclass +class LinkedSpace: + endpoint: str + website: str + +@dataclass +class SpaceAPI: + api_compatibility: List[str] + space: str + logo: str + url: str + location: Location + contact: Contact + state: State + projects: List[str] + linked_spaces: List[LinkedSpace]