@axium/core 0.7.0 → 0.8.0
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/dist/api.d.ts +37 -1
- package/dist/audit.d.ts +43 -0
- package/dist/audit.js +28 -0
- package/dist/color.d.ts +1 -0
- package/dist/color.js +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/plugins.d.ts +49 -0
- package/dist/plugins.js +22 -0
- package/dist/user.d.ts +7 -1
- package/dist/user.js +6 -12
- package/package.json +2 -2
package/dist/api.d.ts
CHANGED
|
@@ -2,10 +2,12 @@ import type { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequest
|
|
|
2
2
|
import type z from 'zod';
|
|
3
3
|
import type { AccessControl } from './access.js';
|
|
4
4
|
import type { App } from './apps.js';
|
|
5
|
+
import type { AuditEvent, AuditFilter, Severity } from './audit.js';
|
|
5
6
|
import type { NewSessionResponse, Session, Verification } from './auth.js';
|
|
6
7
|
import type { Passkey, PasskeyAuthenticationResponse, PasskeyChangeable, PasskeyRegistration } from './passkeys.js';
|
|
7
8
|
import type { RequestMethod } from './requests.js';
|
|
8
|
-
import type { LogoutSessions, User, UserAuthOptions, UserChangeable, UserPublic, UserRegistration } from './user.js';
|
|
9
|
+
import type { LogoutSessions, User, UserAuthOptions, UserChangeable, UserInternal, UserPublic, UserRegistration } from './user.js';
|
|
10
|
+
import type { PluginInternal } from './plugins.js';
|
|
9
11
|
/**
|
|
10
12
|
* Types for all API endpoints
|
|
11
13
|
* @internal
|
|
@@ -88,6 +90,40 @@ export interface $API {
|
|
|
88
90
|
permission: number;
|
|
89
91
|
}, AccessControl];
|
|
90
92
|
};
|
|
93
|
+
'admin/summary': {
|
|
94
|
+
GET: {
|
|
95
|
+
users: number;
|
|
96
|
+
passkeys: number;
|
|
97
|
+
sessions: number;
|
|
98
|
+
auditEvents: Record<keyof typeof Severity, number>;
|
|
99
|
+
configFiles: number;
|
|
100
|
+
plugins: number;
|
|
101
|
+
version: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
'admin/users/all': {
|
|
105
|
+
GET: UserInternal[];
|
|
106
|
+
};
|
|
107
|
+
'admin/users/:userId': {
|
|
108
|
+
GET: UserInternal & {
|
|
109
|
+
sessions: Session[];
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
'admin/config': {
|
|
113
|
+
GET: {
|
|
114
|
+
files: Record<string, object>;
|
|
115
|
+
config: Record<string, unknown>;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
'admin/plugins': {
|
|
119
|
+
GET: PluginInternal[];
|
|
120
|
+
};
|
|
121
|
+
'admin/audit/events': {
|
|
122
|
+
GET: [filter: z.input<typeof AuditFilter>, result: AuditEvent[]];
|
|
123
|
+
};
|
|
124
|
+
'admin/audit/:eventId': {
|
|
125
|
+
GET: AuditEvent;
|
|
126
|
+
};
|
|
91
127
|
}
|
|
92
128
|
export type Endpoint = keyof $API;
|
|
93
129
|
export type APIFunction<Method extends RequestMethod, E extends Endpoint> = Method extends keyof $API[E] ? $API[E][Method] extends [infer Body, infer Result] ? (body: Body) => Promise<Result> : () => $API[E][Method] : unknown;
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
import type { User } from './user.js';
|
|
3
|
+
export declare enum Severity {
|
|
4
|
+
Emergency = 0,
|
|
5
|
+
Alert = 1,
|
|
6
|
+
Critical = 2,
|
|
7
|
+
Error = 3,
|
|
8
|
+
Warning = 4,
|
|
9
|
+
Notice = 5,
|
|
10
|
+
Info = 6,
|
|
11
|
+
Debug = 7
|
|
12
|
+
}
|
|
13
|
+
export declare const severityNames: Lowercase<keyof typeof Severity>[];
|
|
14
|
+
export interface AuditEvent<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
15
|
+
/** UUID of the event */
|
|
16
|
+
id: string;
|
|
17
|
+
/** UUID of the user that triggered the event. `null` for events triggered via the server CLI */
|
|
18
|
+
userId: string | null;
|
|
19
|
+
user?: (Pick<User, 'id' | 'name'> & Partial<User>) | null;
|
|
20
|
+
/** When the event happened */
|
|
21
|
+
timestamp: Date;
|
|
22
|
+
/** How severe the event is */
|
|
23
|
+
severity: Severity;
|
|
24
|
+
/** Snake case name for the event */
|
|
25
|
+
name: string;
|
|
26
|
+
/** The source of the event. This should be a package name */
|
|
27
|
+
source: string;
|
|
28
|
+
/** Tags for the event. For example, `auth` for authentication events */
|
|
29
|
+
tags: string[];
|
|
30
|
+
/** Additional event specific data. */
|
|
31
|
+
extra: T;
|
|
32
|
+
}
|
|
33
|
+
export declare const AuditFilter: z.ZodObject<{
|
|
34
|
+
since: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
35
|
+
until: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
36
|
+
user: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodUUID, z.ZodPipe<z.ZodLiteral<"null">, z.ZodTransform<null, "null">>]>>>;
|
|
37
|
+
severity: z.ZodOptional<z.ZodPipe<z.ZodLiteral<"error" | "debug" | "emergency" | "alert" | "critical" | "warning" | "notice" | "info">, z.ZodTransform<Severity, "error" | "debug" | "emergency" | "alert" | "critical" | "warning" | "notice" | "info">>>;
|
|
38
|
+
source: z.ZodOptional<z.ZodString>;
|
|
39
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
40
|
+
event: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
export interface AuditFilter extends z.infer<typeof AuditFilter> {
|
|
43
|
+
}
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { capitalize, uncapitalize } from 'utilium/string.js';
|
|
2
|
+
import * as z from 'zod';
|
|
3
|
+
export var Severity;
|
|
4
|
+
(function (Severity) {
|
|
5
|
+
Severity[Severity["Emergency"] = 0] = "Emergency";
|
|
6
|
+
Severity[Severity["Alert"] = 1] = "Alert";
|
|
7
|
+
Severity[Severity["Critical"] = 2] = "Critical";
|
|
8
|
+
Severity[Severity["Error"] = 3] = "Error";
|
|
9
|
+
Severity[Severity["Warning"] = 4] = "Warning";
|
|
10
|
+
Severity[Severity["Notice"] = 5] = "Notice";
|
|
11
|
+
Severity[Severity["Info"] = 6] = "Info";
|
|
12
|
+
Severity[Severity["Debug"] = 7] = "Debug";
|
|
13
|
+
})(Severity || (Severity = {}));
|
|
14
|
+
export const severityNames = Object.keys(Severity)
|
|
15
|
+
.filter(k => isNaN(Number(k)))
|
|
16
|
+
.map(uncapitalize);
|
|
17
|
+
export const AuditFilter = z.object({
|
|
18
|
+
since: z.coerce.date().optional(),
|
|
19
|
+
until: z.coerce.date().optional(),
|
|
20
|
+
user: z.union([z.uuid(), z.literal(['null']).transform(() => null)]).nullish(),
|
|
21
|
+
severity: z
|
|
22
|
+
.literal(severityNames)
|
|
23
|
+
.transform(v => Severity[capitalize(v)])
|
|
24
|
+
.optional(),
|
|
25
|
+
source: z.string().optional(),
|
|
26
|
+
tags: z.string().array().optional(),
|
|
27
|
+
event: z.string().optional(),
|
|
28
|
+
});
|
package/dist/color.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function colorHash(input: string): `rgb(${number}, ${number}, ${number})`;
|
package/dist/color.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function colorHash(input) {
|
|
2
|
+
let color = input.charCodeAt(0);
|
|
3
|
+
for (let i = 1; i < input.length; i++) {
|
|
4
|
+
color *= input.charCodeAt(i);
|
|
5
|
+
}
|
|
6
|
+
color &= 0xbfbfbf;
|
|
7
|
+
const r = (color >> 16) & 0xff;
|
|
8
|
+
const g = (color >> 8) & 0xff;
|
|
9
|
+
const b = color & 0xff;
|
|
10
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
11
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export * from './access.js';
|
|
2
2
|
export * from './api.js';
|
|
3
3
|
export * from './apps.js';
|
|
4
|
+
export * from './audit.js';
|
|
4
5
|
export * from './auth.js';
|
|
5
6
|
export * as icons from './icons.js';
|
|
6
7
|
export * from './passkeys.js';
|
|
8
|
+
export * from './plugins.js';
|
|
7
9
|
export * from './preferences.js';
|
|
8
10
|
export * from './requests.js';
|
|
9
11
|
export * from './schemas.js';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export * from './access.js';
|
|
2
2
|
export * from './api.js';
|
|
3
3
|
export * from './apps.js';
|
|
4
|
+
export * from './audit.js';
|
|
4
5
|
export * from './auth.js';
|
|
5
6
|
export * as icons from './icons.js';
|
|
6
7
|
export * from './passkeys.js';
|
|
8
|
+
export * from './plugins.js';
|
|
7
9
|
export * from './preferences.js';
|
|
8
10
|
export * from './requests.js';
|
|
9
11
|
export * from './schemas.js';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
export declare const Plugin: z.ZodObject<{
|
|
3
|
+
name: z.ZodString;
|
|
4
|
+
version: z.ZodString;
|
|
5
|
+
description: z.ZodOptional<z.ZodString>;
|
|
6
|
+
/** The path to the hooks script */
|
|
7
|
+
hooks: z.ZodOptional<z.ZodString>;
|
|
8
|
+
/** The path to the HTTP handler */
|
|
9
|
+
http_handler: z.ZodOptional<z.ZodString>;
|
|
10
|
+
apps: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
11
|
+
id: z.ZodString;
|
|
12
|
+
name: z.ZodOptional<z.ZodString>;
|
|
13
|
+
image: z.ZodOptional<z.ZodString>;
|
|
14
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
15
|
+
}, z.core.$strip>>>;
|
|
16
|
+
routes: z.ZodOptional<z.ZodString>;
|
|
17
|
+
}, z.core.$loose>;
|
|
18
|
+
export type Plugin = z.infer<typeof Plugin>;
|
|
19
|
+
export interface PluginInternal extends Plugin {
|
|
20
|
+
readonly path: string;
|
|
21
|
+
readonly dirname: string;
|
|
22
|
+
readonly specifier: string;
|
|
23
|
+
readonly _loadedBy: string;
|
|
24
|
+
readonly _hooks?: Hooks;
|
|
25
|
+
}
|
|
26
|
+
export declare const PluginHooks: z.ZodObject<{
|
|
27
|
+
statusText: z.ZodOptional<z.ZodCustom<z.core.$InferInnerFunctionTypeAsync<z.ZodTuple<readonly [], null>, z.ZodString>, z.core.$InferInnerFunctionTypeAsync<z.ZodTuple<readonly [], null>, z.ZodString>>>;
|
|
28
|
+
db_init: z.ZodOptional<z.ZodCustom<(...args: any[]) => any, (...args: any[]) => any>>;
|
|
29
|
+
remove: z.ZodOptional<z.ZodCustom<(...args: any[]) => any, (...args: any[]) => any>>;
|
|
30
|
+
db_wipe: z.ZodOptional<z.ZodCustom<(...args: any[]) => any, (...args: any[]) => any>>;
|
|
31
|
+
clean: z.ZodOptional<z.ZodCustom<(...args: any[]) => any, (...args: any[]) => any>>;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
interface _InitOptions {
|
|
34
|
+
force?: boolean;
|
|
35
|
+
skip: boolean;
|
|
36
|
+
check: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface Hooks {
|
|
39
|
+
statusText?(): string | Promise<string>;
|
|
40
|
+
db_init?: (opt: _InitOptions) => void | Promise<void>;
|
|
41
|
+
remove?: (opt: {
|
|
42
|
+
force?: boolean;
|
|
43
|
+
}) => void | Promise<void>;
|
|
44
|
+
db_wipe?: (opt: {
|
|
45
|
+
force?: boolean;
|
|
46
|
+
}) => void | Promise<void>;
|
|
47
|
+
clean?: (opt: Partial<_InitOptions>) => void | Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
export {};
|
package/dist/plugins.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
import { zAsyncFunction } from './schemas.js';
|
|
3
|
+
import { App } from './apps.js';
|
|
4
|
+
export const Plugin = z.looseObject({
|
|
5
|
+
name: z.string(),
|
|
6
|
+
version: z.string(),
|
|
7
|
+
description: z.string().optional(),
|
|
8
|
+
/** The path to the hooks script */
|
|
9
|
+
hooks: z.string().optional(),
|
|
10
|
+
/** The path to the HTTP handler */
|
|
11
|
+
http_handler: z.string().optional(),
|
|
12
|
+
apps: z.array(App).optional(),
|
|
13
|
+
routes: z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
const fn = z.custom(data => typeof data === 'function');
|
|
16
|
+
export const PluginHooks = z.object({
|
|
17
|
+
statusText: zAsyncFunction(z.function({ input: [], output: z.string() })).optional(),
|
|
18
|
+
db_init: fn.optional(),
|
|
19
|
+
remove: fn.optional(),
|
|
20
|
+
db_wipe: fn.optional(),
|
|
21
|
+
clean: fn.optional(),
|
|
22
|
+
});
|
package/dist/user.d.ts
CHANGED
|
@@ -9,15 +9,21 @@ export declare const User: z.ZodObject<{
|
|
|
9
9
|
preferences: z.ZodRecord<z.ZodString, z.ZodAny>;
|
|
10
10
|
roles: z.ZodArray<z.ZodString>;
|
|
11
11
|
registeredAt: z.ZodDate;
|
|
12
|
+
isAdmin: z.ZodBoolean;
|
|
12
13
|
}, z.core.$strip>;
|
|
13
14
|
export interface User extends z.infer<typeof User> {
|
|
14
15
|
preferences: Preferences;
|
|
15
16
|
}
|
|
17
|
+
export interface UserInternal extends User {
|
|
18
|
+
isSuspended: boolean;
|
|
19
|
+
/** Tags are internal, roles are public */
|
|
20
|
+
tags: string[];
|
|
21
|
+
}
|
|
16
22
|
export declare const userPublicFields: ["id", "image", "name", "registeredAt", "roles"];
|
|
17
23
|
type UserPublicField = (typeof userPublicFields)[number];
|
|
18
24
|
export interface UserPublic extends Pick<User, UserPublicField> {
|
|
19
25
|
}
|
|
20
|
-
export declare const userProtectedFields: ["email", "emailVerified", "preferences"];
|
|
26
|
+
export declare const userProtectedFields: ["email", "emailVerified", "preferences", "isAdmin"];
|
|
21
27
|
export declare const UserChangeable: z.ZodObject<{
|
|
22
28
|
email: z.ZodOptional<z.ZodEmail>;
|
|
23
29
|
name: z.ZodOptional<z.ZodString>;
|
package/dist/user.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
2
|
import { PasskeyRegistration } from './passkeys.js';
|
|
3
|
+
import { colorHash } from './color.js';
|
|
3
4
|
export const User = z.object({
|
|
4
5
|
id: z.uuid(),
|
|
5
6
|
name: z.string().min(1, 'Name is required').max(255, 'Name is too long'),
|
|
@@ -9,9 +10,10 @@ export const User = z.object({
|
|
|
9
10
|
preferences: z.record(z.string(), z.any()),
|
|
10
11
|
roles: z.array(z.string()),
|
|
11
12
|
registeredAt: z.date(),
|
|
13
|
+
isAdmin: z.boolean(),
|
|
12
14
|
});
|
|
13
15
|
export const userPublicFields = ['id', 'image', 'name', 'registeredAt', 'roles'];
|
|
14
|
-
export const userProtectedFields = ['email', 'emailVerified', 'preferences'];
|
|
16
|
+
export const userProtectedFields = ['email', 'emailVerified', 'preferences', 'isAdmin'];
|
|
15
17
|
export const UserChangeable = User.pick({
|
|
16
18
|
name: true,
|
|
17
19
|
email: true,
|
|
@@ -21,17 +23,9 @@ export const UserChangeable = User.pick({
|
|
|
21
23
|
export function getUserImage(user) {
|
|
22
24
|
if (user.image)
|
|
23
25
|
return user.image;
|
|
24
|
-
user.name
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
color *= user.name.charCodeAt(i);
|
|
28
|
-
}
|
|
29
|
-
color &= 0xbfbfbf;
|
|
30
|
-
const r = (color >> 16) & 0xff;
|
|
31
|
-
const g = (color >> 8) & 0xff;
|
|
32
|
-
const b = color & 0xff;
|
|
33
|
-
return `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" style="background-color:rgb(${r},${g},${b});display:flex;align-items:center;justify-content:center;">
|
|
34
|
-
<text x="23" y="28" style="font-family:sans-serif;font-weight:bold;" fill="white">${user.name.replaceAll(/\W/g, '')[0]}</text>
|
|
26
|
+
const color = colorHash(user.name ?? '\0');
|
|
27
|
+
return `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" style="background-color:${color};display:flex;align-items:center;justify-content:center;">
|
|
28
|
+
<text x="23" y="28" style="font-family:sans-serif;font-weight:bold;" fill="white">${(user.name ?? '?').replaceAll(/\W/g, '')[0]}</text>
|
|
35
29
|
</svg>`.replaceAll(/[\t\n]/g, '');
|
|
36
30
|
}
|
|
37
31
|
export const UserRegistration = z.object({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"author": "James Prevett <axium@jamespre.dev>
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"author": "James Prevett <axium@jamespre.dev>",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
7
7
|
"url": "https://github.com/sponsors/james-pre"
|