skrift 0.1.0a1__py3-none-any.whl → 0.1.0a3__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/setup/state.py CHANGED
@@ -3,16 +3,36 @@
3
3
  This module implements a two-tier detection strategy:
4
4
  1. Pre-database check: Can we connect to a database?
5
5
  2. Post-database check: Is setup complete (check for setup_completed_at setting)?
6
+
7
+ Smart step detection: If config is already present, skip to the first incomplete step.
6
8
  """
7
9
 
8
10
  import os
11
+ import subprocess
9
12
  from enum import Enum
10
13
  from pathlib import Path
11
-
14
+ import yaml
12
15
  from sqlalchemy import text
13
16
  from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
14
17
 
15
- from skrift.db.services.setting_service import SETUP_COMPLETED_AT_KEY, get_setting
18
+ # Track if migrations have been run this session to avoid running multiple times
19
+ _migrations_run = False
20
+
21
+
22
+ def reset_migrations_flag() -> None:
23
+ """Reset the migrations flag to allow re-running migrations.
24
+
25
+ Call this when starting the configuring page to ensure migrations run fresh.
26
+ """
27
+ global _migrations_run
28
+ _migrations_run = False
29
+
30
+ from skrift.config import get_config_path
31
+ from skrift.db.services.setting_service import (
32
+ SETUP_COMPLETED_AT_KEY,
33
+ SITE_NAME_KEY,
34
+ get_setting,
35
+ )
16
36
 
17
37
 
18
38
  class SetupStep(Enum):
@@ -27,7 +47,7 @@ class SetupStep(Enum):
27
47
 
28
48
  def app_yaml_exists() -> bool:
29
49
  """Check if app.yaml exists in the current working directory."""
30
- return (Path.cwd() / "app.yaml").exists()
50
+ return get_config_path().exists()
31
51
 
32
52
 
33
53
  def get_database_url_from_yaml() -> str | None:
@@ -38,7 +58,7 @@ def get_database_url_from_yaml() -> str | None:
38
58
  """
39
59
  import yaml
40
60
 
41
- config_path = Path.cwd() / "app.yaml"
61
+ config_path = get_config_path()
42
62
  if not config_path.exists():
43
63
  return None
44
64
 
@@ -102,6 +122,167 @@ async def is_setup_complete(db_session: AsyncSession) -> bool:
102
122
  return False
103
123
 
104
124
 
