@axium/server 0.28.3 → 0.28.5
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": "@axium/server",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.5",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev>",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"clean": "rm -rf build .svelte-kit node_modules/{.vite,.vite-temp}"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@axium/client": ">=0.9.
|
|
50
|
+
"@axium/client": ">=0.9.5",
|
|
51
51
|
"@axium/core": ">=0.12.0",
|
|
52
52
|
"kysely": "^0.28.0",
|
|
53
53
|
"utilium": "^2.3.8",
|
|
@@ -77,9 +77,9 @@
|
|
|
77
77
|
<p>{user.id}</p>
|
|
78
78
|
<ClipboardCopy value={user.id} --size="16px" />
|
|
79
79
|
</div>
|
|
80
|
-
<
|
|
81
|
-
<button
|
|
82
|
-
<button
|
|
80
|
+
<div class="inline-button-container">
|
|
81
|
+
<button command="show-modal" commandfor="logout" class="inline-button signout">Sign Out</button>
|
|
82
|
+
<button command="show-modal" commandfor="delete" class="inline-button danger">Delete Account</button>
|
|
83
83
|
<Logout />
|
|
84
84
|
<FormDialog
|
|
85
85
|
id="delete"
|
|
@@ -89,30 +89,38 @@
|
|
|
89
89
|
>
|
|
90
90
|
<p>Are you sure you want to delete your account?<br />This action can't be undone.</p>
|
|
91
91
|
</FormDialog>
|
|
92
|
-
</
|
|
92
|
+
</div>
|
|
93
93
|
</div>
|
|
94
94
|
|
|
95
95
|
<div class="section main">
|
|
96
96
|
<h3>Passkeys</h3>
|
|
97
97
|
{#each passkeys as passkey}
|
|
98
98
|
<div class="item passkey">
|
|
99
|
-
<
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
99
|
+
<p>
|
|
100
|
+
<dfn title={passkey.deviceType == 'multiDevice' ? 'Multiple devices' : 'Single device'}>
|
|
101
|
+
<Icon i={passkey.deviceType == 'multiDevice' ? 'laptop-mobile' : 'mobile'} --size="16px" />
|
|
102
|
+
</dfn>
|
|
103
|
+
<dfn title="This passkey is {passkey.backedUp ? '' : 'not '}backed up">
|
|
104
|
+
<Icon i={passkey.backedUp ? 'circle-check' : 'circle-xmark'} --size="16px" />
|
|
105
|
+
</dfn>
|
|
106
|
+
{#if passkey.name}
|
|
107
|
+
<p>{passkey.name}</p>
|
|
108
|
+
{:else}
|
|
109
|
+
<p class="subtle"><i>Unnamed</i></p>
|
|
110
|
+
{/if}
|
|
111
|
+
</p>
|
|
110
112
|
<p>Created {passkey.createdAt.toLocaleString()}</p>
|
|
111
|
-
|
|
113
|
+
<button commandfor="edit_passkey:{passkey.id}" command="show-modal" class="icon-text">
|
|
114
|
+
<Icon i="pen" --size="16px" />
|
|
115
|
+
<span class="mobile-only">Rename</span>
|
|
116
|
+
</button>
|
|
112
117
|
{#if passkeys.length > 1}
|
|
113
|
-
|
|
118
|
+
<button commandfor="delete_passkey:{passkey.id}" command="show-modal" class="icon-text">
|
|
119
|
+
<Icon i="trash" --size="16px" />
|
|
120
|
+
<span class="mobile-only">Delete</span>
|
|
121
|
+
</button>
|
|
114
122
|
{:else}
|
|
115
|
-
<dfn title="You must have at least one passkey" class="disabled">
|
|
123
|
+
<dfn title="You must have at least one passkey" class="disabled icon-text mobile-hide">
|
|
116
124
|
<Icon i="trash-slash" --fill="#888" --size="16px" />
|
|
117
125
|
</dfn>
|
|
118
126
|
{/if}
|
|
@@ -140,11 +148,10 @@
|
|
|
140
148
|
<p>Are you sure you want to delete this passkey?<br />This action can't be undone.</p>
|
|
141
149
|
</FormDialog>
|
|
142
150
|
{/each}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
</span>
|
|
151
|
+
|
|
152
|
+
<button onclick={() => createPasskey(user.id).then(passkeys.push.bind(passkeys))} class="inline-button icon-text">
|
|
153
|
+
<Icon i="plus" /> Create
|
|
154
|
+
</button>
|
|
148
155
|
</div>
|
|
149
156
|
|
|
150
157
|
<div class="section main">
|
|
@@ -181,8 +188,4 @@
|
|
|
181
188
|
.greeting {
|
|
182
189
|
font-size: 2em;
|
|
183
190
|
}
|
|
184
|
-
|
|
185
|
-
.signout {
|
|
186
|
-
margin-top: 2em;
|
|
187
|
-
}
|
|
188
191
|
</style>
|
|
@@ -25,82 +25,103 @@
|
|
|
25
25
|
|
|
26
26
|
<form id="filter" method="dialog">
|
|
27
27
|
<h4>Filters</h4>
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<span>Since:</span>
|
|
35
|
-
<input type="date" name="since" value={data.filter.since} />
|
|
36
|
-
|
|
37
|
-
<span>Until:</span>
|
|
38
|
-
<input type="date" name="until" value={data.filter.until} />
|
|
39
|
-
|
|
40
|
-
<span>Tags:</span>
|
|
41
|
-
<input type="text" name="tags" value={data.filter.tags} />
|
|
42
|
-
|
|
43
|
-
<span>Event Name:</span>
|
|
44
|
-
{#if data.configured}
|
|
45
|
-
<select name="event">
|
|
46
|
-
<option value="">Any</option>
|
|
47
|
-
{#each data.configured.name as name}
|
|
48
|
-
<option value={name} selected={data.filter.event == name}>{name}</option>
|
|
49
|
-
{/each}
|
|
50
|
-
</select>
|
|
51
|
-
{:else}
|
|
52
|
-
<input type="text" name="event" value={data.filter.event} />
|
|
53
|
-
{/if}
|
|
54
|
-
|
|
55
|
-
<span>Source:</span>
|
|
56
|
-
{#if data.configured}
|
|
57
|
-
<select name="source">
|
|
58
|
-
<option value="">Any</option>
|
|
59
|
-
{#each data.configured.source as source}
|
|
60
|
-
<option value={source} selected={data.filter.source == source}>{source}</option>
|
|
28
|
+
|
|
29
|
+
<div class="filter-field">
|
|
30
|
+
<span>Minimum Severity:</span>
|
|
31
|
+
<select name="severity" value={data.filter.severity}>
|
|
32
|
+
{#each severityNames as value}
|
|
33
|
+
<option {value} selected={value == 'info'}>{capitalize(value)}</option>
|
|
61
34
|
{/each}
|
|
62
35
|
</select>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="filter-field">
|
|
39
|
+
<span>Since:</span>
|
|
40
|
+
<input type="date" name="since" value={data.filter.since} />
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="filter-field">
|
|
44
|
+
<span>Until:</span>
|
|
45
|
+
<input type="date" name="until" value={data.filter.until} />
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="filter-field">
|
|
49
|
+
<span>Tags:</span>
|
|
50
|
+
<input type="text" name="tags" value={data.filter.tags} />
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="filter-field">
|
|
54
|
+
<span>Event Name:</span>
|
|
55
|
+
{#if data.configured}
|
|
56
|
+
<select name="event">
|
|
57
|
+
<option value="">Any</option>
|
|
58
|
+
{#each data.configured.name as name}
|
|
59
|
+
<option value={name} selected={data.filter.event == name}>{name}</option>
|
|
60
|
+
{/each}
|
|
61
|
+
</select>
|
|
62
|
+
{:else}
|
|
63
|
+
<input type="text" name="event" value={data.filter.event} />
|
|
64
|
+
{/if}
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="filter-field">
|
|
68
|
+
<span>Source:</span>
|
|
69
|
+
{#if data.configured}
|
|
70
|
+
<select name="source">
|
|
71
|
+
<option value="">Any</option>
|
|
72
|
+
{#each data.configured.source as source}
|
|
73
|
+
<option value={source} selected={data.filter.source == source}>{source}</option>
|
|
74
|
+
{/each}
|
|
75
|
+
</select>
|
|
76
|
+
{:else}
|
|
77
|
+
<input type="text" name="source" value={data.filter.source} />
|
|
78
|
+
{/if}
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="filter-field">
|
|
82
|
+
<span>User UUID:</span>
|
|
83
|
+
<input type="text" name="user" size="36" value={data.filter.user} />
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="inline-button-container">
|
|
87
|
+
<button
|
|
88
|
+
class="inline-button"
|
|
89
|
+
onclick={e => {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
const fd = new FormData(e.currentTarget.form!);
|
|
92
|
+
const params = new URLSearchParams();
|
|
93
|
+
for (let [key, value] of fd.entries()) {
|
|
94
|
+
if (!value) continue;
|
|
95
|
+
switch (key) {
|
|
96
|
+
case 'severity':
|
|
97
|
+
if (value != 'info') params.set(key, value as string);
|
|
98
|
+
break;
|
|
99
|
+
case 'since':
|
|
100
|
+
case 'until':
|
|
101
|
+
params.set(key, new Date(value as string).toISOString());
|
|
102
|
+
break;
|
|
103
|
+
case 'tags':
|
|
104
|
+
for (const tag of value
|
|
105
|
+
.toString()
|
|
106
|
+
.split(',')
|
|
107
|
+
.map(t => t.trim()))
|
|
108
|
+
params.append(key, tag);
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
params.set(key, value as string);
|
|
112
|
+
}
|
|
93
113
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
location.search = params ? '?' + params.toString() : '';
|
|
115
|
+
}}>Apply</button
|
|
116
|
+
>
|
|
117
|
+
<button
|
|
118
|
+
class="inline-button"
|
|
119
|
+
onclick={e => {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
location.search = '';
|
|
122
|
+
}}>Reset</button
|
|
123
|
+
>
|
|
124
|
+
</div>
|
|
104
125
|
</form>
|
|
105
126
|
|
|
106
127
|
<div class="list-container">
|
|
@@ -146,13 +167,22 @@
|
|
|
146
167
|
background-color: var(--bg-menu);
|
|
147
168
|
padding: 1em;
|
|
148
169
|
border-radius: 0.5em;
|
|
149
|
-
display:
|
|
150
|
-
|
|
170
|
+
display: flex;
|
|
171
|
+
flex-wrap: wrap;
|
|
172
|
+
flex-direction: column;
|
|
151
173
|
gap: 0.25em;
|
|
152
174
|
width: fit-content;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.filter-field {
|
|
178
|
+
display: grid;
|
|
179
|
+
grid-template-columns: 1fr 1fr;
|
|
180
|
+
gap: 0.25em;
|
|
181
|
+
}
|
|
153
182
|
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
@media (width < 700px) {
|
|
184
|
+
#filter {
|
|
185
|
+
width: calc(100%);
|
|
156
186
|
}
|
|
157
187
|
}
|
|
158
188
|
</style>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<div class="user list-item" onclick={e => e.currentTarget === e.target && (location.href = '/admin/users/' + user.id)}>
|
|
27
27
|
<span>{user.name}</span>
|
|
28
28
|
<span>{user.email}</span>
|
|
29
|
-
<span>
|
|
29
|
+
<span class="mobile-hide">
|
|
30
30
|
{#if user.isAdmin}
|
|
31
31
|
{@render attr('crown', 'Admin', '#710')}
|
|
32
32
|
{/if}
|
|
@@ -37,8 +37,14 @@
|
|
|
37
37
|
{@render attr('at', role)}
|
|
38
38
|
{/each}
|
|
39
39
|
</span>
|
|
40
|
-
<a href="/admin/audit?user={user.id}"
|
|
41
|
-
|
|
40
|
+
<a class="icon-text mobile-button" href="/admin/audit?user={user.id}">
|
|
41
|
+
<Icon i="file-shield" />
|
|
42
|
+
<span class="mobile-only">Audit</span>
|
|
43
|
+
</a>
|
|
44
|
+
<a class="icon-text mobile-button" href="/admin/users/{user.id}">
|
|
45
|
+
<Icon i="chevron-right" />
|
|
46
|
+
<span class="mobile-only">Manage</span>
|
|
47
|
+
</a>
|
|
42
48
|
</div>
|
|
43
49
|
{:else}
|
|
44
50
|
<div class="error">No users!</div>
|
|
@@ -46,10 +52,6 @@
|
|
|
46
52
|
</div>
|
|
47
53
|
|
|
48
54
|
<style>
|
|
49
|
-
.list-item {
|
|
50
|
-
grid-template-columns: 1fr 1fr 3fr repeat(2, 2em);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
55
|
.attribute {
|
|
54
56
|
border-radius: 1em;
|
|
55
57
|
padding: 0.25em 0.75em;
|
|
@@ -57,4 +59,14 @@
|
|
|
57
59
|
align-items: center;
|
|
58
60
|
gap: 0.25em;
|
|
59
61
|
}
|
|
62
|
+
|
|
63
|
+
.list-item {
|
|
64
|
+
grid-template-columns: 1fr 1fr 3fr repeat(2, 2em);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@media (width < 700px) {
|
|
68
|
+
.list-item {
|
|
69
|
+
grid-template-columns: 1fr 1fr;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
60
72
|
</style>
|