create-caspian-app 1.6.4 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.github/copilot-instructions.md +271 -271
- package/dist/AGENTS.md +470 -461
- package/dist/app-gitignore +2 -0
- package/dist/index.js +2 -2
- package/dist/main.py +424 -64
- package/dist/public/js/pp-reactive-v2.min.js +1 -1
- package/dist/settings/build-static.py +32 -2
- package/dist/settings/check.py +520 -476
- package/dist/src/app/error.py +94 -95
- package/dist/src/app/layout.py +43 -47
- package/package.json +1 -1
|
@@ -46,6 +46,8 @@ sys.path.insert(0, str(ROOT))
|
|
|
46
46
|
# Keep the app in non-production mode: dev generates an ephemeral session secret,
|
|
47
47
|
# so the build does not require production secrets to be present.
|
|
48
48
|
os.environ.setdefault("APP_ENV", "development")
|
|
49
|
+
# Starting the app's lifespan for the export must not start the task scheduler.
|
|
50
|
+
os.environ["CASPIAN_STATIC_EXPORT"] = "1"
|
|
49
51
|
|
|
50
52
|
import httpx2 # noqa: E402 (the app's HTTP client; drives the ASGI app in-process)
|
|
51
53
|
|
|
@@ -96,8 +98,12 @@ def _fill_rule(fastapi_rule: str, params) -> str:
|
|
|
96
98
|
params = {names[0]: params} if names else {}
|
|
97
99
|
url = fastapi_rule
|
|
98
100
|
for key, value in params.items():
|
|
101
|
+
if value is None or value == "":
|
|
102
|
+
# An optional catch-all (`[[...slug]]`) exports its base URL.
|
|
103
|
+
url = url.replace(f"/{{{key}:path}}", "")
|
|
104
|
+
continue
|
|
99
105
|
url = url.replace(f"{{{key}:path}}", str(value)).replace(f"{{{key}}}", str(value))
|
|
100
|
-
return url
|
|
106
|
+
return url or "/"
|
|
101
107
|
|
|
102
108
|
|
|
103
109
|
def _out_path_for(url_path: str) -> Path:
|
|
@@ -242,6 +248,19 @@ def build() -> int:
|
|
|
242
248
|
|
|
243
249
|
await export_one(client, url)
|
|
244
250
|
|
|
251
|
+
# sitemap.py / robots.py -> static/sitemap.xml, static/robots.txt
|
|
252
|
+
for url in getattr(main, "SEO_ROUTES", []):
|
|
253
|
+
resp = await client.get(url)
|
|
254
|
+
if resp.status_code != 200:
|
|
255
|
+
skipped.append((url, f"returned HTTP {resp.status_code}"))
|
|
256
|
+
continue
|
|
257
|
+
out_file = OUT_DIR / url.lstrip("/")
|
|
258
|
+
out_file.write_text(resp.text, encoding="utf-8")
|
|
259
|
+
exported.append(url)
|
|
260
|
+
if not os.getenv("APP_BASE_URL"):
|
|
261
|
+
seo_warnings.append(url)
|
|
262
|
+
|
|
263
|
+
seo_warnings: list[str] = []
|
|
245
264
|
asyncio.run(render_all())
|
|
246
265
|
|
|
247
266
|
_copy_assets()
|
|
@@ -249,9 +268,20 @@ def build() -> int:
|
|
|
249
268
|
# ---- Report ----
|
|
250
269
|
print(f"\n{GREEN}Exported {len(exported)} page(s):{RESET}")
|
|
251
270
|
for url in exported:
|
|
252
|
-
|
|
271
|
+
target = (
|
|
272
|
+
OUT_DIR / url.lstrip("/")
|
|
273
|
+
if url in getattr(main, "SEO_ROUTES", [])
|
|
274
|
+
else _out_path_for(url)
|
|
275
|
+
)
|
|
276
|
+
rel = target.relative_to(ROOT)
|
|
253
277
|
print(f" {GREEN}OK{RESET} {url} -> {rel}")
|
|
254
278
|
|
|
279
|
+
if seo_warnings:
|
|
280
|
+
print(
|
|
281
|
+
f"\n{YELLOW}Note: APP_BASE_URL is not set{RESET}{DIM} -- {', '.join(seo_warnings)} "
|
|
282
|
+
"contain http://testserver URLs. Set APP_BASE_URL to the public origin and rebuild.{RESET}"
|
|
283
|
+
)
|
|
284
|
+
|
|
255
285
|
if rpc_warnings:
|
|
256
286
|
print(f"\n{YELLOW}Note: these pages appear to use pp.rpc()/server actions{RESET}")
|
|
257
287
|
print(f"{DIM} (they render, but those interactions are dead without the backend):{RESET}")
|