125
+ def is_auth_configured() -> bool:
126
+ """Check if at least one OAuth provider is fully configured in app.yaml.
127
+
128
+ A provider is considered configured if it has both client_id and client_secret.
129
+
130
+ Returns:
131
+ True if at least one provider is configured, False otherwise.
132
+ """
133
+ config_path = get_config_path()
134
+ if not config_path.exists():
135
+ return False
136
+
137
+ try:
138
+ with open(config_path, "r") as f:
139
+ config = yaml.safe_load(f)
140
+
141
+ if not config:
142
+ return False
143
+
144
+ auth = config.get("auth", {})
145
+ providers = auth.get("providers", {})
146
+
147
+ for _, provider_config in providers.items():
148
+ if not isinstance(provider_config, dict):
149
+ continue
150
+ # Check if provider has both client_id and client_secret (even as env var refs)
151
+ client_id = provider_config.get("client_id", "")
152
+ client_secret = provider_config.get("client_secret", "")
153
+ if client_id and client_secret:
154
+ return True
155
+
156
+ return False
157
+ except Exception:
158
+ return False
159
+
160
+
161
+ def run_migrations_if_needed() -> tuple[bool, str | None]:
162
+ """Run database migrations if they haven't been run this session.
163
+
164
+ This ensures the database schema is up to date before checking for
165
+ settings or other database-dependent configuration.
166
+
167
+ Returns:
168
+ Tuple of (success, error_message)
169
+ """
170
+ global _migrations_run
171
+ if _migrations_run:
172
+ return True, None
173
+
174
+ try:
175
+ # Try skrift-db first
176
+ result = subprocess.run(
177
+ ["skrift-db", "upgrade", "head"],
178
+ capture_output=True,
179
+ text=True,
180
+ cwd=Path.cwd(),
181
+ timeout=60,
182
+ )
183
+ if result.returncode == 0:
184
+ _migrations_run = True
185
+ return True, None
186
+ # If skrift-db fails, try alembic directly
187
+ except (subprocess.TimeoutExpired, FileNotFoundError):
188
+ pass
189
+
190
+ try:
191
+ result = subprocess.run(
192
+ ["alembic", "upgrade", "head"],
193
+ capture_output=True,
194
+ text=True,
195
+ cwd=Path.cwd(),
196
+ timeout=60,
197
+ )
198
+ if result.returncode == 0:
199
+ _migrations_run = True
200
+ return True, None
201
+ return False, result.stderr
202
+ except subprocess.TimeoutExpired:
203
+ return False, "Migration timed out"
204
+ except FileNotFoundError:
205
+ return False, "Neither skrift-db nor alembic found"
206
+ except Exception as e:
207
+ return False, str(e)
208
+
209
+
210
+ async def is_site_configured() -> bool:
211
+ """Check if site settings have been configured in the database.
212
+
213
+ The site step is considered complete if site_name has been set.
214
+ Returns False if the settings table doesn't exist yet (pre-migration).
215
+
216
+ Returns:
217
+ True if site is configured, False otherwise.
218
+ """
219
+ db_url = get_database_url_from_yaml()
220
+ if not db_url:
221
+ return False
222
+
223
+ engine = None
224
+ try:
225
+ engine = create_async_engine(db_url)
226
+ from sqlalchemy.ext.asyncio import async_sessionmaker
227
+
228
+ async_session = async_sessionmaker(engine, expire_on_commit=False)
229
+ async with async_session() as session:
230
+ try:
231
+ site_name = await get_setting(session, SITE_NAME_KEY)
232
+ return site_name is not None
233
+ except Exception:
234
+ # Table might not exist yet (before migration)
235
+ return False
236
+ except Exception:
237
+ return False
238
+ finally:
239
+ if engine:
240
+ await engine.dispose()
241
+
242
+
243
+ async def get_first_incomplete_step() -> SetupStep:
244
+ """Determine the first incomplete step in the setup wizard.
245
+
246
+ This function checks configuration completeness for each step and returns
247
+ the first step that needs to be completed. Use this to skip already-configured
248
+ steps when the user is forced back into the setup wizard.
249
+
250
+ If database is configured and connectable, runs migrations to ensure
251
+ all tables exist before checking database-dependent configuration.
252
+
253
+ Returns:
254
+ The first setup step that needs user input.
255
+ """
256
+ # Step 1: Database - check if we can connect
257
+ if not app_yaml_exists():
258
+ return SetupStep.DATABASE
259
+
260
+ db_url = get_database_url_from_yaml()
261
+ if not db_url:
262
+ return SetupStep.DATABASE
263
+
264
+ can_connect, _ = await can_connect_to_database()
265
+ if not can_connect:
266
+ return SetupStep.DATABASE
267
+
268
+ # Database is configured and connectable - run migrations to ensure tables exist
269
+ migration_success, _ = run_migrations_if_needed()
270
+ if not migration_success:
271
+ # If migrations fail, go back to database step to show the error
272
+ return SetupStep.DATABASE
273
+
274
+ # Step 2: Auth - check if at least one provider is configured
275
+ if not is_auth_configured():
276
+ return SetupStep.AUTH
277
+
278
+ # Step 3: Site - check if site settings exist in DB
279
+ if not await is_site_configured():
280
+ return SetupStep.SITE
281
+
282
+ # Step 4: Admin - always go here if setup not complete
283
+ return SetupStep.ADMIN
284
+
285
+
105
286
  async def get_setup_step(db_session: AsyncSession | None = None) -> SetupStep:
