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 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 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: app:
image: git.snyman.info/raoul/codesmidgen:latest image: git.snyman.info/raoul/codesmidgen:latest
env: env:
- SQLALCHEMY_URL=postgres://codesmidgen:codesmidgen@postgres/codesmidgen - SMIDGEN_SECRET_KEY=yoursecrethere
- SQLALCHEMY_DATABASE_URL=postgres://codesmidgen:codesmidgen@postgres/codesmidgen
restart: unless-stopped restart: unless-stopped
ports: ports:
- "127.0.0.1:8000:8000" - "127.0.0.1:8000:8000"

View File

@ -2,48 +2,25 @@
""" """
CodeSmidgen, yet another paste bin CodeSmidgen, yet another paste bin
""" """
from configparser import ConfigParser
from pathlib import Path
import quart_flask_patch # noqa: F401 import quart_flask_patch # noqa: F401
from quart import Quart from quart import Quart
from codesmidgen.config import get_config
from codesmidgen.db import db from codesmidgen.db import db
from codesmidgen.views import views from codesmidgen.views import views
def read_config(config_path=None): def make_app() -> Quart:
"""
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):
""" """
Create the application object Create the application object
""" """
app = Quart(__name__) app = Quart(__name__)
# Load the config file app.config.update(get_config())
config = read_config(config_path)
app.config.update(config)
app.config.update({'SQLALCHEMY_TRACK_MODIFICATIONS': False})
db.init_app(app) db.init_app(app)
app.register_blueprint(views) app.register_blueprint(views)
@app.before_first_request @app.before_first_request
async def setup_db(): async def setup_db() -> None:
db.create_all() db.create_all()
return app return app

View File

@ -2,10 +2,9 @@
""" """
This is the entry point for the WSGI server This is the entry point for the WSGI server
""" """
from pathlib import Path
from codesmidgen import make_app from codesmidgen import make_app
application = make_app(Path(__file__).parent.parent) application = make_app()
if __name__ == '__main__': if __name__ == '__main__':
application.run(debug=True) 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 import string
from datetime import timedelta, datetime 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 import highlight
from pygments.formatters.html import HtmlFormatter from pygments.formatters.html import HtmlFormatter
from pygments.lexers import get_lexer_by_name, get_all_lexers 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 Encode the URL
""" """
@ -40,7 +40,7 @@ def _generate_short_url():
@views.route('/', methods=['GET']) @views.route('/', methods=['GET'])
async def index(): async def index() -> str:
""" """
Add a new sticky note Add a new sticky note
""" """
@ -51,7 +51,7 @@ async def index():
@views.route('/notes', methods=['GET']) @views.route('/notes', methods=['GET'])
async def notes(): async def notes() -> str:
""" """
Show a list of recent notes Show a list of recent notes
""" """
@ -64,7 +64,7 @@ async def notes():
@views.route('/about', methods=['GET']) @views.route('/about', methods=['GET'])
async def about(): async def about() -> str:
""" """
Show the about page Show the about page
""" """
@ -72,7 +72,7 @@ async def about():
@views.route('/', methods=['POST']) @views.route('/', methods=['POST'])
async def save(): async def save() -> Response:
""" """
Save a sticky note Save a sticky note
""" """
@ -107,7 +107,7 @@ async def save():
@views.route('/<string:note_url>', methods=['GET']) @views.route('/<string:note_url>', methods=['GET'])
async def view(note_url): async def view(note_url: str) -> Response | str:
""" """
Show a sticky note Show a sticky note
@ -125,7 +125,7 @@ async def view(note_url):
@views.route('/raw/<string:note_url>', methods=['GET']) @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 Show the raw version of a sticky note
@ -139,7 +139,7 @@ async def raw(note_url):
@views.route('/pygments.css', methods=['GET']) @views.route('/pygments.css', methods=['GET'])
async def pygments_css(): async def pygments_css() -> Response:
""" """
Return the Pygments CSS to the browser Return the Pygments CSS to the browser
""" """