skrift 0.1.0a3__tar.gz → 0.1.0a5__tar.gz
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-0.1.0a3 → skrift-0.1.0a5}/PKG-INFO +1 -1
- {skrift-0.1.0a3 → skrift-0.1.0a5}/pyproject.toml +2 -3
- skrift-0.1.0a5/skrift/__main__.py +12 -0
- skrift-0.1.0a5/skrift/cli.py +143 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/config.py +2 -2
- skrift-0.1.0a3/skrift/__main__.py +0 -17
- skrift-0.1.0a3/skrift/cli.py +0 -54
- {skrift-0.1.0a3 → skrift-0.1.0a5}/.gitignore +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/README.md +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/admin/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/admin/controller.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/admin/navigation.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/env.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/script.py.mako +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/versions/20260120_210154_09b0364dbb7b_initial_schema.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/versions/20260122_152744_0b7c927d2591_add_roles_and_permissions.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/versions/20260122_172836_cdf734a5b847_add_sa_orm_sentinel_column.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/versions/20260122_175637_a9c55348eae7_remove_page_type_column.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/versions/20260122_200000_add_settings_table.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/versions/20260129_add_oauth_accounts.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic.ini +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/asgi.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/auth/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/auth/guards.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/auth/roles.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/auth/services.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/controllers/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/controllers/auth.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/controllers/web.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/base.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/models/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/models/oauth_account.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/models/page.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/models/role.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/models/setting.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/models/user.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/services/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/services/page_service.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/db/services/setting_service.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/lib/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/lib/exceptions.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/lib/template.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/setup/__init__.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/setup/config_writer.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/setup/controller.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/setup/middleware.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/setup/providers.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/setup/state.py +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/static/css/style.css +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/admin/admin.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/admin/base.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/admin/pages/edit.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/admin/pages/list.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/admin/settings/site.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/admin/users/list.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/admin/users/roles.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/auth/dummy_login.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/auth/login.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/base.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/error-404.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/error-500.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/error.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/index.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/page.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/admin.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/auth.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/base.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/complete.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/configuring.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/database.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/restart.html +0 -0
- {skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/templates/setup/site.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "skrift"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.0a5"
|
|
4
4
|
description = "A lightweight async Python CMS for crafting modern websites"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -20,8 +20,7 @@ dependencies = [
|
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
[project.scripts]
|
|
23
|
-
skrift = "skrift.
|
|
24
|
-
skrift-db = "skrift.cli:db"
|
|
23
|
+
skrift = "skrift.cli:cli"
|
|
25
24
|
|
|
26
25
|
[tool.uv]
|
|
27
26
|
package = true
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""CLI commands for Skrift."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import secrets
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group()
|
|
14
|
+
@click.version_option(package_name="skrift")
|
|
15
|
+
def cli():
|
|
16
|
+
"""Skrift - A lightweight async Python CMS."""
|
|
17
|
+
pass
|
|
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
|
|
114
|
+
"""
|
|
115
|
+
from alembic.config import main as alembic_main
|
|
116
|
+
|
|
117
|
+
# Always run from the project root (where app.yaml and .env are)
|
|
118
|
+
# This ensures database paths like ./app.db resolve correctly
|
|
119
|
+
project_root = Path.cwd()
|
|
120
|
+
if not (project_root / "app.yaml").exists():
|
|
121
|
+
# If not in project root, try parent directory
|
|
122
|
+
project_root = Path(__file__).parent.parent
|
|
123
|
+
os.chdir(project_root)
|
|
124
|
+
|
|
125
|
+
# Find alembic.ini - check project root first, then skrift package directory
|
|
126
|
+
alembic_ini = project_root / "alembic.ini"
|
|
127
|
+
if not alembic_ini.exists():
|
|
128
|
+
skrift_dir = Path(__file__).parent
|
|
129
|
+
alembic_ini = skrift_dir / "alembic.ini"
|
|
130
|
+
|
|
131
|
+
if not alembic_ini.exists():
|
|
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)
|
|
134
|
+
sys.exit(1)
|
|
135
|
+
|
|
136
|
+
# Build argv for alembic: ['-c', '/path/to/alembic.ini', ...extra_args]
|
|
137
|
+
alembic_argv = ["-c", str(alembic_ini)] + ctx.args
|
|
138
|
+
|
|
139
|
+
sys.exit(alembic_main(alembic_argv))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
cli()
|
|
@@ -9,8 +9,8 @@ from pydantic import BaseModel
|
|
|
9
9
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
10
10
|
|
|
11
11
|
# Load .env file early so env vars are available for YAML interpolation
|
|
12
|
-
#
|
|
13
|
-
_env_file = Path(
|
|
12
|
+
# Load from current working directory (where app.yaml lives)
|
|
13
|
+
_env_file = Path.cwd() / ".env"
|
|
14
14
|
load_dotenv(_env_file)
|
|
15
15
|
|
|
16
16
|
# Pattern to match $VAR_NAME environment variable references
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""Entry point for the skrift package."""
|
|
2
|
-
|
|
3
|
-
import uvicorn
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def main():
|
|
7
|
-
"""Run the Skrift development server."""
|
|
8
|
-
uvicorn.run(
|
|
9
|
-
"skrift.asgi:app",
|
|
10
|
-
host="0.0.0.0",
|
|
11
|
-
port=8080,
|
|
12
|
-
reload=True,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if __name__ == "__main__":
|
|
17
|
-
main()
|
skrift-0.1.0a3/skrift/cli.py
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"""CLI commands for Skrift database management."""
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def db() -> None:
|
|
8
|
-
"""Run Alembic database migrations.
|
|
9
|
-
|
|
10
|
-
This is a thin wrapper around Alembic that sets up the correct working
|
|
11
|
-
directory and passes through all arguments.
|
|
12
|
-
|
|
13
|
-
Usage:
|
|
14
|
-
skrift-db upgrade head # Apply all migrations
|
|
15
|
-
skrift-db downgrade -1 # Rollback one migration
|
|
16
|
-
skrift-db current # Show current revision
|
|
17
|
-
skrift-db history # Show migration history
|
|
18
|
-
skrift-db revision -m "description" --autogenerate # Create new migration
|
|
19
|
-
"""
|
|
20
|
-
from alembic.config import main as alembic_main
|
|
21
|
-
|
|
22
|
-
import os
|
|
23
|
-
|
|
24
|
-
# Always run from the project root (where app.yaml and .env are)
|
|
25
|
-
# This ensures database paths like ./app.db resolve correctly
|
|
26
|
-
project_root = Path.cwd()
|
|
27
|
-
if not (project_root / "app.yaml").exists():
|
|
28
|
-
# If not in project root, try parent directory
|
|
29
|
-
project_root = Path(__file__).parent.parent
|
|
30
|
-
os.chdir(project_root)
|
|
31
|
-
|
|
32
|
-
# Find alembic.ini - check project root first, then skrift package directory
|
|
33
|
-
alembic_ini = project_root / "alembic.ini"
|
|
34
|
-
if not alembic_ini.exists():
|
|
35
|
-
skrift_dir = Path(__file__).parent
|
|
36
|
-
alembic_ini = skrift_dir / "alembic.ini"
|
|
37
|
-
|
|
38
|
-
if not alembic_ini.exists():
|
|
39
|
-
print("Error: Could not find alembic.ini", file=sys.stderr)
|
|
40
|
-
print("Make sure you're running from the project root directory.", file=sys.stderr)
|
|
41
|
-
sys.exit(1)
|
|
42
|
-
|
|
43
|
-
# Build argv with config path at the beginning (before any subcommand)
|
|
44
|
-
# Original argv: ['skrift-db', 'upgrade', 'head']
|
|
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
|
|
48
|
-
|
|
49
|
-
# Pass through all CLI arguments to Alembic
|
|
50
|
-
sys.exit(alembic_main(sys.argv[1:]))
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if __name__ == "__main__":
|
|
54
|
-
db()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{skrift-0.1.0a3 → skrift-0.1.0a5}/skrift/alembic/versions/20260122_200000_add_settings_table.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|