@svadmin/lite 0.3.2 → 0.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/lite",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "SSR-compatible lightweight admin UI for @svadmin — zero client-side JS, works in IE11",
5
5
  "type": "module",
6
6
  "files": [
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "peerDependencies": {
23
23
  "svelte": "^5.0.0",
24
- "@svadmin/core": "^0.21.2",
24
+ "@svadmin/core": "^0.22.1",
25
25
  "@sveltejs/kit": "^2.0.0"
26
26
  },
27
27
  "dependencies": {
@@ -79,11 +79,11 @@
79
79
  {#if totalPages > 1}
80
80
  <div class="lite-pagination" style="margin-top:12px;">
81
81
  {#if page > 1}
82
- <a href="{basePath}page={page - 1}" class="lite-btn lite-btn-sm">&laquo; Prev</a>
82
+ <a href={`${basePath}page={page - 1}`} class="lite-btn lite-btn-sm">&laquo; Prev</a>
83
83
  {/if}
84
84
  <span style="padding:0 12px;font-size:14px;color:#64748b;">Page {page} / {totalPages} ({total} total)</span>
85
85
  {#if page < totalPages}
86
- <a href="{basePath}page={page + 1}" class="lite-btn lite-btn-sm">Next &raquo;</a>
86
+ <a href={`${basePath}page={page + 1}`} class="lite-btn lite-btn-sm">Next &raquo;</a>
87
87
  {/if}
88
88
  </div>
89
89
  {/if}
@@ -81,7 +81,7 @@
81
81
  {:else}
82
82
  {#each menuResources as res}
83
83
  <a
84
- href="{basePath}/{res.name}"
84
+ href={`${basePath}/${res.name}`}
85
85
  class={res.name === currentResource ? 'active' : ''}
86
86
  >
87
87
  {res.label}
@@ -91,7 +91,7 @@
91
91
  {#if userName}
92
92
  <div style="position:absolute;bottom:0;left:0;right:0;padding:12px 16px;border-top:1px solid #334155;font-size:12px;color:#94a3b8;">
93
93
  {userName}
94
- <form method="POST" action="{basePath}/login?/logout" style="display:inline;margin-left:8px;">
94
+ <form method="POST" action={`${basePath}/login?/logout`} style="display:inline;margin-left:8px;">
95
95
  <button type="submit" class="lite-btn lite-btn-sm" style="color:#94a3b8;border-color:#475569;background:transparent;">Logout</button>
96
96
  </form>
97
97
  </div>
@@ -33,11 +33,11 @@
33
33
  <h1>{resource.label} #{id}</h1>
34
34
  <div>
35
35
  {#if canEdit}
36
- <a href="{basePath}/{resource.name}/edit/{id}" class="lite-btn lite-btn-primary">
36
+ <a href={`${basePath}/${resource.name}/edit/${id}`} class="lite-btn lite-btn-primary">
37
37
  {t('common.edit') || 'Edit'}
38
38
  </a>
39
39
  {/if}
40
- <a href="{basePath}/{resource.name}" class="lite-btn" style="margin-left:8px;">
40
+ <a href={`${basePath}/${resource.name}`} class="lite-btn" style="margin-left:8px;">
41
41
  {t('common.backToList') || 'Back to List'}
42
42
  </a>
43
43
  </div>
@@ -103,7 +103,7 @@
103
103
  {#if canEdit || canDelete}
104
104
  <td class="actions">
105
105
  {#if canEdit}
106
- <a href="{basePath}/{resource.name}/edit/{id}" class="lite-btn lite-btn-sm">{t('common.edit') || 'Edit'}</a>
106
+ <a href={`${basePath}/${resource.name}/edit/${id}`} class="lite-btn lite-btn-sm">{t('common.edit') || 'Edit'}</a>
107
107
  {/if}
108
108
  {#if canDelete}
109
109
  <!-- Delete uses <details> for no-JS confirmation -->
@@ -26,7 +26,7 @@
26
26
  const href = $derived(
27
27
  mode === 'create'
28
28
  ? `${basePath}/${resource.name}/create`
29
- : `${basePath}/${resource.name}/${recordId}/edit`
29
+ : `${basePath}/${resource.name}/edit/${recordId}`
30
30
  );
31
31
 
32
32
  const buttonLabel = $derived(
@@ -27,7 +27,7 @@
27
27
  const href = $derived(
28
28
  mode === 'create'
29
29
  ? `${basePath}/${resource.name}/create`
30
- : `${basePath}/${resource.name}/${recordId}/edit`
30
+ : `${basePath}/${resource.name}/edit/${recordId}`
31
31
  );
32
32
 
33
33
  const buttonLabel = $derived(
@@ -22,7 +22,7 @@
22
22
  </script>
23
23
 
24
24
  <a
25
- href="{basePath}/{resource}/clone/{recordItemId}"
25
+ href={`${basePath}/${resource}/clone/${recordItemId}`}
26
26
  class="lite-btn {size === 'sm' ? 'lite-btn-sm' : ''} {className}"
27
27
  title={t('common.clone') || 'Clone'}
28
28
  >
@@ -20,7 +20,7 @@
20
20
  </script>
21
21
 
22
22
  <a
23
- href="{basePath}/{resource}/create"
23
+ href={`${basePath}/${resource}/create`}
24
24
  class="lite-btn lite-btn-primary {size === 'sm' ? 'lite-btn-sm' : ''} {className}"
25
25
  title={t('common.create') || 'Create'}
26
26
  >
@@ -37,7 +37,7 @@
37
37
  <p style="margin: 0 0 8px; font-size: 13px; color: #111827;">
38
38
  {t('common.areYouSure') || 'Are you sure?'}
39
39
  </p>
40
- <form method="POST" action="?/{resource}_delete" style="display:inline-flex; gap: 8px;">
40
+ <form method="POST" action="?/delete" style="display:inline-flex; gap: 8px;">
41
41
  <input type="hidden" name="id" value={String(recordItemId)} />
42
42
  {#if redirectUrl}
43
43
  <input type="hidden" name="redirect" value={redirectUrl} />
@@ -22,7 +22,7 @@
22
22
  </script>
23
23
 
24
24
  <a
25
- href="{basePath}/{resource}/edit/{recordItemId}"
25
+ href={`${basePath}/${resource}/edit/${recordItemId}`}
26
26
  class="lite-btn {size === 'sm' ? 'lite-btn-sm' : ''} {className}"
27
27
  title={t('common.edit') || 'Edit'}
28
28
  >
@@ -31,7 +31,7 @@
31
31
  </summary>
32
32
  <div class="lite-confirm-panel">
33
33
  <p style="margin: 0 0 8px; font-size: 13px;">{t('common.importData') || 'Import data (CSV/JSON)'}</p>
34
- <form method="POST" action="?/{resource}_import" enctype="multipart/form-data" style="display:flex; flex-direction: column; gap: 8px;">
34
+ <form method="POST" action="?/${resource}_import" enctype="multipart/form-data" style="display:flex; flex-direction: column; gap: 8px;">
35
35
  <input type="file" name="file" accept=".csv,.json" required style="font-size: 13px;" />
36
36
  <div style="display:flex; gap: 8px; justify-content: flex-end;">
37
37
  <button
@@ -20,7 +20,7 @@
20
20
  </script>
21
21
 
22
22
  <a
23
- href="{basePath}/{resource}"
23
+ href={`${basePath}/${resource}`}
24
24
  class="lite-btn {size === 'sm' ? 'lite-btn-sm' : ''} {className}"
25
25
  title={t('common.list') || 'List'}
26
26
  >
@@ -22,7 +22,7 @@
22
22
  </script>
23
23
 
24
24
  <a
25
- href="{basePath}/{resource}/show/{recordItemId}"
25
+ href={`${basePath}/${resource}/show/${recordItemId}`}
26
26
  class="lite-btn {size === 'sm' ? 'lite-btn-sm' : ''} {className}"
27
27
  title={t('common.show') || 'Show'}
28
28
  >
@@ -75,7 +75,7 @@
75
75
  {:else}
76
76
  {#each menuResources as res}
77
77
  <a
78
- href="{basePath}/{res.name}"
78
+ href={`${basePath}/${res.name}`}
79
79
  class={res.name === currentResource ? 'active' : ''}
80
80
  >
81
81
  {res.label ?? res.name}
@@ -85,7 +85,7 @@
85
85
  {#if userName}
86
86
  <div style="position:absolute;bottom:0;left:0;right:0;padding:12px 16px;border-top:1px solid #334155;font-size:12px;color:#94a3b8;">
87
87
  {userName}
88
- <form method="POST" action="{basePath}/login?/logout" style="display:inline;margin-left:8px;">
88
+ <form method="POST" action={`${basePath}/login?/logout`} style="display:inline;margin-left:8px;">
89
89
  <button type="submit" class="lite-btn lite-btn-sm" style="color:#94a3b8;border-color:#475569;background:transparent;padding:2px 8px;">Logout</button>
90
90
  </form>
91
91
  </div>
@@ -33,7 +33,7 @@
33
33
  {resource}
34
34
  {errors}
35
35
  {values}
36
- action="?/{resource.name}_create"
37
- cancelUrl="{basePath}/{resource.name}"
36
+ action="?/create"
37
+ cancelUrl={`${basePath}/${resource.name}`}
38
38
  />
39
39
  </div>
@@ -37,7 +37,7 @@
37
37
  {/if}
38
38
  <LiteListButton resource={resource.name} {basePath} />
39
39
  {#if canDelete}
40
- <LiteDeleteButton resource={resource.name} recordItemId={idStr} redirectUrl="{basePath}/{resource.name}" {basePath} />
40
+ <LiteDeleteButton resource={resource.name} recordItemId={idStr} redirectUrl={`${basePath}/${resource.name}`} {basePath} />
41
41
  {/if}
42
42
  </div>
43
43
  </div>
@@ -48,7 +48,7 @@
48
48
  {resource}
49
49
  {errors}
50
50
  values={record}
51
- action="?/{resource.name}_update"
52
- cancelUrl="{basePath}/{resource.name}"
51
+ action="?/update"
52
+ cancelUrl={`${basePath}/${resource.name}`}
53
53
  />
54
54
  </div>
@@ -39,7 +39,7 @@
39
39
  {/if}
40
40
  <LiteListButton resource={resource.name} {basePath} />
41
41
  {#if canDelete}
42
- <LiteDeleteButton resource={resource.name} recordItemId={idStr} redirectUrl="{basePath}/{resource.name}" {basePath} />
42
+ <LiteDeleteButton resource={resource.name} recordItemId={idStr} redirectUrl={`${basePath}/${resource.name}`} {basePath} />
43
43
  {/if}
44
44
  </div>
45
45
  </div>
@@ -9,7 +9,7 @@ import type {
9
9
  ResourceDefinition, FieldDefinition,
10
10
  Sort, Filter,
11
11
  } from '@svadmin/core';
12
- import { fail, redirect, type RequestEvent } from '@sveltejs/kit';
12
+ import { fail, redirect, isRedirect, type RequestEvent } from '@sveltejs/kit';
13
13
 
14
14
  // ─── List Loader ──────────────────────────────────────────────
15
15
 
@@ -107,6 +107,7 @@ export function createCrudActions(
107
107
  const result = await dp.create({ resource: resource.name, variables });
108
108
  return { success: true, id: (result.data as Record<string, unknown>)[pk] };
109
109
  } catch (e) {
110
+ if (isRedirect(e)) throw e;
110
111
  return { success: false, error: (e as Error).message, values: variables };
111
112
  }
112
113
  },
@@ -120,6 +121,7 @@ export function createCrudActions(
120
121
  await dp.update({ resource: resource.name, id, variables });
121
122
  return { success: true };
122
123
  } catch (e) {
124
+ if (isRedirect(e)) throw e;
123
125
  return { success: false, error: (e as Error).message, values: variables };
124
126
  }
125
127
  },
@@ -133,6 +135,7 @@ export function createCrudActions(
133
135
  if (redirectTo) throw redirect(303, redirectTo);
134
136
  return { success: true };
135
137
  } catch (e) {
138
+ if (isRedirect(e)) throw e;
136
139
  return { success: false, error: (e as Error).message };
137
140
  }
138
141
  },
@@ -155,21 +158,31 @@ export function createAuthGuard(
155
158
  return resolve(event);
156
159
  }
157
160
 
161
+ // Local Lite Session Verify
162
+ const session = event.cookies.get('svadmin-session');
163
+ if (!session) {
164
+ return new Response(null, {
165
+ status: 302,
166
+ headers: { Location: loginPath },
167
+ });
168
+ }
169
+
158
170
  try {
159
171
  const check = await authProvider.check();
160
172
  if (!check.authenticated) {
173
+ event.cookies.delete('svadmin-session', { path: '/' });
161
174
  return new Response(null, {
162
175
  status: 302,
163
176
  headers: { Location: loginPath },
164
177
  });
165
178
  }
166
179
  } catch {
180
+ event.cookies.delete('svadmin-session', { path: '/' });
167
181
  return new Response(null, {
168
182
  status: 302,
169
183
  headers: { Location: loginPath },
170
184
  });
171
185
  }
172
-
173
186
  return resolve(event);
174
187
  };
175
188
  }
@@ -196,6 +209,7 @@ export function createAuthActions(authProvider: AuthProvider) {
196
209
  }
197
210
  return { success: false, error: result.error?.message ?? 'Login failed' };
198
211
  } catch (e) {
212
+ if (isRedirect(e)) throw e;
199
213
  return { success: false, error: (e as Error).message };
200
214
  }
201
215
  },