trying to add gitea
This commit is contained in:
@@ -46,10 +46,12 @@ def create_app(config_class=Config):
|
||||
from .routes import main
|
||||
from .blueprints.slides import slides_bp
|
||||
from .blueprints.admin import admin_bp
|
||||
from .blueprints.internal import internal_bp
|
||||
|
||||
app.register_blueprint(main)
|
||||
app.register_blueprint(slides_bp)
|
||||
app.register_blueprint(admin_bp)
|
||||
app.register_blueprint(internal_bp)
|
||||
|
||||
@app.template_filter('md')
|
||||
def render_markdown(text):
|
||||
|
||||
5
app/blueprints/internal/__init__.py
Normal file
5
app/blueprints/internal/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from flask import Blueprint
|
||||
|
||||
internal_bp = Blueprint('internal', __name__, url_prefix='/internal')
|
||||
|
||||
from . import routes #noqa
|
||||
87
app/blueprints/internal/routes.py
Normal file
87
app/blueprints/internal/routes.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import hmac
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
from flask import request, jsonify, current_app
|
||||
from app.models import db, Deck, Course, Password
|
||||
from . import internal_bp
|
||||
|
||||
|
||||
@internal_bp.route('/register-deck', methods=['POST'])
|
||||
def register_deck():
|
||||
# Verify shared secret
|
||||
token = request.headers.get('X-Internal-Token', '')
|
||||
if not hmac.compare_digest(token, current_app.config['INTERNAL_TOKEN']):
|
||||
return jsonify({'error': 'Unauthorized'}), 401
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': 'Invalid JSON'}), 400
|
||||
|
||||
for field in ('course', 'deck', 'name'):
|
||||
if not data.get(field):
|
||||
return jsonify({'error': f'Missing required field: {field}'}), 400
|
||||
|
||||
# Look up course by folder name
|
||||
course = db.session.execute(
|
||||
db.select(Course).where(Course.folder == data['course'])
|
||||
).scalar_one_or_none()
|
||||
if not course:
|
||||
return jsonify({'error': f"Course '{data['course']}' not found"}), 404
|
||||
|
||||
# Upsert deck
|
||||
# NOTE: Deck.__init__ takes course= (object), not course_id=
|
||||
deck = db.session.execute(
|
||||
db.select(Deck).where(
|
||||
Deck.deck == data['deck'],
|
||||
Deck.course_id == course.id
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
|
||||
if deck:
|
||||
deck.name = data['name']
|
||||
deck.description = data['description']
|
||||
deck.export_file = data.get('export_file') or None
|
||||
else:
|
||||
deck = Deck(
|
||||
name = data['name'],
|
||||
deck = data['deck'],
|
||||
course = course, # pass object, not course_id
|
||||
description = data['description'],
|
||||
index_file = data.get('index_file', 'index.html'),
|
||||
export_file = data.get('export_file') or None,
|
||||
)
|
||||
db.session.add(deck)
|
||||
db.session.flush() # populate deck.id before linking password
|
||||
|
||||
# Upsert password if provided
|
||||
if data.get('password'):
|
||||
expires_at = None
|
||||
if data.get('password_expires'):
|
||||
try:
|
||||
# Parse ISO string and convert to UTC naive (matches UTCDateTime model)
|
||||
expires_at = datetime.fromisoformat(
|
||||
data['password_expires'].replace('Z', '+00:00')
|
||||
).astimezone(pytz.utc)
|
||||
except ValueError:
|
||||
current_app.logger.warning(
|
||||
f"Invalid password_expires format: {data['password_expires']}"
|
||||
)
|
||||
|
||||
existing_pw = db.session.execute(
|
||||
db.select(Password).where(Password.value == data['password'])
|
||||
).scalar_one_or_none()
|
||||
|
||||
if existing_pw:
|
||||
existing_pw.expires_at = expires_at
|
||||
if deck not in existing_pw.decks:
|
||||
existing_pw.decks.append(deck)
|
||||
else:
|
||||
pw = Password(value=data['password'], expires_at=expires_at)
|
||||
pw.decks.append(deck)
|
||||
db.session.add(pw)
|
||||
|
||||
db.session.commit()
|
||||
current_app.logger.info(
|
||||
f"Registered deck '{data['deck']}' for course '{data['course']}'"
|
||||
)
|
||||
return jsonify({'status': 'ok', 'deck': data['deck']}), 200
|
||||
@@ -8,6 +8,7 @@ class Config:
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL")
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
ADMIN_PASSWORD = os.environ.get("ADMIN_PASSWORD")
|
||||
INTERNAL_TOKEN = os.environ.get("INTERNAL_TOKEN")
|
||||
COURSE_MAP = {
|
||||
"GRNVS25": "GRNVS Tutorium SS 25",
|
||||
"IT-Sec25": "IT-Sec Tutorium WS 25/26",
|
||||
|
||||
@@ -83,7 +83,7 @@ class Deck(db.Model):
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
name: Mapped[str] = mapped_column(String(60), unique=True, nullable=False)
|
||||
deck: Mapped[str] = mapped_column(String(60), nullable=False)
|
||||
description: Mapped[str] = mapped_column(String(60))
|
||||
description: Mapped[str] = mapped_column(String(200))
|
||||
index_file: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||
export_file: Mapped[Optional[str]] = mapped_column(String(40), nullable=True)
|
||||
|
||||
|
||||
@@ -75,3 +75,18 @@ services:
|
||||
depends_on:
|
||||
- prometheus
|
||||
- db
|
||||
|
||||
act-runner:
|
||||
image: gitea/act_runner:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- GITEA_INSTANCE_URL=https://gitea.cato447.de
|
||||
- GITEA_RUNNER_REGISTRATION_TOKEN=${ACT_RUNNER_TOKEN}
|
||||
- GITEA_RUNNER_NAME=tutor-runner
|
||||
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:22
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./data/act_runner:/data
|
||||
- ./data/slides:/slides
|
||||
- ./data/exports:/exports
|
||||
mem_limit: 2g
|
||||
|
||||
Reference in New Issue
Block a user