Monorepo: consolidate 7 repos into one
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s
Combines shared, blog, market, cart, events, federation, and account into a single repository. Eliminates submodule sync, sibling model copying at build time, and per-app CI orchestration. Changes: - Remove per-app .git, .gitmodules, .gitea, submodule shared/ dirs - Remove stale sibling model copies from each app - Update all 6 Dockerfiles for monorepo build context (root = .) - Add build directives to docker-compose.yml - Add single .gitea/workflows/ci.yml with change detection - Add .dockerignore for monorepo build context - Create __init__.py for federation and account (cross-app imports)
This commit is contained in:
7
market/bp/market/__init__.py
Normal file
7
market/bp/market/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
# create the blueprint at package import time
|
||||
from .routes import register # = Blueprint("browse_bp", __name__)
|
||||
|
||||
# import routes AFTER browse_bp is defined so routes can attach to it
|
||||
from . import routes # noqa: F401
|
||||
0
market/bp/market/admin/__init__.py
Normal file
0
market/bp/market/admin/__init__.py
Normal file
28
market/bp/market/admin/routes.py
Normal file
28
market/bp/market/admin/routes.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from quart import (
|
||||
render_template, make_response, Blueprint
|
||||
)
|
||||
|
||||
|
||||
from shared.browser.app.authz import require_admin
|
||||
|
||||
|
||||
def register():
|
||||
bp = Blueprint("admin", __name__, url_prefix='/admin')
|
||||
|
||||
# ---------- Pages ----------
|
||||
@bp.get("/")
|
||||
@require_admin
|
||||
async def admin():
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
|
||||
# Determine which template to use based on request type
|
||||
if not is_htmx_request():
|
||||
# Normal browser request: full page with layout
|
||||
html = await render_template("_types/market/admin/index.html")
|
||||
else:
|
||||
html = await render_template("_types/market/admin/_oob_elements.html")
|
||||
|
||||
return await make_response(html)
|
||||
return bp
|
||||
0
market/bp/market/filters/__init__.py
Normal file
0
market/bp/market/filters/__init__.py
Normal file
101
market/bp/market/filters/qs.py
Normal file
101
market/bp/market/filters/qs.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from quart import request
|
||||
|
||||
from typing import Iterable, Optional, Union
|
||||
|
||||
from shared.browser.app.filters.qs_base import (
|
||||
KEEP, _norm, make_filter_set, build_qs,
|
||||
)
|
||||
from shared.browser.app.filters.query_types import MarketQuery
|
||||
|
||||
|
||||
def decode() -> MarketQuery:
|
||||
page = int(request.args.get("page", 1))
|
||||
search = request.args.get("search")
|
||||
sort = request.args.get("sort")
|
||||
liked = request.args.get("liked")
|
||||
|
||||
selected_brands = tuple(s.strip() for s in request.args.getlist("brand") if s.strip())
|
||||
selected_stickers = tuple(s.strip().lower() for s in request.args.getlist("sticker") if s.strip())
|
||||
selected_labels = tuple(s.strip().lower() for s in request.args.getlist("label") if s.strip())
|
||||
|
||||
return MarketQuery(page, search, sort, selected_brands, selected_stickers, selected_labels, liked)
|
||||
|
||||
|
||||
def makeqs_factory():
|
||||
"""
|
||||
Build a makeqs(...) that starts from the current filters + page.
|
||||
Auto-resets page to 1 when filters change unless you pass page explicitly.
|
||||
"""
|
||||
q = decode()
|
||||
base_stickers = [s for s in q.selected_stickers if (s or "").strip()]
|
||||
base_labels = [s for s in q.selected_labels if (s or "").strip()]
|
||||
base_brands = [s for s in q.selected_brands if (s or "").strip()]
|
||||
base_search = q.search or None
|
||||
base_liked = q.liked or None
|
||||
base_sort = q.sort or None
|
||||
base_page = int(q.page or 1)
|
||||
|
||||
def makeqs(
|
||||
*,
|
||||
clear_filters: bool = False,
|
||||
add_sticker: Union[str, Iterable[str], None] = None,
|
||||
remove_sticker: Union[str, Iterable[str], None] = None,
|
||||
add_label: Union[str, Iterable[str], None] = None,
|
||||
remove_label: Union[str, Iterable[str], None] = None,
|
||||
add_brand: Union[str, Iterable[str], None] = None,
|
||||
remove_brand: Union[str, Iterable[str], None] = None,
|
||||
search: Union[str, None, object] = KEEP,
|
||||
sort: Union[str, None, object] = KEEP,
|
||||
page: Union[int, None, object] = None,
|
||||
extra: Optional[Iterable[tuple]] = None,
|
||||
leading_q: bool = True,
|
||||
liked: Union[bool, None, object] = KEEP,
|
||||
) -> str:
|
||||
stickers = make_filter_set(base_stickers, add_sticker, remove_sticker, clear_filters)
|
||||
labels = make_filter_set(base_labels, add_label, remove_label, clear_filters)
|
||||
brands = make_filter_set(base_brands, add_brand, remove_brand, clear_filters)
|
||||
|
||||
final_search = None if clear_filters else base_search if search is KEEP else ((search or "").strip() or None)
|
||||
final_sort = base_sort if sort is KEEP else (sort or None)
|
||||
final_liked = None if clear_filters else base_liked if liked is KEEP else liked
|
||||
|
||||
# Did filters change?
|
||||
filters_changed = (
|
||||
set(map(_norm, stickers)) != set(map(_norm, base_stickers))
|
||||
or set(map(_norm, labels)) != set(map(_norm, base_labels))
|
||||
or set(map(_norm, brands)) != set(map(_norm, base_brands))
|
||||
or final_search != base_search
|
||||
or final_sort != base_sort
|
||||
or final_liked != base_liked
|
||||
)
|
||||
|
||||
# Page logic
|
||||
if page is KEEP:
|
||||
final_page = 1 if filters_changed else base_page
|
||||
else:
|
||||
final_page = page
|
||||
|
||||
# Build params
|
||||
params = []
|
||||
for s in stickers:
|
||||
params.append(("sticker", s))
|
||||
for s in labels:
|
||||
params.append(("label", s))
|
||||
for s in brands:
|
||||
params.append(("brand", s))
|
||||
if final_search:
|
||||
params.append(("search", final_search))
|
||||
if final_liked is not None:
|
||||
params.append(("liked", final_liked))
|
||||
if final_sort:
|
||||
params.append(("sort", final_sort))
|
||||
if final_page is not None:
|
||||
params.append(("page", str(final_page)))
|
||||
if extra:
|
||||
for k, v in extra:
|
||||
if v is not None:
|
||||
params.append((k, str(v)))
|
||||
|
||||
return build_qs(params, leading_q=leading_q)
|
||||
|
||||
return makeqs
|
||||
49
market/bp/market/routes.py
Normal file
49
market/bp/market/routes.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from quart import Blueprint, g, render_template, make_response, url_for
|
||||
|
||||
|
||||
from ..browse.routes import register as register_browse_bp
|
||||
|
||||
from .filters.qs import makeqs_factory
|
||||
from ..browse.services.nav import get_nav
|
||||
from ..api.routes import products_api
|
||||
from .admin.routes import register as register_admin
|
||||
|
||||
|
||||
|
||||
def register(url_prefix, title):
|
||||
bp = Blueprint("market", __name__, url_prefix)
|
||||
|
||||
@bp.before_request
|
||||
def route():
|
||||
g.makeqs_factory = makeqs_factory
|
||||
|
||||
|
||||
@bp.context_processor
|
||||
async def inject_root():
|
||||
market = getattr(g, "market", None)
|
||||
market_id = market.id if market else None
|
||||
post_data = getattr(g, "post_data", None) or {}
|
||||
return {
|
||||
**post_data,
|
||||
"market_title": market.name if market else title,
|
||||
"categories": (await get_nav(g.s, market_id=market_id))["cats"],
|
||||
"qs": makeqs_factory()(),
|
||||
"market": market,
|
||||
}
|
||||
|
||||
bp.register_blueprint(
|
||||
register_browse_bp(),
|
||||
)
|
||||
bp.register_blueprint(
|
||||
products_api,
|
||||
)
|
||||
bp.register_blueprint(
|
||||
register_admin(),
|
||||
)
|
||||
|
||||
|
||||
|
||||
return bp
|
||||
|
||||
Reference in New Issue
Block a user