Merge pull request 'Getting things ready to containerize CodeSmigen' (#6) from container-image into master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #6
This commit is contained in:
commit
96ed72778a
@ -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"]
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
62
codesmidgen/config.py
Normal 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
|
@ -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
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user