includio-cms 0.6.1 → 0.7.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/CHANGELOG.md +29 -0
- package/ROADMAP.md +10 -2
- package/dist/admin/api/accept-invite.js +2 -2
- package/dist/admin/auth-client.d.ts +2122 -2122
- package/dist/admin/client/admin/admin-layout.svelte +18 -4
- package/dist/admin/components/fields/blocks-field.svelte +3 -3
- package/dist/admin/components/fields/image-field.svelte +2 -2
- package/dist/admin/components/fields/media-field.svelte +4 -4
- package/dist/admin/components/media/file/file-miniature.svelte +6 -6
- package/dist/admin/context/remotes.d.ts +1 -1
- package/dist/admin/context/remotes.js +0 -1
- package/dist/admin/styles/admin.css +1 -1
- package/dist/components/ui/command/command.svelte.d.ts +1 -1
- package/dist/components/ui/input/input.svelte +2 -2
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group.svelte +1 -1
- package/dist/components/ui/select/select-trigger.svelte +1 -1
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/dist/components/ui/textarea/textarea.svelte +1 -1
- package/dist/core/cms.d.ts +5 -3
- package/dist/core/cms.js +41 -2
- package/dist/db-postgres/index.d.ts +6 -1
- package/dist/db-postgres/index.js +1 -0
- package/dist/server/auth.d.ts +1 -1358
- package/dist/server/auth.js +3 -23
- package/dist/sveltekit/server/handle.js +21 -2
- package/dist/types/cms.d.ts +6 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/updates/0.6.1/index.d.ts +2 -0
- package/dist/updates/0.6.1/index.js +9 -0
- package/dist/updates/0.6.2/index.d.ts +2 -0
- package/dist/updates/0.6.2/index.js +8 -0
- package/dist/updates/0.7.0/index.d.ts +2 -0
- package/dist/updates/0.7.0/index.js +16 -0
- package/dist/updates/index.js +4 -1
- package/package.json +5 -1
|
@@ -4,16 +4,30 @@
|
|
|
4
4
|
import { Toaster } from '../../../components/ui/sonner/index.js';
|
|
5
5
|
import { MediaSort, setMediaSort } from '../../state/media-sort.svelte.js';
|
|
6
6
|
import AdminPreloader from './admin-preloader.svelte';
|
|
7
|
+
import { onMount } from 'svelte';
|
|
7
8
|
|
|
8
9
|
setMediaSort(new MediaSort());
|
|
9
10
|
|
|
10
11
|
let { children }: { children: Snippet } = $props();
|
|
12
|
+
|
|
13
|
+
let mounted = $state(false);
|
|
14
|
+
onMount(() => {
|
|
15
|
+
mounted = true;
|
|
16
|
+
});
|
|
11
17
|
</script>
|
|
12
18
|
|
|
13
19
|
<Toaster richColors />
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
{#if !mounted}
|
|
22
|
+
<AdminPreloader />
|
|
23
|
+
{/if}
|
|
24
|
+
|
|
25
|
+
<div class:hidden={!mounted}>
|
|
26
|
+
<svelte:boundary>
|
|
27
|
+
{#snippet failed(error)}
|
|
28
|
+
<pre style="color:red;padding:2rem">{error?.message ?? error}</pre>
|
|
29
|
+
{/snippet}
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
</svelte:boundary>
|
|
31
|
+
{@render children()}
|
|
32
|
+
</svelte:boundary>
|
|
33
|
+
</div>
|
|
@@ -299,9 +299,9 @@
|
|
|
299
299
|
{@const objectField = field.of.find((option) => option.slug === item.slug)}
|
|
300
300
|
|
|
301
301
|
{#if objectField}
|
|
302
|
-
<Accordion.Item value={index.toString()} class="border-0" data-depth={depth + 1}>
|
|
302
|
+
<Accordion.Item value={index.toString()} class="overflow-hidden rounded-md border-0" data-depth={depth + 1}>
|
|
303
303
|
<Accordion.Trigger
|
|
304
|
-
class="items-center border px-4 text-base font-normal data-[state=open]:rounded-b-none"
|
|
304
|
+
class="items-center rounded-md border bg-card/60 px-4 text-base font-normal data-[state=open]:rounded-b-none"
|
|
305
305
|
>
|
|
306
306
|
<div class="flex grow items-center justify-between gap-4">
|
|
307
307
|
<div class="flex items-center gap-4">
|
|
@@ -373,7 +373,7 @@
|
|
|
373
373
|
{/if}
|
|
374
374
|
</div>
|
|
375
375
|
</Accordion.Trigger>
|
|
376
|
-
<Accordion.Content class="space-y-4 rounded-b-md border border-t-0 p-4">
|
|
376
|
+
<Accordion.Content class="space-y-4 rounded-b-md border border-t-0 bg-card/80 p-4">
|
|
377
377
|
{@const itemPath = joinPath(path, index)}
|
|
378
378
|
<div data-field-path={itemPath}>
|
|
379
379
|
<FieldRenderer
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
</button>
|
|
115
115
|
</div>
|
|
116
116
|
<!-- Counter -->
|
|
117
|
-
<div class="absolute top-2 right-2 rounded-full bg-
|
|
117
|
+
<div class="absolute top-2 right-2 rounded-full bg-plum-darker/60 px-2 py-0.5 text-xs font-medium text-white backdrop-blur">
|
|
118
118
|
{currentIndex + 1} / {valueArr.length}
|
|
119
119
|
</div>
|
|
120
120
|
{/if}
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
|
|
123
123
|
<!-- Stały gradient z przyciskami na dole -->
|
|
124
124
|
<div
|
|
125
|
-
class="absolute inset-x-0 bottom-0 flex items-center justify-between gap-2 bg-gradient-to-t from-
|
|
125
|
+
class="absolute inset-x-0 bottom-0 flex items-center justify-between gap-2 bg-gradient-to-t from-plum-darker/50 to-transparent p-2 pt-8"
|
|
126
126
|
>
|
|
127
127
|
<div class="flex gap-1">
|
|
128
128
|
<Button {...props} size="sm" variant="secondary" class="h-8">
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
>
|
|
227
227
|
<FileMiniature {file} />
|
|
228
228
|
</button>
|
|
229
|
-
<div class="absolute inset-x-0 bottom-0 flex items-center justify-between gap-2 bg-gradient-to-t from-
|
|
229
|
+
<div class="absolute inset-x-0 bottom-0 flex items-center justify-between gap-2 bg-gradient-to-t from-plum-darker/50 to-transparent p-2 pt-8">
|
|
230
230
|
<Button {...props} size="sm" variant="secondary" class="h-8">
|
|
231
231
|
<Plus class="h-4 w-4" />
|
|
232
232
|
{lang[interfaceLanguage.current].change}
|
|
@@ -279,12 +279,12 @@
|
|
|
279
279
|
<ChevronRight class="h-5 w-5" />
|
|
280
280
|
</button>
|
|
281
281
|
</div>
|
|
282
|
-
<div class="absolute top-2 right-2 rounded-full bg-
|
|
282
|
+
<div class="absolute top-2 right-2 rounded-full bg-plum-darker/60 px-2 py-0.5 text-xs font-medium text-white backdrop-blur">
|
|
283
283
|
{currentIndex + 1} / {valueArr.length}
|
|
284
284
|
</div>
|
|
285
285
|
{/if}
|
|
286
286
|
|
|
287
|
-
<div class="absolute inset-x-0 bottom-0 flex items-center justify-between gap-2 bg-gradient-to-t from-
|
|
287
|
+
<div class="absolute inset-x-0 bottom-0 flex items-center justify-between gap-2 bg-gradient-to-t from-plum-darker/50 to-transparent p-2 pt-8">
|
|
288
288
|
<Button {...props} size="sm" variant="secondary" class="h-8">
|
|
289
289
|
<Plus class="h-4 w-4" />
|
|
290
290
|
{lang[interfaceLanguage.current].change}
|
|
@@ -306,7 +306,7 @@
|
|
|
306
306
|
<button
|
|
307
307
|
{...props}
|
|
308
308
|
type="button"
|
|
309
|
-
class="flex aspect-video max-w-64 w-full flex-col items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-
|
|
309
|
+
class="flex aspect-video max-w-64 w-full flex-col items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-border bg-card/80 p-6 transition-colors hover:border-primary/50 hover:bg-card"
|
|
310
310
|
>
|
|
311
311
|
<div class="rounded-full bg-muted p-3">
|
|
312
312
|
<Plus class="h-6 w-6 text-muted-foreground" />
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
{#if mode === 'thumb'}
|
|
19
19
|
<!-- Thumb mode: just the media content for 120px thumb area -->
|
|
20
20
|
{#if file.type === 'image'}
|
|
21
|
-
<img class="pointer-events-none size-full object-contain" src={file.url} alt={file.name} />
|
|
21
|
+
<img class="pointer-events-none size-full object-contain m-0" src={file.url} alt={file.name} />
|
|
22
22
|
{:else if file.type === 'video'}
|
|
23
23
|
{#if file.thumbnailUrl}
|
|
24
|
-
<img class="pointer-events-none size-full object-contain" src={file.thumbnailUrl} alt={file.name} />
|
|
24
|
+
<img class="pointer-events-none size-full object-contain m-0" src={file.thumbnailUrl} alt={file.name} />
|
|
25
25
|
{/if}
|
|
26
26
|
<div class="ml-video-overlay">
|
|
27
27
|
<div class="ml-video-play">
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
<div class="absolute top-1 right-1 rounded-sm bg-background/80 backdrop-blur-sm p-0.5">
|
|
49
49
|
<Photo class="text-muted-foreground" />
|
|
50
50
|
</div>
|
|
51
|
-
<img class="pointer-events-none size-full object-contain" src={file.url} alt={file.name} />
|
|
51
|
+
<img class="pointer-events-none size-full object-contain m-0" src={file.url} alt={file.name} />
|
|
52
52
|
{:else if file.type === 'video'}
|
|
53
53
|
<div class="absolute top-1 right-1 rounded-sm bg-background/80 backdrop-blur-sm p-0.5">
|
|
54
54
|
<Video class="text-muted-foreground" />
|
|
55
55
|
</div>
|
|
56
|
-
<img class="pointer-events-none size-full object-contain" src={file.thumbnailUrl} alt={file.name} />
|
|
56
|
+
<img class="pointer-events-none size-full object-contain m-0" src={file.thumbnailUrl} alt={file.name} />
|
|
57
57
|
{:else if file.type === 'audio'}
|
|
58
58
|
<div class="absolute top-1 right-1 rounded-sm bg-background/80 backdrop-blur-sm p-0.5">
|
|
59
59
|
<Music class="text-muted-foreground" />
|
|
@@ -65,12 +65,12 @@
|
|
|
65
65
|
<div class="absolute top-1 right-1 rounded-sm bg-background/80 backdrop-blur-sm p-0.5">
|
|
66
66
|
<Pdf class="text-muted-foreground" />
|
|
67
67
|
</div>
|
|
68
|
-
<Pdf class="pointer-events-none size-full object-contain" />
|
|
68
|
+
<Pdf class="pointer-events-none size-full object-contain m-0" />
|
|
69
69
|
{:else}
|
|
70
70
|
<div class="absolute top-1 right-1 rounded-sm bg-background/80 backdrop-blur-sm p-0.5">
|
|
71
71
|
<File class="text-muted-foreground" />
|
|
72
72
|
</div>
|
|
73
|
-
<File class="pointer-events-none size-full object-contain" />
|
|
73
|
+
<File class="pointer-events-none size-full object-contain m-0" />
|
|
74
74
|
{/if}
|
|
75
75
|
</div>
|
|
76
76
|
{/if}
|
|
@@ -650,7 +650,7 @@ div[data-collapsible]:not([data-collapsible='icon'])
|
|
|
650
650
|
|
|
651
651
|
/* Checkered transparency background */
|
|
652
652
|
.ml-checkered-bg {
|
|
653
|
-
background: repeating-conic-gradient(
|
|
653
|
+
background: repeating-conic-gradient(#E2DFF0 0% 25%, #F4F2FA 0% 50%) 50% / 16px 16px;
|
|
654
654
|
}
|
|
655
655
|
|
|
656
656
|
/* Staggered fadeUp animation for file cards */
|
|
@@ -3,6 +3,6 @@ export type CommandRootApi = CommandPrimitive.Root;
|
|
|
3
3
|
type $$ComponentProps = CommandPrimitive.RootProps & {
|
|
4
4
|
api?: CommandRootApi | null;
|
|
5
5
|
};
|
|
6
|
-
declare const Command: import("svelte").Component<$$ComponentProps, {}, "
|
|
6
|
+
declare const Command: import("svelte").Component<$$ComponentProps, {}, "ref" | "value" | "api">;
|
|
7
7
|
type Command = ReturnType<typeof Command>;
|
|
8
8
|
export default Command;
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
bind:this={ref}
|
|
26
26
|
data-slot={dataSlot}
|
|
27
27
|
class={cn(
|
|
28
|
-
"selection:bg-primary selection:text-primary-foreground border-input ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-
|
|
28
|
+
"selection:bg-primary selection:text-primary-foreground border-input ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-card px-3 pt-1.5 text-sm font-medium outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50",
|
|
29
29
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
30
30
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
31
31
|
className
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
bind:this={ref}
|
|
41
41
|
data-slot={dataSlot}
|
|
42
42
|
class={cn(
|
|
43
|
-
"border-input bg-
|
|
43
|
+
"border-input bg-card selection:bg-primary selection:text-primary-foreground ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
44
44
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
45
45
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
46
46
|
className
|
|
@@ -2,7 +2,7 @@ declare const InputGroupInput: import("svelte").Component<(Omit<import("svelte/e
|
|
|
2
2
|
type: "file";
|
|
3
3
|
files?: FileList;
|
|
4
4
|
} | {
|
|
5
|
-
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "
|
|
5
|
+
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "password" | "hidden" | "reset" | "time" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
|
|
6
6
|
files?: undefined;
|
|
7
7
|
})) & {
|
|
8
8
|
ref?: HTMLElement | null | undefined;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
data-slot="input-group"
|
|
16
16
|
role="group"
|
|
17
17
|
class={cn(
|
|
18
|
-
"group/input-group border-input
|
|
18
|
+
"group/input-group border-input bg-card shadow-xs relative flex w-full items-center rounded-md border outline-none transition-[color,box-shadow]",
|
|
19
19
|
"h-9 has-[>textarea]:h-auto",
|
|
20
20
|
|
|
21
21
|
// Variants based on alignment.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
data-slot="select-trigger"
|
|
20
20
|
data-size={size}
|
|
21
21
|
class={cn(
|
|
22
|
-
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive
|
|
22
|
+
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shadow-xs flex w-fit select-none items-center justify-between gap-2 whitespace-nowrap rounded-md border bg-card px-3 py-2 text-sm outline-none transition-[color,box-shadow] focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
23
23
|
className
|
|
24
24
|
)}
|
|
25
25
|
{...restProps}
|
|
@@ -2,7 +2,7 @@ declare const SidebarInput: import("svelte").Component<(Omit<import("svelte/elem
|
|
|
2
2
|
type: "file";
|
|
3
3
|
files?: FileList;
|
|
4
4
|
} | {
|
|
5
|
-
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "
|
|
5
|
+
type?: "number" | "image" | "url" | "text" | "date" | "radio" | "color" | "button" | "checkbox" | "search" | (string & {}) | "email" | "password" | "hidden" | "reset" | "time" | "submit" | "tel" | "datetime-local" | "month" | "range" | "week";
|
|
6
6
|
files?: undefined;
|
|
7
7
|
})) & {
|
|
8
8
|
ref?: HTMLElement | null | undefined;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
bind:this={ref}
|
|
16
16
|
data-slot={dataSlot}
|
|
17
17
|
class={cn(
|
|
18
|
-
"border-input placeholder:text-muted-foreground shadow-xs field-sizing-content flex min-h-16 w-full rounded-md border bg-
|
|
18
|
+
"border-input placeholder:text-muted-foreground shadow-xs field-sizing-content flex min-h-16 w-full rounded-md border bg-card px-3 py-2 text-base outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
19
19
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
20
20
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
21
21
|
className
|
package/dist/core/cms.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from '../types/adapters/db.js';
|
|
2
2
|
import type { FilesAdapter } from '../types/adapters/files.js';
|
|
3
|
-
import type { ApiKeyConfig, CMSConfig, ICMS, MediaConfig } from '../types/cms.js';
|
|
3
|
+
import type { ApiKeyConfig, AuthConfig, CMSConfig, ICMS, MediaConfig } from '../types/cms.js';
|
|
4
4
|
import type { CollectionConfigWithType } from '../types/collections.js';
|
|
5
5
|
import type { Language } from '../types/languages.js';
|
|
6
6
|
import type { SingleConfigWithType } from '../types/singles.js';
|
|
@@ -8,14 +8,15 @@ import type { PluginConfig } from '../types/plugins.js';
|
|
|
8
8
|
import type { FormConfig } from '../types/forms.js';
|
|
9
9
|
import type { AIAdapter } from '../types/adapters/ai.js';
|
|
10
10
|
import type { EmailAdapter } from '../types/adapters/email.js';
|
|
11
|
-
import
|
|
11
|
+
import { betterAuth } from 'better-auth';
|
|
12
12
|
export declare class CMS implements ICMS {
|
|
13
13
|
private config;
|
|
14
14
|
databaseAdapter: DatabaseAdapter;
|
|
15
15
|
filesAdapter: FilesAdapter;
|
|
16
16
|
emailAdapter: EmailAdapter | null;
|
|
17
|
-
|
|
17
|
+
authConfig: AuthConfig | null;
|
|
18
18
|
aiAdapter: AIAdapter | null;
|
|
19
|
+
private _betterAuth;
|
|
19
20
|
collections: Record<string, CollectionConfigWithType>;
|
|
20
21
|
singles: Record<string, SingleConfigWithType>;
|
|
21
22
|
forms: Record<string, FormConfig>;
|
|
@@ -24,6 +25,7 @@ export declare class CMS implements ICMS {
|
|
|
24
25
|
plugins: PluginConfig[];
|
|
25
26
|
apiKeys: ApiKeyConfig[];
|
|
26
27
|
constructor(config: CMSConfig);
|
|
28
|
+
get auth(): ReturnType<typeof betterAuth>;
|
|
27
29
|
getBySlug(slug: string): CollectionConfigWithType | SingleConfigWithType;
|
|
28
30
|
getFormBySlug(slug: string): FormConfig;
|
|
29
31
|
}
|
package/dist/core/cms.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { betterAuth } from 'better-auth';
|
|
2
|
+
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
|
|
3
|
+
import { admin } from 'better-auth/plugins';
|
|
4
|
+
import { resetPasswordEmailTemplate } from '../admin/email/reset-password-template.js';
|
|
5
|
+
import * as authSchema from '../server/db/schema/auth-schema.js';
|
|
1
6
|
export class CMS {
|
|
2
7
|
config;
|
|
3
8
|
databaseAdapter;
|
|
4
9
|
filesAdapter;
|
|
5
10
|
emailAdapter;
|
|
6
|
-
|
|
11
|
+
authConfig;
|
|
7
12
|
aiAdapter = null;
|
|
13
|
+
_betterAuth = null;
|
|
8
14
|
collections;
|
|
9
15
|
singles;
|
|
10
16
|
forms;
|
|
@@ -17,7 +23,7 @@ export class CMS {
|
|
|
17
23
|
this.databaseAdapter = config.db;
|
|
18
24
|
this.filesAdapter = config.files;
|
|
19
25
|
this.emailAdapter = config.email ?? null;
|
|
20
|
-
this.
|
|
26
|
+
this.authConfig = config.auth ?? null;
|
|
21
27
|
this.aiAdapter = config.ai || null;
|
|
22
28
|
this.mediaConfig = config.media || {};
|
|
23
29
|
this.collections = {};
|
|
@@ -46,6 +52,39 @@ export class CMS {
|
|
|
46
52
|
this.plugins = config.plugins;
|
|
47
53
|
}
|
|
48
54
|
}
|
|
55
|
+
get auth() {
|
|
56
|
+
if (!this._betterAuth) {
|
|
57
|
+
if (!this.authConfig) {
|
|
58
|
+
throw new Error('Auth not configured. Add `auth` to your CMS config.');
|
|
59
|
+
}
|
|
60
|
+
const drizzleDb = this.databaseAdapter._drizzle;
|
|
61
|
+
if (!drizzleDb) {
|
|
62
|
+
throw new Error('Database adapter does not expose a drizzle instance (_drizzle).');
|
|
63
|
+
}
|
|
64
|
+
const emailAdapter = this.emailAdapter;
|
|
65
|
+
this._betterAuth = betterAuth({
|
|
66
|
+
database: drizzleAdapter(drizzleDb, { provider: 'pg', schema: authSchema }),
|
|
67
|
+
secret: this.authConfig.secret,
|
|
68
|
+
baseURL: this.authConfig.baseURL,
|
|
69
|
+
emailAndPassword: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
async sendResetPassword({ user, url }, request) {
|
|
72
|
+
if (!emailAdapter) {
|
|
73
|
+
throw new Error('Email adapter not configured');
|
|
74
|
+
}
|
|
75
|
+
const lang = request?.headers.get('Accept-Language')?.startsWith('pl') ? 'pl' : 'en';
|
|
76
|
+
const { subject, html } = resetPasswordEmailTemplate({
|
|
77
|
+
resetUrl: url,
|
|
78
|
+
lang
|
|
79
|
+
});
|
|
80
|
+
await emailAdapter.sendMail({ to: [user.email], subject, html });
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
plugins: [admin()]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return this._betterAuth;
|
|
87
|
+
}
|
|
49
88
|
getBySlug(slug) {
|
|
50
89
|
const config = this.collections[slug] || this.singles[slug];
|
|
51
90
|
if (!config) {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
1
2
|
import type { Config } from './types.js';
|
|
3
|
+
import * as schema from './schema/index.js';
|
|
2
4
|
import type { DatabaseAdapter } from '../types/adapters/db.js';
|
|
3
|
-
export
|
|
5
|
+
export type DatabaseAdapterWithDrizzle = DatabaseAdapter & {
|
|
6
|
+
_drizzle: ReturnType<typeof drizzle<typeof schema>>;
|
|
7
|
+
};
|
|
8
|
+
export declare function pg(config: Config): DatabaseAdapterWithDrizzle;
|
|
@@ -88,6 +88,7 @@ export function pg(config) {
|
|
|
88
88
|
const client = postgres(config.databaseUrl);
|
|
89
89
|
const db = drizzle(client, { schema });
|
|
90
90
|
return {
|
|
91
|
+
_drizzle: db,
|
|
91
92
|
createEntry: async (data) => {
|
|
92
93
|
return db.transaction(async (tx) => {
|
|
93
94
|
const [newEntry] = await tx.insert(schema.entriesTable).values(data).returning();
|