Skip to content

Commit

Permalink
rewrite to flask
Browse files Browse the repository at this point in the history
  • Loading branch information
Headary committed Jul 20, 2024
1 parent f9ed867 commit 316f317
Show file tree
Hide file tree
Showing 26 changed files with 583 additions and 167 deletions.
14 changes: 0 additions & 14 deletions astrid.service

This file was deleted.

4 changes: 0 additions & 4 deletions build.sh

This file was deleted.

14 changes: 0 additions & 14 deletions config.ini.sample

This file was deleted.

38 changes: 38 additions & 0 deletions config.toml.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Flask config file
#
# This file is loaded by flask to app.config.
# It can overload flask configuration (https://flask.palletsprojects.com/en/3.0.x/config/)
# and it load custom config values for the app.
# All variable names must be upper case for flask to load them.
#

# Flask debug mode
# set to true for development
DEBUG = false

# Flask tests
# set to true for development
TESTING = false

# Secret key used for session encryption
# set to long random string for security
SECRET_KEY = '<secret>' # CHANGE THIS!

[OAUTH]

# Client ID created in OIDC provider (keycloak)
CLIENT_ID = '<client-id>'

# Client secret from OIDC provider (keycloak)
CLIENT_SECRET = '<client-secret>'

# URL for oauth client to pull information about openid configuration
SERVER_METADATA_URL = 'https://<oidc-url>/realms/master/.well-known/openid-configuration'

[GUNICORN]

# The number of worker processes for handling requests
THREADS = 2

# The number of worker threads in each process for handling requests
WORKERS = 4
40 changes: 0 additions & 40 deletions doc/initscript

This file was deleted.

4 changes: 4 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
FROM python:3.12

# tells entrypoint if it's a production or development environment
# and should run gunicorn or not
ENV MODE="production"

# install docker
RUN apt update && apt install -y docker.io containers-storage

Expand Down
4 changes: 3 additions & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3'
name: astrid

services:
astrid:
Expand All @@ -12,8 +12,10 @@ services:
TZ: 'Europe/Prague'
PUID: 1000
GUID: 65534
MODE: 'development'
privileged: true # needed for containers
volumes:
- ./data:/data
- ..:/app
ports:
- 8080:8080 # opened port mapping, not needed with proxy
10 changes: 7 additions & 3 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,16 @@ chown "$PUID:$GUID" /data
# create needed files if missing
su - $USER -c "mkdir -p /data/config /data/containers /data/log /data/repos /data/ssh"

su - $USER -c "cp -n /app/config.ini.sample /data/config/config.ini"
su - $USER -c "cp -n /app/repos.ini.sample /data/config/repos.ini"
su - $USER -c "cp -n /app/config.toml.sample /data/config/config.toml"
su - $USER -c "cp -n /app/repos.toml.sample /data/config/repos.toml"

if [ $(ls "/data/ssh" | grep ".pub" | wc -l) -eq 0 ]; then
su - $USER -c "ssh-keygen -t ed25519 -f /data/ssh/id_ed25519"
fi

dockerd &
su - $USER -c "python3 -u /app/main"
if [ "$MODE" == "development" ]; then
su - $USER -c "python3 -u /app/src/app.py"
else
su - $USER -c "gunicorn --config /app/src/gunicorn.conf.py --chdir /app/src"
fi
36 changes: 0 additions & 36 deletions main

This file was deleted.

6 changes: 0 additions & 6 deletions repos.ini.sample

This file was deleted.

12 changes: 12 additions & 0 deletions repos.toml.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Repos config file
#
# Declares all repositories in this app. Each repository is its own section
# specified by [reponame] with listed config values.
#

[fykos37] # reponame
git_path='[email protected]:FYKOS/fykos37.git' # ssh address of git repository, string
allowed_roles=['fksdb-fykos'] # array of allowed users, array of strings
build_cmd='make -k all' # build command, string
image_version='latest' # docker image version of buildtools, string, optional (default 'latest')
submodules=false # does repo have submodules, bool, optional (default false)
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
CherryPy
GitPython
Flask
AuthLib
requests
gunicorn
2 changes: 0 additions & 2 deletions setup.cfg

This file was deleted.

46 changes: 0 additions & 46 deletions setup.py

This file was deleted.

114 changes: 114 additions & 0 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from flask import Flask, abort, redirect, render_template, send_from_directory, session, url_for
import configparser
import os
import tomllib

from repository import Repository
from oauth import isLoggedIn, registerAuthRoutes, requireLogin

# TODO run using gunicorn for more threads

app = Flask(__name__,
static_folder='static',
template_folder='templates')

# load config
app.config.from_file("/data/config/config.toml", load=tomllib.load, text=False)

registerAuthRoutes(app)

# load repos
#reposConfig = configparser.ConfigParser()
#reposConfig.read('/data/config/repos.ini')
with open('/data/config/repos.toml', 'rb') as file:
reposConfig = tomllib.load(file)

repos = {repo: Repository(repo, '/data/repos/', reposConfig[repo]) for repo in reposConfig}

# automatically inject user to every rendered template
@app.context_processor
def inject_user():
return dict(user = session.get('user'))

#
# routes
#

@app.route('/')
def index():
if isLoggedIn():
return render_template('index.html.jinja', repos=repos)
return render_template('signin.html.jinja')

@app.route('/info/<string:repoName>')
@requireLogin
def info(repoName):
if repoName not in repos:
abort(404)
repo = repos[repoName]
if not repo.checkAccess():
abort(401)
return render_template('info.html.jinja', repo=repoName, log=repo.logger.getLogs())

@app.route('/buildlog/<string:repoName>')
@requireLogin
def buildLog(repoName):
if repoName not in repos:
abort(404)
repo = repos[repoName]
if not repo.checkAccess():
abort(401)
return render_template('buildlog.html.jinja', repo=repoName, log=repo.logger.getBuildLog())

@app.route('/build/<string:repoName>')
@requireLogin
def build(repoName):
if repoName not in repos:
abort(404)
repo = repos[repoName]
if not repo.checkAccess():
abort(401)

repo.execute()

return redirect(url_for('index'))

# TODO redirect to url with trailing / when showing folder for correct html hrefs
@app.route('/<string:repoName>/')
@app.route('/<string:repoName>/<path:path>')
@requireLogin
def repository(repoName, path=''):
if repoName not in repos:
abort(404)
repo = repos[repoName]
if not repo.checkAccess():
abort(401)

repoDir = os.path.normpath(os.path.join('/data/repos', repoName))
normalizedPath = os.path.normpath(path)
targetPath = os.path.normpath(os.path.join(repoDir, normalizedPath))

# check that listed directory is child of repository directory
# for potencial directory travelsal attacks
if not targetPath.startswith(repoDir):
abort(404)

if not os.path.exists(targetPath):
abort(404)

if os.path.isdir(targetPath):
directoryContent = next(os.walk(targetPath))
# filter directories and files starting with .
dirs=[d for d in sorted(directoryContent[1]) if not d.startswith('.')]
files=[f for f in sorted(directoryContent[2]) if not f.startswith('.')]

return render_template('listdir.html.jinja', path=os.path.normpath(os.path.join(repoName, normalizedPath)), repo=repoName, dirs=dirs, files=files)

if os.path.isfile(targetPath):
return send_from_directory(repoDir, normalizedPath)

abort(404)


if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
Loading

0 comments on commit 316f317

Please sign in to comment.