Merge pull request 'Getting things ready to containerize CodeSmigen' (#6) from container-image into master
ci/woodpecker/push/woodpecker Pipeline was successful Details

Reviewed-on: #6
This commit is contained in:
raoul 2023-07-28 20:10:40 +00:00
commit 96ed72778a
6 changed files with 80 additions and 41 deletions

View File

@ -1,6 +1,6 @@
FROM python:3.11
RUN pip install stickynotes --index-url https://git.snyman.info/packages/raoul/index
RUN pip install --extra-index-url https://git.snyman.info/api/packages/raoul/pypi/simple/ CodeSmidgen hypercorn
EXPOSE 8000
CMD ["hypercorn", "stickynotes.app"]
CMD ["hypercorn", "codesmidgen.app"]

View File

@ -25,7 +25,8 @@ The easiest way to install CodeSmidgen is via Docker and Docker Compose. Here's
app:
image: git.snyman.info/raoul/codesmidgen:latest
env:
- SQLALCHEMY_URL=postgres://codesmidgen:codesmidgen@postgres/codesmidgen
- SMIDGEN_SECRET_KEY=yoursecrethere
- SQLALCHEMY_DATABASE_URL=postgres://codesmidgen:codesmidgen@postgres/codesmidgen
restart: unless-stopped
ports:
- "127.0.0.1:8000:8000"

View File

@ -2,48 +2,25 @@
"""
CodeSmidgen, yet another paste bin
"""
from configparser import ConfigParser
from pathlib import Path
import quart_flask_patch # noqa: F401
from quart import Quart
from codesmidgen.config import get_config
from codesmidgen.db import db
from codesmidgen.views import views
def read_config(config_path=None):
"""
Read the configuration file and return the values in a dictionary
"""
if config_path:
config_file = config_path / 'codesmidgen.cfg'
else:
config_file = Path(__file__).parent / '..' / 'codesmidgen.cfg'
if not config_file.exists():
return {}
config_parser = ConfigParser()
config_parser.read(config_file)
config = {}
for option in config_parser.options('codesmidgen'):
config[option.upper()] = config_parser.get('codesmidgen', option)
return config
def make_app(config_path=None):
def make_app() -> Quart:
"""
Create the application object
"""
app = Quart(__name__)
# Load the config file
config = read_config(config_path)
app.config.update(config)
app.config.update({'SQLALCHEMY_TRACK_MODIFICATIONS': False})
app.config.update(get_config())
db.init_app(app)
app.register_blueprint(views)
@app.before_first_request
async def setup_db():
async def setup_db() -> None:
db.create_all()
return app

View File

@ -2,10 +2,9 @@
"""
This is the entry point for the WSGI server
"""
from pathlib import Path
from codesmidgen import make_app
application = make_app(Path(__file__).parent.parent)
application = make_app()
if __name__ == '__main__':
application.run(debug=True)

62
codesmidgen/config.py Normal file
View File

@ -0,0 +1,62 @@
import json
import os
from configparser import ConfigParser
from pathlib import Path
from typing import Any
CONFIG_DEFAULTS = {
'SQLALCHEMY_TRACK_MODIFICATIONS': False
}
CONFIG_PREFIXES = ['SMIDGEN_', 'SQLALCHEMY_']
STRIPPED_PREFIXES = ['SMIDGEN_']
def read_from_file() -> dict:
"""Read the configuration file and return the values in a dictionary"""
config_file = Path(__file__).parent / '..' / 'codesmidgen.cfg'
if not config_file.exists():
return {}
config_parser = ConfigParser()
config_parser.read(config_file)
config: dict[str, Any] = {}
for option in config_parser.options('codesmidgen'):
config[option.upper()] = config_parser.get('codesmidgen', option)
return config
def read_from_envvars() -> dict:
"""Read the configuration from environment variables"""
config: dict[str, Any] = {}
for key in sorted(os.environ):
if not any([key.startswith(prefix) for prefix in CONFIG_PREFIXES]):
continue
value = os.environ[key]
try:
value = json.loads(value)
except Exception:
# If the value is not JSON parseable, just leave it as a string
pass
for prefix in STRIPPED_PREFIXES:
key = key.replace(prefix, '')
# Check if there are any "nested" values
if '__' not in key:
config[key] = value
continue
# Navigate the nested values and build the structure
*parents, child = key.split('__')
item = config
for parent in parents:
if parent not in item:
item[parent] = {}
item = item[parent]
item[child] = value
return config
def get_config() -> dict:
"""Read configuration from files, environment variables, etc."""
config = {}
config.update(CONFIG_DEFAULTS)
config.update(read_from_file())
config.update(read_from_envvars())
return config

View File

@ -7,7 +7,7 @@ import secrets
import string
from datetime import timedelta, datetime
from quart import Blueprint, redirect, request, flash, make_response, current_app, render_template
from quart import Blueprint, Response, redirect, request, flash, make_response, current_app, render_template
from pygments import highlight
from pygments.formatters.html import HtmlFormatter
from pygments.lexers import get_lexer_by_name, get_all_lexers
@ -30,7 +30,7 @@ EXPIRY_DELTAS = {
}
def _generate_short_url():
def _generate_short_url() -> str:
"""
Encode the URL
"""
@ -40,7 +40,7 @@ def _generate_short_url():
@views.route('/', methods=['GET'])
async def index():
async def index() -> str:
"""
Add a new sticky note
"""
@ -51,7 +51,7 @@ async def index():
@views.route('/notes', methods=['GET'])
async def notes():
async def notes() -> str:
"""
Show a list of recent notes
"""
@ -64,7 +64,7 @@ async def notes():
@views.route('/about', methods=['GET'])
async def about():
async def about() -> str:
"""
Show the about page
"""
@ -72,7 +72,7 @@ async def about():
@views.route('/', methods=['POST'])
async def save():
async def save() -> Response:
"""
Save a sticky note
"""
@ -107,7 +107,7 @@ async def save():
@views.route('/<string:note_url>', methods=['GET'])
async def view(note_url):
async def view(note_url: str) -> Response | str:
"""
Show a sticky note
@ -125,7 +125,7 @@ async def view(note_url):
@views.route('/raw/<string:note_url>', methods=['GET'])
async def raw(note_url):
async def raw(note_url: str) -> Response | str:
"""
Show the raw version of a sticky note
@ -139,7 +139,7 @@ async def raw(note_url):
@views.route('/pygments.css', methods=['GET'])
async def pygments_css():
async def pygments_css() -> Response:
"""
Return the Pygments CSS to the browser
"""