cfg.site with markdown

This commit is contained in:
Jörn-Michael Miehe 2023-11-03 14:40:44 +00:00
parent 6d015420c0
commit 4fbdb94caa
8 changed files with 99 additions and 91 deletions

View file

@ -3,7 +3,8 @@ import tomllib
from enum import Enum
from random import Random
from pydantic import BaseModel, field_validator
from markdown import markdown
from pydantic import BaseModel, ConfigDict, field_validator
from .dav.webdav import WebDAV
from .settings import SETTINGS
@ -25,6 +26,8 @@ class TransformedString(BaseModel):
# whitespace entfernen
IGNORE = "IGNORE"
# special chars
class __Case(str, Enum):
# unverändert
KEEP = "KEEP"
@ -78,10 +81,27 @@ class TransformedString(BaseModel):
return result
class Puzzle(BaseModel):
class Site(BaseModel):
model_config = ConfigDict(validate_default=True)
# Titel
title: str
# Untertitel
subtitle: str
# Inhalt der Seite
content: str
# Fußzeile der Seite
footer: str = "**Advent22** by [Lenaisten e.V.](//www.lenaisten.de)"
@field_validator("content", "footer", mode="after")
def parse_md(cls, v) -> str:
return markdown(v)
class Puzzle(BaseModel):
# Tag, an dem der Kalender startet
begin_day: int = 1
@ -108,6 +128,7 @@ class Config(BaseModel):
solution: TransformedString
# Weitere Einstellungen
site: Site
puzzle: Puzzle
image: Image
@ -117,11 +138,6 @@ class Config(BaseModel):
# Serverseitiger zusätzlicher "random" seed
random_seed: str = ""
# Fußzeile der Seite
footer: str = (
'<strong>Advent22</strong> by <a href="//www.lenaisten.de">Lenaisten e.V.</a>'
)
async def get_config() -> Config:
"""

View file

@ -5,7 +5,7 @@ from fastapi.responses import StreamingResponse
from PIL import Image
from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config
from ..core.config import Config, get_config
from ..core.config import Config, Site, get_config
from ..core.depends import get_all_event_dates, get_day_image
from ..core.helpers import EventDates, api_return_ico, api_return_jpeg, load_image
from ._security import user_can_view_day, user_is_admin, user_visible_days
@ -45,19 +45,15 @@ async def get_favicon(
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
# - favicon
# - "user-config-model"
@router.get("/title")
async def get_title(
@router.get("/site_config")
async def get_site_config(
cfg: Config = Depends(get_config),
) -> str:
) -> Site:
"""
Lädt Kalendertitel
Seiteninhalt
"""
return cfg.puzzle.title
return cfg.site
@router.get("/doors")
@ -72,17 +68,6 @@ async def get_doors(
return [door for door in cal_cfg.doors if door.day in visible_days]
@router.get("/footer")
async def get_footer(
cfg: Config = Depends(get_config),
) -> str:
"""
Seiten-Fußzeile lesen
"""
return cfg.footer
@router.get(
"/image_{day}",
response_class=StreamingResponse,

17
api/poetry.lock generated
View file

@ -580,6 +580,21 @@ html5 = ["html5lib"]
htmlsoup = ["BeautifulSoup4"]
source = ["Cython (>=0.29.35)"]
[[package]]
name = "markdown"
version = "3.5.1"
description = "Python implementation of John Gruber's Markdown."
optional = false
python-versions = ">=3.8"
files = [
{file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"},
{file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"},
]
[package.extras]
docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
testing = ["coverage", "pyyaml"]
[[package]]
name = "mccabe"
version = "0.7.0"
@ -1376,4 +1391,4 @@ typing-extensions = ">=4.4.0"
[metadata]
lock-version = "2.0"
python-versions = ">=3.11,<3.13"
content-hash = "e97f2ab7e4314a0b6ce3725b8068a147a06e5ac5c9ee8e5406ca546dac3075cd"
content-hash = "73d5978c4787027b57334c5e5ac98df12e7b9e958bc92a5c326b512861986c57"

View file

@ -21,6 +21,7 @@ redis = {extras = ["hiredis"], version = "^5.0.1"}
tomli-w = "^1.0.0"
uvicorn = {extras = ["standard"], version = "^0.23.2"}
webdavclient3 = "^3.14.6"
markdown = "^3.5.1"
[tool.poetry.group.dev.dependencies]
black = "^23.10.1"

View file

@ -1,8 +1,8 @@
<template>
<section class="hero is-small is-primary">
<div class="hero-body">
<h1 class="title is-uppercase">{{ title }}</h1>
<h2 class="subtitle">{{ subtitle }}</h2>
<h1 class="title is-uppercase">{{ store.site_config.title }}</h1>
<h2 class="subtitle">{{ store.site_config.subtitle }}</h2>
</div>
</section>
@ -18,7 +18,7 @@
<footer class="footer">
<div class="level">
<div class="level-item">
<p v-html="footer" />
<p v-html="store.site_config.footer" />
</div>
<div class="level-right">
<div class="level-item">
@ -50,25 +50,10 @@ import UserView from "./components/UserView.vue";
},
})
export default class extends Vue {
public title = "Adventskalender";
public subtitle = "Lorem Ipsum";
public footer = "";
public readonly store = advent22Store();
public mounted(): void {
Promise.all([
this.$advent22.api_get<string>("user/title"),
this.subtitle, // TODO
this.$advent22.api_get<string>("user/footer"),
])
.then(([title, subtitle, footer]) => {
document.title = `${title} ${subtitle}`;
this.title = title;
this.subtitle = subtitle;
this.footer = footer;
})
.catch(this.$advent22.alert_user_error);
this.store.init();
}
}
</script>

View file

@ -37,9 +37,6 @@
<h3>Rätsel</h3>
<dl>
<dt>Titel</dt>
<dd>{{ title }}</dd>
<dt>Offene Türchen</dt>
<dd>{{ num_user_doors }}</dd>
@ -47,9 +44,6 @@
<dd v-if="next_door === null">Kein nächstes Türchen</dd>
<dd v-else><CountDown :millis="next_door" /></dd>
<dt>Fußzeile</dt>
<dd class="is-family-monospace">{{ footer }}</dd>
<dt>Erstes Türchen</dt>
<dd>{{ fmt_puzzle_date("first") }}</dd>
@ -218,8 +212,6 @@ export default class extends Vue {
},
};
public doors: DoorsSaved = [];
public title = "";
public footer = "";
public num_user_doors = 0;
public next_door: number | null = null;
public dav_credentials: Credentials = ["", ""];
@ -237,22 +229,16 @@ export default class extends Vue {
this.$advent22.api_get<AdminConfigModel>("admin/config_model"),
this.$advent22.api_get<DoorsSaved>("admin/doors"),
this.$advent22.api_get<DoorsSaved>("user/doors"),
this.$advent22.api_get<string>("user/title"),
this.$advent22.api_get<string>("user/footer"),
this.$advent22.api_get<number | null>("user/next_door"),
])
.then(
([admin_config_model, doors, user_doors, title, footer, next_door]) => {
this.admin_config_model = admin_config_model;
this.doors = doors;
this.num_user_doors = user_doors.length;
this.title = title;
this.footer = footer;
this.next_door = next_door;
.then(([admin_config_model, doors, user_doors, next_door]) => {
this.admin_config_model = admin_config_model;
this.doors = doors;
this.num_user_doors = user_doors.length;
this.next_door = next_door;
ready();
},
)
ready();
})
.catch(fail);
}

View file

@ -35,6 +35,13 @@ export interface AdminConfigModel {
};
}
export interface SiteConfigModel {
title: string;
subtitle: string;
content: string;
footer: string;
}
export interface NumStrDict {
[key: number]: string;
}

View file

@ -1,4 +1,4 @@
import { Credentials } from "@/lib/api";
import { Credentials, SiteConfigModel } from "@/lib/api";
import { ADVENT22 } from "@/plugins/advent22";
import { AxiosBasicCredentials } from "axios";
import { acceptHMRUpdate, defineStore } from "pinia";
@ -9,27 +9,17 @@ const empty_creds = () => ["", ""] as Credentials;
export const advent22Store = defineStore({
id: "advent22",
state: () => {
ADVENT22.api_get_blob("user/favicon")
.then((favicon_src) => {
const link: HTMLLinkElement =
document.querySelector("link[rel*='icon']") ||
document.createElement("link");
link.rel = "shortcut icon";
link.type = "image/x-icon";
link.href = favicon_src;
if (link.parentElement === null)
document.getElementsByTagName("head")[0].appendChild(link);
})
.catch(() => {});
return {
api_creds: empty_creds(),
is_admin: false,
is_touch_device: check_touch_device(),
};
},
state: () => ({
api_creds: empty_creds(),
is_admin: false,
site_config: {
title: "Adventskalender",
subtitle: "Lorem Ipsum",
content: "",
footer: "",
} as SiteConfigModel,
is_touch_device: check_touch_device(),
}),
getters: {
axios_creds: (state): AxiosBasicCredentials => {
@ -39,6 +29,29 @@ export const advent22Store = defineStore({
},
actions: {
init(): void {
ADVENT22.api_get_blob("user/favicon")
.then((favicon_src) => {
const link: HTMLLinkElement =
document.querySelector("link[rel*='icon']") ||
document.createElement("link");
link.rel = "shortcut icon";
link.type = "image/x-icon";
link.href = favicon_src;
if (link.parentElement === null)
document.getElementsByTagName("head")[0].appendChild(link);
})
.catch(() => {});
ADVENT22.api_get<SiteConfigModel>("user/site_config")
.then((site_config) => {
document.title = `${site_config.title} ${site_config.subtitle}`;
this.site_config = site_config;
})
.catch(ADVENT22.alert_user_error);
},
login(creds: Credentials = empty_creds()): Promise<boolean> {
this.api_creds = creds;