skrift 0.1.0a4__py3-none-any.whl → 0.1.0a6__py3-none-any.whl
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.
- skrift/__main__.py +3 -8
- skrift/asgi.py +13 -6
- skrift/cli.py +112 -23
- {skrift-0.1.0a4.dist-info → skrift-0.1.0a6.dist-info}/METADATA +1 -1
- {skrift-0.1.0a4.dist-info → skrift-0.1.0a6.dist-info}/RECORD +7 -7
- skrift-0.1.0a6.dist-info/entry_points.txt +2 -0
- skrift-0.1.0a4.dist-info/entry_points.txt +0 -3
- {skrift-0.1.0a4.dist-info → skrift-0.1.0a6.dist-info}/WHEEL +0 -0
skrift/__main__.py
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
"""Entry point for the skrift package."""
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from skrift.cli import cli
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def main():
|
|
7
|
-
"""Run the Skrift
|
|
8
|
-
|
|
9
|
-
"skrift.asgi:app",
|
|
10
|
-
host="0.0.0.0",
|
|
11
|
-
port=8080,
|
|
12
|
-
reload=True,
|
|
13
|
-
)
|
|
7
|
+
"""Run the Skrift CLI."""
|
|
8
|
+
cli()
|
|
14
9
|
|
|
15
10
|
|
|
16
11
|
if __name__ == "__main__":
|
skrift/asgi.py
CHANGED
|
@@ -10,6 +10,7 @@ The application uses a dispatcher architecture:
|
|
|
10
10
|
import asyncio
|
|
11
11
|
import hashlib
|
|
12
12
|
import importlib
|
|
13
|
+
import os
|
|
13
14
|
from datetime import datetime
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
|
|
@@ -309,9 +310,11 @@ def create_app() -> Litestar:
|
|
|
309
310
|
)
|
|
310
311
|
|
|
311
312
|
# Template configuration
|
|
313
|
+
# Search working directory first for user overrides, then package directory
|
|
314
|
+
working_dir_templates = Path(os.getcwd()) / "templates"
|
|
312
315
|
template_dir = Path(__file__).parent / "templates"
|
|
313
316
|
template_config = TemplateConfig(
|
|
314
|
-
directory=template_dir,
|
|
317
|
+
directory=[working_dir_templates, template_dir],
|
|
315
318
|
engine=JinjaTemplateEngine,
|
|
316
319
|
engine_callback=lambda engine: engine.engine.globals.update({
|
|
317
320
|
"now": datetime.now,
|
|
@@ -322,10 +325,11 @@ def create_app() -> Litestar:
|
|
|
322
325
|
}),
|
|
323
326
|
)
|
|
324
327
|
|
|
325
|
-
# Static files
|
|
328
|
+
# Static files - working directory first for user overrides, then package directory
|
|
329
|
+
working_dir_static = Path(os.getcwd()) / "static"
|
|
326
330
|
static_files_router = create_static_files_router(
|
|
327
331
|
path="/static",
|
|
328
|
-
directories=[Path(__file__).parent / "static"],
|
|
332
|
+
directories=[working_dir_static, Path(__file__).parent / "static"],
|
|
329
333
|
)
|
|
330
334
|
|
|
331
335
|
from skrift.auth import sync_roles_to_database
|
|
@@ -381,9 +385,11 @@ def create_setup_app() -> Litestar:
|
|
|
381
385
|
)
|
|
382
386
|
|
|
383
387
|
# Template configuration
|
|
388
|
+
# Search working directory first for user overrides, then package directory
|
|
389
|
+
working_dir_templates = Path(os.getcwd()) / "templates"
|
|
384
390
|
template_dir = Path(__file__).parent / "templates"
|
|
385
391
|
template_config = TemplateConfig(
|
|
386
|
-
directory=template_dir,
|
|
392
|
+
directory=[working_dir_templates, template_dir],
|
|
387
393
|
engine=JinjaTemplateEngine,
|
|
388
394
|
engine_callback=lambda engine: engine.engine.globals.update({
|
|
389
395
|
"now": datetime.now,
|
|
@@ -394,10 +400,11 @@ def create_setup_app() -> Litestar:
|
|
|
394
400
|
}),
|
|
395
401
|
)
|
|
396
402
|
|
|
397
|
-
# Static files
|
|
403
|
+
# Static files - working directory first for user overrides, then package directory
|
|
404
|
+
working_dir_static = Path(os.getcwd()) / "static"
|
|
398
405
|
static_files_router = create_static_files_router(
|
|
399
406
|
path="/static",
|
|
400
|
-
directories=[Path(__file__).parent / "static"],
|
|
407
|
+
directories=[working_dir_static, Path(__file__).parent / "static"],
|
|
401
408
|
)
|
|
402
409
|
|
|
403
410
|
# Import controllers
|
skrift/cli.py
CHANGED
|
@@ -1,26 +1,119 @@
|
|
|
1
|
-
"""CLI commands for Skrift
|
|
1
|
+
"""CLI commands for Skrift."""
|
|
2
2
|
|
|
3
|
+
import base64
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import secrets
|
|
3
7
|
import sys
|
|
4
8
|
from pathlib import Path
|
|
5
9
|
|
|
10
|
+
import click
|
|
6
11
|
|
|
7
|
-
def db() -> None:
|
|
8
|
-
"""Run Alembic database migrations.
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
@click.group()
|
|
14
|
+
@click.version_option(package_name="skrift")
|
|
15
|
+
def cli():
|
|
16
|
+
"""Skrift - A lightweight async Python CMS."""
|
|
17
|
+
pass
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
@cli.command()
|
|
21
|
+
@click.option("--host", default="127.0.0.1", help="Host to bind to")
|
|
22
|
+
@click.option("--port", default=8080, type=int, help="Port to bind to")
|
|
23
|
+
@click.option("--reload", is_flag=True, help="Enable auto-reload for development")
|
|
24
|
+
@click.option("--workers", default=1, type=int, help="Number of worker processes")
|
|
25
|
+
@click.option(
|
|
26
|
+
"--log-level",
|
|
27
|
+
default="info",
|
|
28
|
+
type=click.Choice(["debug", "info", "warning", "error"]),
|
|
29
|
+
help="Logging level",
|
|
30
|
+
)
|
|
31
|
+
def serve(host, port, reload, workers, log_level):
|
|
32
|
+
"""Run the Skrift server."""
|
|
33
|
+
import uvicorn
|
|
34
|
+
|
|
35
|
+
uvicorn.run(
|
|
36
|
+
"skrift.asgi:app",
|
|
37
|
+
host=host,
|
|
38
|
+
port=port,
|
|
39
|
+
reload=reload,
|
|
40
|
+
workers=workers if not reload else 1,
|
|
41
|
+
log_level=log_level,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@cli.command()
|
|
46
|
+
@click.option(
|
|
47
|
+
"--write",
|
|
48
|
+
type=click.Path(),
|
|
49
|
+
default=None,
|
|
50
|
+
help="Write SECRET_KEY to a .env file",
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"--format",
|
|
54
|
+
"fmt",
|
|
55
|
+
default="urlsafe",
|
|
56
|
+
type=click.Choice(["urlsafe", "hex", "base64"]),
|
|
57
|
+
help="Output format for the secret key",
|
|
58
|
+
)
|
|
59
|
+
@click.option("--length", default=32, type=int, help="Number of random bytes")
|
|
60
|
+
def secret(write, fmt, length):
|
|
61
|
+
"""Generate a secure secret key."""
|
|
62
|
+
# Generate key based on format
|
|
63
|
+
if fmt == "urlsafe":
|
|
64
|
+
key = secrets.token_urlsafe(length)
|
|
65
|
+
elif fmt == "hex":
|
|
66
|
+
key = secrets.token_hex(length)
|
|
67
|
+
else: # base64
|
|
68
|
+
key = base64.b64encode(secrets.token_bytes(length)).decode("ascii")
|
|
69
|
+
|
|
70
|
+
if write:
|
|
71
|
+
env_path = Path(write)
|
|
72
|
+
env_content = ""
|
|
73
|
+
|
|
74
|
+
# Read existing content if file exists
|
|
75
|
+
if env_path.exists():
|
|
76
|
+
env_content = env_path.read_text()
|
|
77
|
+
|
|
78
|
+
# Update or add SECRET_KEY
|
|
79
|
+
secret_key_pattern = re.compile(r"^SECRET_KEY=.*$", re.MULTILINE)
|
|
80
|
+
new_line = f"SECRET_KEY={key}"
|
|
81
|
+
|
|
82
|
+
if secret_key_pattern.search(env_content):
|
|
83
|
+
# Replace existing SECRET_KEY
|
|
84
|
+
env_content = secret_key_pattern.sub(new_line, env_content)
|
|
85
|
+
else:
|
|
86
|
+
# Add SECRET_KEY at the end
|
|
87
|
+
if env_content and not env_content.endswith("\n"):
|
|
88
|
+
env_content += "\n"
|
|
89
|
+
env_content += new_line + "\n"
|
|
90
|
+
|
|
91
|
+
env_path.write_text(env_content)
|
|
92
|
+
click.echo(f"SECRET_KEY written to {env_path}")
|
|
93
|
+
else:
|
|
94
|
+
click.echo(key)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@cli.command(
|
|
98
|
+
context_settings=dict(
|
|
99
|
+
ignore_unknown_options=True,
|
|
100
|
+
allow_extra_args=True,
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
@click.pass_context
|
|
104
|
+
def db(ctx):
|
|
105
|
+
"""Run database migrations via Alembic.
|
|
106
|
+
|
|
107
|
+
\b
|
|
108
|
+
Examples:
|
|
109
|
+
skrift db upgrade head # Apply all migrations
|
|
110
|
+
skrift db downgrade -1 # Rollback one migration
|
|
111
|
+
skrift db current # Show current revision
|
|
112
|
+
skrift db history # Show migration history
|
|
113
|
+
skrift db revision -m "description" --autogenerate # Create new migration
|
|
19
114
|
"""
|
|
20
115
|
from alembic.config import main as alembic_main
|
|
21
116
|
|
|
22
|
-
import os
|
|
23
|
-
|
|
24
117
|
# Always run from the project root (where app.yaml and .env are)
|
|
25
118
|
# This ensures database paths like ./app.db resolve correctly
|
|
26
119
|
project_root = Path.cwd()
|
|
@@ -36,19 +129,15 @@ def db() -> None:
|
|
|
36
129
|
alembic_ini = skrift_dir / "alembic.ini"
|
|
37
130
|
|
|
38
131
|
if not alembic_ini.exists():
|
|
39
|
-
|
|
40
|
-
|
|
132
|
+
click.echo("Error: Could not find alembic.ini", err=True)
|
|
133
|
+
click.echo("Make sure you're running from the project root directory.", err=True)
|
|
41
134
|
sys.exit(1)
|
|
42
135
|
|
|
43
|
-
# Build argv
|
|
44
|
-
|
|
45
|
-
# New argv: ['skrift-db', '-c', '/path/to/alembic.ini', 'upgrade', 'head']
|
|
46
|
-
new_argv = [sys.argv[0], "-c", str(alembic_ini)] + sys.argv[1:]
|
|
47
|
-
sys.argv = new_argv
|
|
136
|
+
# Build argv for alembic: ['-c', '/path/to/alembic.ini', ...extra_args]
|
|
137
|
+
alembic_argv = ["-c", str(alembic_ini)] + ctx.args
|
|
48
138
|
|
|
49
|
-
|
|
50
|
-
sys.exit(alembic_main(sys.argv[1:]))
|
|
139
|
+
sys.exit(alembic_main(alembic_argv))
|
|
51
140
|
|
|
52
141
|
|
|
53
142
|
if __name__ == "__main__":
|
|
54
|
-
|
|
143
|
+
cli()
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
skrift/__init__.py,sha256=eXE5PFVkJpH5XsV_ZlrTIeFPUPrmcHYAj4GpRS3R5PY,29
|
|
2
|
-
skrift/__main__.py,sha256=
|
|
2
|
+
skrift/__main__.py,sha256=wt6JZL9nBhKU36vdyurhOEtWy7w3C9zohyy24PLcKho,164
|
|
3
3
|
skrift/alembic.ini,sha256=mYguI6CbMCTyfHctsGiTyf9Z5gv21FdeI3qtfgOHO3A,1815
|
|
4
|
-
skrift/asgi.py,sha256=
|
|
5
|
-
skrift/cli.py,sha256=
|
|
4
|
+
skrift/asgi.py,sha256=PQRC1tyPT4iYNbrTusvLlLQ9rbsDp8w8V2scsNWM7qY,19874
|
|
5
|
+
skrift/cli.py,sha256=aT-7pXvOuuZC-Eypx1h-xCiqaBKhIFjSqd5Ky_dHna0,4214
|
|
6
6
|
skrift/config.py,sha256=_fXfdRdy03yJWUhijthC4cNgOXLJ0jhB__C-VwVYaxc,7398
|
|
7
7
|
skrift/admin/__init__.py,sha256=x81Cj_ilVmv6slaMl16HHyT_AgrnLxKEWkS0RPa4V9s,289
|
|
8
8
|
skrift/admin/controller.py,sha256=5ZDypvKHXLNDESsKNsdsH2E3Si5OqlpzttFl7Ot8aF0,15651
|
|
@@ -66,7 +66,7 @@ skrift/templates/setup/configuring.html,sha256=2KHW9h2BrJgL_kO5IizbAYs4pnFLyRf76
|
|
|
66
66
|
skrift/templates/setup/database.html,sha256=gU4-315-QraHa2Eq4Fh3b55QpOM2CkJzh27_Yz13frA,5495
|
|
67
67
|
skrift/templates/setup/restart.html,sha256=GHg31F_e2uLFhWUzJoalk0Y0oYLqsFWyZXWKX3mblbY,1355
|
|
68
68
|
skrift/templates/setup/site.html,sha256=PSOH-q1-ZBl47iSW9-Ad6lEfJn_fzdGD3Pk4vb3xgK4,1680
|
|
69
|
-
skrift-0.1.
|
|
70
|
-
skrift-0.1.
|
|
71
|
-
skrift-0.1.
|
|
72
|
-
skrift-0.1.
|
|
69
|
+
skrift-0.1.0a6.dist-info/METADATA,sha256=TAXajlBybDa-iLbvVsiERUoP9xrjDj636dNpE2auDto,6435
|
|
70
|
+
skrift-0.1.0a6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
71
|
+
skrift-0.1.0a6.dist-info/entry_points.txt,sha256=uquZ5Mumqr0xwYTpTcNiJtFSITGfF6_QCCy2DZJSZig,42
|
|
72
|
+
skrift-0.1.0a6.dist-info/RECORD,,
|
|
File without changes
|