106
287
  """Determine which setup step the user should be on.
107
288
 
@@ -175,7 +175,7 @@ a {
175
175
  position: relative;
176
176
  }
177
177
 
178
- a:not([role="button"])::after {
178
+ a:not([role="button"]):not(.oauth-btn)::after {
179
179
  content: '';
180
180
  position: absolute;
181
181
  left: 0;
@@ -188,11 +188,11 @@ a:not([role="button"])::after {
188
188
  transition: transform 500ms ease-out;
189
189
  }
190
190
 
191
- a:hover {
191
+ a:not([role="button"]):not(.oauth-btn):hover {
192
192
  color: var(--color-primary-hover);
193
193
  }
194
194
 
195
- a:not([role="button"]):hover::after {
195
+ a:not([role="button"]):not(.oauth-btn):hover::after {
196
196
  transform: scaleX(1);
197
197
  transform-origin: left;
198
198
  }
@@ -0,0 +1,102 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Dummy Login (Dev) - {{ site_name() }}{% endblock %}
4
+
5
+ {% block head %}
6
+ <style>
7
+ .login-container {
8
+ max-width: 400px;
9
+ margin: 2rem auto;
10
+ }
11
+
12
+ .warning-banner {
13
+ background: #fef3c7;
14
+ border: 1px solid #f59e0b;
15
+ color: #92400e;
16
+ padding: 1rem;
17
+ border-radius: 0.5rem;
18
+ margin-bottom: 1.5rem;
19
+ text-align: center;
20
+ font-weight: 600;
21
+ }
22
+
23
+ .form-group {
24
+ margin-bottom: 1rem;
25
+ }
26
+
27
+ .form-group label {
28
+ display: block;
29
+ margin-bottom: 0.5rem;
30
+ font-weight: 600;
31
+ }
32
+
33
+ .form-group input {
34
+ width: 100%;
35
+ padding: 0.75rem;
36
+ border: 1px solid var(--color-border, #ccc);
37
+ border-radius: 0.5rem;
38
+ font-size: 1rem;
39
+ }
40
+
41
+ .form-group small {
42
+ display: block;
43
+ margin-top: 0.25rem;
44
+ color: var(--color-text-muted, #666);
45
+ }
46
+
47
+ .submit-btn {
48
+ width: 100%;
49
+ padding: 1rem;
50
+ background: #6b7280;
51
+ color: white;
52
+ border: none;
53
+ border-radius: 0.5rem;
54
+ font-size: 1rem;
55
+ font-weight: 600;
56
+ cursor: pointer;
57
+ margin-top: 1rem;
58
+ }
59
+
60
+ .submit-btn:hover {
61
+ background: #4b5563;
62
+ }
63
+
64
+ .back-link {
65
+ display: block;
66
+ text-align: center;
67
+ margin-top: 1.5rem;
68
+ color: var(--color-text-muted, #666);
69
+ }
70
+ </style>
71
+ {% endblock %}
72
+
73
+ {% block content %}
74
+ <div class="login-container">
75
+ <article>
76
+ <header>
77
+ <h1>Dummy Login</h1>
78
+ </header>
79
+
80
+ <div class="warning-banner">
81
+ DEVELOPMENT ONLY - Not for production use
82
+ </div>
83
+
84
+ <form method="post" action="/auth/dummy-login">
85
+ <div class="form-group">
86
+ <label for="email">Email</label>
87
+ <input type="email" id="email" name="email" required placeholder="test@example.com">
88
+ </div>
89
+
90
+ <div class="form-group">
91
+ <label for="name">Name</label>
92
+ <input type="text" id="name" name="name" placeholder="Test User">
93
+ <small>Optional. Defaults to email username if not provided.</small>
94
+ </div>
95
+
96
+ <button type="submit" class="submit-btn">Login</button>
97
+ </form>
98
+
99
+ <a href="/auth/login" class="back-link">&larr; Back to login options</a>
100
+ </article>
101
+ </div>
102
+ {% endblock %}
@@ -120,6 +120,20 @@
120
120
  <p>No authentication providers are configured. Please contact the site administrator.</p>
121
121
  {% endfor %}
122
122
  </div>
123
+
124
+ {% if has_dummy %}
125
+ <div style="margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--color-border, #ccc);">
126
+ <p style="text-align: center; color: var(--color-text-muted, #666); font-size: 0.875rem; margin-bottom: 1rem;">
127
+ Development Only
128
+ </p>
129
+ <a href="/auth/dummy/login" class="oauth-btn" style="background: #6b7280; color: white;">
130
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
131
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/>
132
+ </svg>
133
+ Dummy Login (Dev)
134
+ </a>
135
+ </div>
136
+ {% endif %}
123
137
  </article>
124
138
  </div>
125
139
  {% endblock %}
@@ -0,0 +1,158 @@
1
+ {% extends "setup/base.html" %}
2
+
3
+ {% block title %}Configuring Database - Skrift{% endblock %}
4
+
5
+ {% block head %}
6
+ <style>
7
+ .configuring-content {
8
+ text-align: center;
9
+ padding: 2rem 0;
10
+ }
11
+
12
+ .spinner {
13
+ width: 48px;
14
+ height: 48px;
15
+ border: 4px solid var(--color-border);
16
+ border-top-color: var(--color-primary);
17
+ border-radius: 50%;
18
+ animation: spin 1s linear infinite;
19
+ margin: 0 auto 1.5rem;
20
+ }
21
+
22
+ .spinner.complete {
23
+ border-color: var(--color-success);
24
+ border-top-color: var(--color-success);
25
+ animation: none;
26
+ }
27
+
28
+ .spinner.error {
29
+ border-color: var(--color-error);
30
+ border-top-color: var(--color-error);
31
+ animation: none;
32
+ }
33
+
34
+ @keyframes spin {
35
+ to { transform: rotate(360deg); }
36
+ }
37
+
38
+ .status-icon {
39
+ width: 48px;
40
+ height: 48px;
41
+ margin: 0 auto 1.5rem;
42
+ font-size: 48px;
43
+ line-height: 1;
44
+ display: none;
45
+ }
46
+
47
+ .status-icon.complete {
48
+ display: block;
49
+ color: var(--color-success);
50
+ }
51
+
52
+ .status-icon.error {
53
+ display: block;
54
+ color: var(--color-error);
55
+ }
56
+
57
+ .status-message {
58
+ font-size: 1.125rem;
59
+ margin-bottom: 0.5rem;
60
+ }
61
+
62
+ .status-detail {
63
+ color: var(--color-text-muted);
64
+ font-size: 0.875rem;
65
+ min-height: 1.25rem;
66
+ }
67
+
68
+ .form-actions button:disabled {
69
+ opacity: 0.5;
70
+ cursor: not-allowed;
71
+ }
72
+ </style>
73
+ {% endblock %}
74
+
75
+ {% block content %}
76
+ <h2>Configuring Database</h2>
77
+ <p class="description">Setting up your database and running migrations.</p>
78
+
79
+ <div class="configuring-content">
80
+ <div class="spinner" id="spinner"></div>
81
+ <div class="status-icon" id="status-icon"></div>
82
+ <div class="status-message" id="status-message">Initializing...</div>
83
+ <div class="status-detail" id="status-detail"></div>
84
+ </div>
85
+
86
+ <div class="form-actions">
87
+ <a href="/setup/database" role="button" class="btn-secondary">Back</a>
88
+ <button type="button" id="next-button" disabled>Continue</button>
89
+ </div>
90
+ {% endblock %}
91
+
92
+ {% block scripts %}
93
+ <script>
94
+ document.addEventListener('DOMContentLoaded', function() {
95
+ const spinner = document.getElementById('spinner');
96
+ const statusIcon = document.getElementById('status-icon');
97
+ const statusMessage = document.getElementById('status-message');
98
+ const statusDetail = document.getElementById('status-detail');
99
+ const nextButton = document.getElementById('next-button');
100
+
101
+ let nextStep = 'auth'; // Default fallback
102
+
103
+ function setComplete(step) {
104
+ spinner.style.display = 'none';
105
+ statusIcon.textContent = '\u2713';
106
+ statusIcon.classList.add('complete');
107
+ statusMessage.textContent = 'Database configured successfully!';
108
+ statusDetail.textContent = '';
109
+ nextButton.disabled = false;
110
+ if (step) {
111
+ nextStep = step;
112
+ }
113
+ nextButton.onclick = function() {
114
+ window.location.href = '/setup/' + nextStep;
115
+ };
116
+ }
117
+
118
+ function setError(message) {
119
+ spinner.style.display = 'none';
120
+ statusIcon.textContent = '\u2717';
121
+ statusIcon.classList.add('error');
122
+ statusMessage.textContent = 'Configuration failed';
123
+ statusDetail.textContent = message || 'An error occurred during setup.';
124
+ }
125
+
126
+ function setStatus(message, detail) {
127
+ statusMessage.textContent = message;
128
+ statusDetail.textContent = detail || '';
129
+ }
130
+
131
+ // Connect to SSE endpoint
132
+ const eventSource = new EventSource('/setup/configuring/status');
133
+
134
+ eventSource.onmessage = function(event) {
135
+ const data = JSON.parse(event.data);
136
+
137
+ if (data.status === 'running') {
138
+ setStatus(data.message, data.detail);
139
+ } else if (data.status === 'complete') {
140
+ setComplete(data.next_step);
141
+ eventSource.close();
142
+ } else if (data.status === 'error') {
143
+ setError(data.message);
144
+ eventSource.close();
145
+ }
146
+ };
147
+
148
+ eventSource.onerror = function() {
149
+ // Check if we were successful before the connection closed
150
+ if (!nextButton.disabled) {
151
+ return; // Already complete, ignore the error
152
+ }
153
+ setError('Connection lost. Please refresh the page.');
154
+ eventSource.close();
155
+ };
156
+ });
157
+ </script>
158
+ {% endblock %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skrift
3
- Version: 0.1.0a1
3
+ Version: 0.1.0a3
4
4
  Summary: A lightweight async Python CMS for crafting modern websites
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: advanced-alchemy>=0.26.0
@@ -15,6 +15,8 @@ Requires-Dist: pyyaml>=6.0.0
15
15
  Requires-Dist: ruamel-yaml>=0.18.0
16
16
  Requires-Dist: sqlalchemy[asyncio]>=2.0.36
17
17
  Requires-Dist: uvicorn>=0.34.0
18
+ Provides-Extra: docs
19
+ Requires-Dist: zensical>=0.0.19; extra == 'docs'
18
20
  Description-Content-Type: text/markdown
19
21
 
20
22
  # Skrift
@@ -1,33 +1,35 @@
1
1
  skrift/__init__.py,sha256=eXE5PFVkJpH5XsV_ZlrTIeFPUPrmcHYAj4GpRS3R5PY,29
2
2
  skrift/__main__.py,sha256=Fs17xxkqTjZpJn9MMC6pzkW4sOzNhV5RGLsA2ONPFww,271
3
- skrift/alembic.ini,sha256=aq1sNDUgKvdON-N28lN9fncRcYtg430HE2DsuEMIDtk,1782
4
- skrift/asgi.py,sha256=62fWbHMwAqUBveboWK-bwmplREvXXBlBna8-0kAcKOI,18836
5
- skrift/cli.py,sha256=rrm0cSc2LhQ1FA5-ucDN1sDTwFwKnDk1DCYf746SYyo,1522
6
- skrift/config.py,sha256=fwB8oGKFmSkGEHqjM5Z9dJvcztjx8WSUqyCkvQXfIBo,5640
3
+ skrift/alembic.ini,sha256=mYguI6CbMCTyfHctsGiTyf9Z5gv21FdeI3qtfgOHO3A,1815
4
+ skrift/asgi.py,sha256=U-9p_JlWlwjrhKJzAlBHpQ3DvTB2uIcvZe3-5Gs87Ec,19248
5
+ skrift/cli.py,sha256=DB_MssHeBAvpm7DVgd3V-BQr5BjBqSO0O_0vm15PZJ8,1941
6
+ skrift/config.py,sha256=7Jqqm9jXff7rIRY83C4jchDVk_1UkpGdKATX3JgX974,7423
7
7
  skrift/admin/__init__.py,sha256=x81Cj_ilVmv6slaMl16HHyT_AgrnLxKEWkS0RPa4V9s,289
8
8
  skrift/admin/controller.py,sha256=5ZDypvKHXLNDESsKNsdsH2E3Si5OqlpzttFl7Ot8aF0,15651
9
9
  skrift/admin/navigation.py,sha256=VwttFoIUIJy5rONKIkJd5w4CNkUpeK22_OfLGHecN34,3382
10
- skrift/alembic/env.py,sha256=vYy8dBm7zW8F5izsQdVgQyJ6yGmRbqUfGoWgefD49vQ,2609
10
+ skrift/alembic/env.py,sha256=GaQx7D-3f0zVTV4YJNhN0GOfqHL99N4VBfMzjZJz0Bc,2673
11
11
  skrift/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
12
12
  skrift/alembic/versions/20260120_210154_09b0364dbb7b_initial_schema.py,sha256=X6w1vbVKFurhEcblTLGO4Nd_IKkMVtb8TsptV4gpAJ4,2750
13
13
  skrift/alembic/versions/20260122_152744_0b7c927d2591_add_roles_and_permissions.py,sha256=yCGjnMTpfSMHVYQCsPTD6wdDI8pJSUtGRESO6bz0KfU,2672
14
14
  skrift/alembic/versions/20260122_172836_cdf734a5b847_add_sa_orm_sentinel_column.py,sha256=96TCvvQbYk3RGpZdEQ0x4bIDXxBLc3Gv7VTItTq9T54,850
15
15
  skrift/alembic/versions/20260122_175637_a9c55348eae7_remove_page_type_column.py,sha256=XJ55LhwzgN07MAREk6w4Bx2Vu3KvMe_bDkIjgE6EfqM,1223
16
16
  skrift/alembic/versions/20260122_200000_add_settings_table.py,sha256=aG4pxp18fwonGAWt7EoT0XaiIEmu7zltSrj8e2a8QZU,1310
17
+ skrift/alembic/versions/20260129_add_oauth_accounts.py,sha256=SJYQl0cfyJnUKLzsbGqu97Kcm7waPhxmpyARJXB3Z1Q,5240
17
18
  skrift/auth/__init__.py,sha256=uHMqty3dgDSYlReVT96WhygzH6qNAWSVDWoxumzxmsA,1155
18
19
  skrift/auth/guards.py,sha256=QePajHsGnJ4R_hlhzblr5IoAgZcY5jzeZ64bJwDL9hM,4451
19
20
  skrift/auth/roles.py,sha256=jpqK4qaavqAhJRxhptm2x5mUb6KkwEALaml8sEH4Sug,2267
20
21
  skrift/auth/services.py,sha256=h6GTXdN5UMRYglnaFz4asMoutVkSSAyL3_Vt56N26pA,5441
21
22
  skrift/controllers/__init__.py,sha256=bVr0tsSGz7jBi002Lqd1AA1FQd7ZA_IagsqTKpiHiK0,147
22
- skrift/controllers/auth.py,sha256=fqn-E2YCiMKJU1lh2fzUwVdx9cHfrFiBIi-cdBkjwN0,13282
23
+ skrift/controllers/auth.py,sha256=2XQSljDnqdk1NZZTfYLBAqfZb4Ref2p3Uy2maUkKJgY,18881
23
24
  skrift/controllers/web.py,sha256=vmoBS1u5G9gCBu65S49yqZn_WBKlmsqlcvX5tYXTKnE,2348
24
25
  skrift/db/__init__.py,sha256=uSghyDFT2K4SFiEqUzdjCGzWpS-Oy6Sd1FUappau-v0,52
25
26
  skrift/db/base.py,sha256=QJplFj9235kZdScASEpvyNHln6YW2hqbHwJEYZ3OSsc,173
26
- skrift/db/models/__init__.py,sha256=8ivPDjVMDZPvl06ZWBjp4YmSfC4qhbq76PDJ8MWqzv4,269
27
+ skrift/db/models/__init__.py,sha256=wFF9YWe7rhIWIzRuMtAWVDcZdJsNcKnjfhKbLZw9JVs,341
28
+ skrift/db/models/oauth_account.py,sha256=kLb-1Do8a7tluW3f-uhA_BpgkZ9h10kg_8v0jInDrwY,1216
27
29
  skrift/db/models/page.py,sha256=CI5W2sWq0zbKHxfwH5TpmGdJ4zlTicckzpdZtaqjJVE,1019
28
30
  skrift/db/models/role.py,sha256=VkwkF3XWemmFtkUpQRk6RTIBfcukrU-0PUPCeo8x834,1768
29
31
  skrift/db/models/setting.py,sha256=Am4HTyq2LFR408R6BZb--slys1xM8YLYq9HTq_gybD0,392
30
- skrift/db/models/user.py,sha256=zqZ-sYcYa0StFsSGsPcovy7EpswFo_PtrObBJ8TyHiA,1310
32
+ skrift/db/models/user.py,sha256=kkR3h0CphUyvOGJOpiZH6Qp28LqW2OriwVTfybs7vuY,1338
31
33
  skrift/db/services/__init__.py,sha256=qAC24IPOYg6AampUWonmLeajljQRMwUw0lgXksQG6Nk,69
32
34
  skrift/db/services/page_service.py,sha256=cXJ-urV7LjUDoVsT1P52f6wcOsur3T-pvqKkGG4xlvI,5246
33
35
  skrift/db/services/setting_service.py,sha256=eqFxukn8QFrDHQbaBILOLjP1nr34JPIPFT_NnhmeaTY,5613
@@ -35,12 +37,12 @@ skrift/lib/__init__.py,sha256=PL66bS4_-90zJDMsQSnzqkkxwkO_3aLcYl9l9cDrE2Y,65
35
37
  skrift/lib/exceptions.py,sha256=p8ceLIQCc7agCwW6-mhBDAuMAMxZDcf9TDLC6PfztU4,5803
36
38
  skrift/lib/template.py,sha256=4_urkRfvth75yNeQ5TyGTHvkvs3vVef7TcwZx0k285k,4226
37
39
  skrift/setup/__init__.py,sha256=3VjFPMES5y0M5cQ9R4C1xazqiEPEDqTPjX9-3rBMXnA,478
38
- skrift/setup/config_writer.py,sha256=FkAfS4xa_R0tmgdp2Ac-0QlVXte2xUo1TWpxWBGBkUA,6260
39
- skrift/setup/controller.py,sha256=HZs6Q2lNdvNh-JakmznMVL-1sj_6wD9axr3ESL1iIaA,29202
40
+ skrift/setup/config_writer.py,sha256=YFH3FVjXN7Rum2fzGVPAQRkjdc9b0bHECDqMKYiEkhg,6347
41
+ skrift/setup/controller.py,sha256=v0Ey8T7ptJ5A3vOqQ1TUAXH1bQwA0288J5uyUWMihsw,34250
40
42
  skrift/setup/middleware.py,sha256=Nai8ZG2vHldngmAhq7kWzAwKRNcP5tHKhJHa5dCh404,2941
41
- skrift/setup/providers.py,sha256=XQQ7q_4axkTkspHyN66wn-zVaCzGZCf4BClEhNbEnWU,6171
42
- skrift/setup/state.py,sha256=JRFh6ZSbznLtTwyPBATR75heHdG4uuk8JcCFHawTrPI,3711
43
- skrift/static/css/style.css,sha256=4874DhxJD9oD81PjvJGwiQwdVjUDKWAZ2V8Gq62Igew,20755
43
+ skrift/setup/providers.py,sha256=0BFKB6168NcmtXxFF6ofHgEDMQD2FbXkexsqrARVtDI,7967
44
+ skrift/setup/state.py,sha256=RMe9LtIjzDoOm9u-Nk5-KAnr_JBiQIjWDpTP9E30ezc,9304
45
+ skrift/static/css/style.css,sha256=sJ7-y8nrUdB5EB5_CyjWo1CTnqmYq0UgMZYMjxw0988,20824
44
46
  skrift/templates/base.html,sha256=4bg4s4VdES0dSvhJYLgrfrN26ynqeq1-3jyKPkWWVWk,2065
45
47
  skrift/templates/error-404.html,sha256=sJrDaF3Or3Nyki8mxo3wBxLLzgy4wkB9p9wdS8pRA6k,409
46
48
  skrift/templates/error-500.html,sha256=MR0wJ1JKLqdmdvsoJbQnZxLkxDPE59LrlbtVPKLM8-A,401
@@ -54,15 +56,17 @@ skrift/templates/admin/pages/list.html,sha256=LYh1vEwSHkyXihjcy5PIo33iA7UeIxprw4
54
56
  skrift/templates/admin/settings/site.html,sha256=xHZCZGj9XqyLKCe2eoBlpI3oFIx5VoG_5FP-VAF2mz4,1516
55
57
  skrift/templates/admin/users/list.html,sha256=9GWql1la5Srm-OOgooHR9Eouk7ZL66K_FXY6HAAB5lA,1732
56
58
  skrift/templates/admin/users/roles.html,sha256=pos-ZM-gXYCN_D8DZpzwEBEm_WvmOKMMPz-3xJqs7nY,1473
57
- skrift/templates/auth/login.html,sha256=hafLcmmRgiLhFjTDzGvEla31JoKl6dfw7sLdzQyzGmA,6195
59
+ skrift/templates/auth/dummy_login.html,sha256=G2ykWSiOK7XFdr7dzD-7VvKLTXzYT6Lduq5Pc6B21I0,2430
60
+ skrift/templates/auth/login.html,sha256=kBOfKRvKqn0L_POtXgOdl_rzUTNbr9F5NKtqxOMXQdk,7057
58
61
  skrift/templates/setup/admin.html,sha256=BSIztZT2iqxVSW23Tfg7ZM51SdGrKL422CR05DPjNic,804
59
62
  skrift/templates/setup/auth.html,sha256=0DVL0kU6DlJ2pWe4zwc3DsIVfBAdqpMJxPYvS4zm4OM,4953
60
63
  skrift/templates/setup/base.html,sha256=LTXqbnHMvx1wDsxFvo4BSieBPD9pcLMj6NM4ZGzErFM,10372
61
64
  skrift/templates/setup/complete.html,sha256=oyT-rYPl0uuyOjPXgNeLr8YoptW9QjHTlScZSViDvTk,630
65
+ skrift/templates/setup/configuring.html,sha256=2KHW9h2BrJgL_kO5IizbAYs4pnFLyRf76IQvEj_cNRM,4607
62
66
  skrift/templates/setup/database.html,sha256=gU4-315-QraHa2Eq4Fh3b55QpOM2CkJzh27_Yz13frA,5495
63
67
  skrift/templates/setup/restart.html,sha256=GHg31F_e2uLFhWUzJoalk0Y0oYLqsFWyZXWKX3mblbY,1355
64
68
  skrift/templates/setup/site.html,sha256=PSOH-q1-ZBl47iSW9-Ad6lEfJn_fzdGD3Pk4vb3xgK4,1680
65
- skrift-0.1.0a1.dist-info/METADATA,sha256=lrfb40qZ-KZ5o5U7j1A9ablJG4cbqXexdcOTkMMuD54,6365
66
- skrift-0.1.0a1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
67
- skrift-0.1.0a1.dist-info/entry_points.txt,sha256=4AIrmbeWKOdZnvTsKT3US6N3X9rrgk9jEDsYOPEZ1AE,74
68
- skrift-0.1.0a1.dist-info/RECORD,,
69
+ skrift-0.1.0a3.dist-info/METADATA,sha256=BNvNuT2HgfrWYz5Eh5zKmp-9_6JypPmEM8PiALxdeDg,6435
70
+ skrift-0.1.0a3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
71
+ skrift-0.1.0a3.dist-info/entry_points.txt,sha256=4AIrmbeWKOdZnvTsKT3US6N3X9rrgk9jEDsYOPEZ1AE,74
72
+ skrift-0.1.0a3.dist-info/RECORD,,