mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: improve oidc,fix truncate
This commit is contained in:
@@ -40,6 +40,10 @@ AUTH_OIDC_JWKS_ENDPOINT=""
|
||||
AUTH_OIDC_PKCE=true
|
||||
ALLOWED_GROUP="admin"
|
||||
|
||||
# If true, the user's role will be updated from OIDC claims on every login.
|
||||
# If false, the role is only set when the user is first created.
|
||||
AUTH_SYNC_OIDC_ROLES_ON_LOGIN=true
|
||||
|
||||
AUTH_EMAIL_PASSWORD_ENABLED=true
|
||||
AUTH_SIGNUP_ENABLED=true
|
||||
AUTH_PASSKEY_ENABLED=true
|
||||
|
||||
+2
-2
@@ -26,5 +26,5 @@ keywords:
|
||||
- web-ui
|
||||
- agent
|
||||
license: Apache-2.0
|
||||
version: 1.2.8-rc.1
|
||||
date-released: "2026-02-17"
|
||||
version: 1.2.9
|
||||
date-released: "2026-02-19"
|
||||
|
||||
@@ -33,14 +33,14 @@
|
||||
<strong>
|
||||
<a href="https://portabase.io">Website</a> •
|
||||
<a href="https://portabase.io/docs">Documentation</a> •
|
||||
<a href="https://www.youtube.com/watch?v=hvLbX5LN1UE">Demo</a> •
|
||||
<a href="https://www.youtube.com/watch?v=nSTzT27GgAg">Demo</a> •
|
||||
<a href="https://portabase.io/docs/dashboard/setup">Installation</a> •
|
||||
<a href="https://github.com/Portabase/portabase/issues/new?labels=bug&template=bug-report---.md">Report Bug</a> •
|
||||
<a href="https://github.com/Portabase/portabase/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
|
||||
</strong>
|
||||
</p>
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import {PageParams} from "@/types/next";
|
||||
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
|
||||
import {db} from "@/db";
|
||||
import {SettingsEmailSection} from "@/components/wrappers/dashboard/admin/settings/email/settings-email-section";
|
||||
import {notFound} from "next/navigation";
|
||||
|
||||
export default async function RoutePage(props: PageParams<{}>) {
|
||||
|
||||
const settings = await db.query.setting.findFirst({
|
||||
where: (fields, {eq}) => eq(fields.name, "system"),
|
||||
});
|
||||
|
||||
if (!settings) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<PageHeader className="flex flex-col">
|
||||
<div className="flex justify-between">
|
||||
<PageTitle className="mb-3">System email</PageTitle>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<PageContent className="flex flex-col gap-5">
|
||||
<SettingsEmailSection settings={settings}/>
|
||||
</PageContent>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import {PageParams} from "@/types/next";
|
||||
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
|
||||
import {SettingsStorageSection} from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage-section";
|
||||
import {db} from "@/db";
|
||||
import {notFound} from "next/navigation";
|
||||
|
||||
export default async function RoutePage(props: PageParams<{}>) {
|
||||
|
||||
const settings = await db.query.setting.findFirst({
|
||||
where: (fields, {eq}) => eq(fields.name, "system"),
|
||||
});
|
||||
|
||||
if (!settings) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<PageHeader className="flex flex-col">
|
||||
<div className="flex justify-between">
|
||||
<PageTitle className="mb-3">System storage</PageTitle>
|
||||
</div>
|
||||
</PageHeader>
|
||||
<PageContent className="flex flex-col gap-5">
|
||||
<SettingsStorageSection settings={settings}/>
|
||||
</PageContent>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -35,8 +35,6 @@ export default async function RoutePage(
|
||||
|
||||
const edgeKey = await generateEdgeKey(getServerUrl(), agent.id);
|
||||
|
||||
console.log("edgeKey", edgeKey);
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<div className="justify-between gap-2 sm:flex">
|
||||
|
||||
@@ -30,6 +30,7 @@ services:
|
||||
- "localhost:host-gateway"
|
||||
volumes:
|
||||
- ./private/uploads/tmp:/data/uploads/tmp
|
||||
user: "1000:1000"
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:latest
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "portabase",
|
||||
"version": "1.2.8-rc.1",
|
||||
"version": "1.2.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack -p 8887",
|
||||
|
||||
@@ -208,12 +208,14 @@ type UseZodFormProps<Z extends ZodSchema> = Exclude<
|
||||
schema: Z;
|
||||
};
|
||||
|
||||
|
||||
const useZodForm = <Z extends ZodSchema>({
|
||||
schema,
|
||||
...formProps
|
||||
}: UseZodFormProps<Z>) =>
|
||||
useForm({
|
||||
...formProps,
|
||||
// @ts-ignore
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
|
||||
+2
-1
@@ -51,7 +51,8 @@ export const ChannelTestButton = ({channel, organizationId, kind}: NotifierTestC
|
||||
toast.success("Successfully connected to storage channel");
|
||||
} else {
|
||||
console.error(result);
|
||||
toast.error("An error occurred while testing the storage channel, check your configuration");
|
||||
const responseText = result.response ? `(${result.response})` : "";
|
||||
toast.error(`An error occurred while testing the storage channel, check your configuration ${responseText}`);
|
||||
}
|
||||
} else {
|
||||
toast.error("Not yet supported");
|
||||
|
||||
+51
-4
@@ -4,7 +4,15 @@ import {Info} from "lucide-react";
|
||||
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import {Form, FormControl, FormField, FormItem, FormLabel, FormMessage, useZodForm} from "@/components/ui/form";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
useZodForm
|
||||
} from "@/components/ui/form";
|
||||
import {StorageChannelWith} from "@/db/schema/12_storage-channel";
|
||||
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
@@ -18,6 +26,7 @@ import {
|
||||
} from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage.action";
|
||||
import {toast} from "sonner";
|
||||
import {Switch} from "@/components/ui/switch";
|
||||
import {downloadMasterKeyAction} from "@/features/keys/keys.action";
|
||||
|
||||
export type SettingsStorageSectionProps = {
|
||||
settings: Setting;
|
||||
@@ -50,6 +59,34 @@ export const SettingsStorageSection = ({settings, storageChannels}: SettingsStor
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const handleDownload = async () => {
|
||||
const result = await downloadMasterKeyAction();
|
||||
|
||||
if (!result?.success) {
|
||||
toast.error(result?.message ?? "Download failed");
|
||||
return;
|
||||
}
|
||||
|
||||
const byteCharacters = atob(result.data as string);
|
||||
const byteNumbers = new Array(byteCharacters.length)
|
||||
.fill(null)
|
||||
.map((_, i) => byteCharacters.charCodeAt(i));
|
||||
|
||||
const blob = new Blob([new Uint8Array(byteNumbers)], {
|
||||
type: "application/octet-stream",
|
||||
});
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "master_key.bin";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<Alert className="mt-3">
|
||||
@@ -59,7 +96,7 @@ export const SettingsStorageSection = ({settings, storageChannels}: SettingsStor
|
||||
The default storage channel will be used by default to store your backups if no storage policy is
|
||||
configured at the database level. </AlertDescription>
|
||||
</Alert>
|
||||
<div className="flex flex-col h-full py-4 ">
|
||||
<div className="flex flex-col h-full py-4 gap-3">
|
||||
<Form
|
||||
className="space-y-4"
|
||||
form={form}
|
||||
@@ -96,8 +133,6 @@ export const SettingsStorageSection = ({settings, storageChannels}: SettingsStor
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="encryption"
|
||||
@@ -120,6 +155,18 @@ export const SettingsStorageSection = ({settings, storageChannels}: SettingsStor
|
||||
Confirm
|
||||
</ButtonWithLoading>
|
||||
</Form>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-sm font-medium ">
|
||||
Download instance <button
|
||||
type="button"
|
||||
onClick={handleDownload}
|
||||
className="text-sm hover:text-muted-foreground underline underline-offset-4 transition-colors w-fit"
|
||||
>
|
||||
Master Key
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
+94
-111
@@ -1,14 +1,6 @@
|
||||
"use client";
|
||||
import { Backup, BackupWith, Restoration } from "@/db/schema/07_database";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
//@ts-ignore
|
||||
import "swiper/css";
|
||||
import "swiper/css/pagination";
|
||||
import { Pagination, Mousewheel } from "swiper/modules";
|
||||
import {
|
||||
DatabaseActionKind,
|
||||
useBackupModal,
|
||||
} from "@/components/wrappers/dashboard/database/backup/backup-modal-context";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -18,33 +10,36 @@ import {
|
||||
FormMessage,
|
||||
useZodForm,
|
||||
} from "@/components/ui/form";
|
||||
import {
|
||||
BackupActionsSchema,
|
||||
BackupActionsType,
|
||||
} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions.schema";
|
||||
import { ButtonWithLoading } from "@/components/wrappers/common/button/button-with-loading";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { BackupStorageWith } from "@/db/schema/14_storage-backup";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { ButtonWithLoading } from "@/components/wrappers/common/button/button-with-loading";
|
||||
import { getChannelIcon } from "@/components/wrappers/dashboard/admin/channels/helpers/common";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
getStatusColor,
|
||||
getStatusIcon,
|
||||
} from "@/components/wrappers/dashboard/admin/notifications/logs/columns";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
createRestorationBackupAction,
|
||||
deleteBackupAction,
|
||||
deleteBackupStorageAction,
|
||||
downloadBackupAction,
|
||||
} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions.action";
|
||||
import { toast } from "sonner";
|
||||
import { SafeActionResult } from "next-safe-action";
|
||||
import {
|
||||
BackupActionsSchema,
|
||||
BackupActionsType,
|
||||
} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions.schema";
|
||||
import {
|
||||
DatabaseActionKind,
|
||||
useBackupModal,
|
||||
} from "@/components/wrappers/dashboard/database/backup/backup-modal-context";
|
||||
import { Backup, BackupWith, Restoration } from "@/db/schema/07_database";
|
||||
import { BackupStorageWith } from "@/db/schema/14_storage-backup";
|
||||
import { ServerActionResult } from "@/types/action-type";
|
||||
import { ZodString } from "zod";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { AlertCircleIcon } from "lucide-react";
|
||||
import { SafeActionResult } from "next-safe-action";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { toast } from "sonner";
|
||||
import { ZodString } from "zod";
|
||||
|
||||
type BackupActionsFormProps = {
|
||||
backup: BackupWith;
|
||||
@@ -161,7 +156,7 @@ export const BackupActionsForm = ({
|
||||
<TooltipProvider>
|
||||
<Form
|
||||
form={form}
|
||||
className="flex flex-col gap-4 mb-1"
|
||||
className="flex flex-col gap-4 mb-1 min-w-0"
|
||||
onSubmit={async (values) => {
|
||||
await mutation.mutateAsync(values);
|
||||
}}
|
||||
@@ -174,94 +169,82 @@ export const BackupActionsForm = ({
|
||||
<FormItem>
|
||||
<FormLabel>Choose a storage backup</FormLabel>
|
||||
<FormControl>
|
||||
<div style={{ height: "250px" }}>
|
||||
<Swiper
|
||||
direction="vertical"
|
||||
slidesPerView={3.5}
|
||||
spaceBetween={10}
|
||||
// pagination={{ clickable: true }}
|
||||
mousewheel={{ releaseOnEdges: true, forceToAxis: true }}
|
||||
modules={[Pagination, Mousewheel]}
|
||||
className="mySwiper"
|
||||
style={{ height: "100%" }}
|
||||
>
|
||||
{filteredBackupStorages.map(
|
||||
(storage: BackupStorageWith) => (
|
||||
<SwiperSlide key={storage.id}>
|
||||
<button
|
||||
disabled={
|
||||
action !== "delete" &&
|
||||
storage.status.toLowerCase() !== "success"
|
||||
}
|
||||
type="button"
|
||||
onClick={() => field.onChange(storage.id)}
|
||||
className={`w-full h-full flex items-start gap-3 p-4 rounded-lg border text-left transition-colors
|
||||
${
|
||||
field.value ===
|
||||
storage.id
|
||||
? "border-foreground bg-background"
|
||||
: "border-border bg-background" +
|
||||
(storage.status.toLowerCase() ===
|
||||
"success" ||
|
||||
action ===
|
||||
"delete"
|
||||
? " hover:border-muted-foreground"
|
||||
: "")
|
||||
}
|
||||
${storage.status.toLowerCase() !== "success" && action !== "delete" ? "opacity-50 cursor-not-allowed" : ""}`}
|
||||
>
|
||||
<div
|
||||
className={`mt-0.5 h-4 w-4 shrink-0 rounded-full border ${
|
||||
field.value === storage.id
|
||||
? "border-foreground"
|
||||
: "border-muted-foreground"
|
||||
} flex items-center justify-center`}
|
||||
<div
|
||||
className="flex flex-col gap-2.5 overflow-y-auto overflow-x-hidden w-full pr-1"
|
||||
style={{ maxHeight: "250px" }}
|
||||
>
|
||||
{filteredBackupStorages.map(
|
||||
(storage: BackupStorageWith) => (
|
||||
<button
|
||||
key={storage.id}
|
||||
disabled={
|
||||
action !== "delete" &&
|
||||
storage.status.toLowerCase() !== "success"
|
||||
}
|
||||
type="button"
|
||||
onClick={() => field.onChange(storage.id)}
|
||||
className={`w-full flex items-center gap-3 p-4 rounded-lg border text-left transition-colors
|
||||
${
|
||||
field.value === storage.id
|
||||
? "border-foreground bg-background"
|
||||
: "border-border bg-background" +
|
||||
(storage.status.toLowerCase() === "success" ||
|
||||
action === "delete"
|
||||
? " hover:border-muted-foreground"
|
||||
: "")
|
||||
}
|
||||
${
|
||||
storage.status.toLowerCase() !== "success" &&
|
||||
action !== "delete"
|
||||
? "opacity-50 cursor-not-allowed"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`h-4 w-4 shrink-0 rounded-full border ${
|
||||
field.value === storage.id
|
||||
? "border-foreground"
|
||||
: "border-muted-foreground"
|
||||
} flex items-center justify-center`}
|
||||
>
|
||||
{field.value === storage.id && (
|
||||
<div className="h-2 w-2 rounded-full bg-foreground" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0 flex items-center gap-2">
|
||||
<div className="shrink-0">
|
||||
{getChannelIcon(
|
||||
storage.storageChannel?.provider || "",
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h3 className="font-medium text-foreground truncate flex-1">
|
||||
{storage.storageChannel?.name}
|
||||
</h3>
|
||||
|
||||
{storage.storageChannel?.provider && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="text-xs font-mono shrink-0"
|
||||
>
|
||||
{field.value === storage.id && (
|
||||
<div className="h-2 w-2 rounded-full bg-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1 min-w-0 flex flex-col gap-1">
|
||||
<div className="flex items-center justify-between gap-2 min-w-0 w-full">
|
||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||
<div className="shrink-0">
|
||||
{getChannelIcon(
|
||||
storage.storageChannel?.provider ||
|
||||
"",
|
||||
)}
|
||||
</div>
|
||||
<h3 className="font-medium text-foreground truncate min-w-0">
|
||||
{storage.storageChannel?.name}
|
||||
</h3>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="text-xs font-mono shrink-0"
|
||||
>
|
||||
{storage.storageChannel?.provider}
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`gap-1.5 shrink-0 ${getStatusColor(storage.status)}`}
|
||||
>
|
||||
{getStatusIcon(
|
||||
storage.status === "success",
|
||||
)}
|
||||
<span className="capitalize">
|
||||
{storage.status.toUpperCase()}
|
||||
</span>
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</SwiperSlide>
|
||||
),
|
||||
) ?? <p>No storages available</p>}
|
||||
</Swiper>
|
||||
{storage.storageChannel.provider}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`gap-1.5 shrink-0 ${getStatusColor(storage.status)}`}
|
||||
>
|
||||
{getStatusIcon(storage.status === "success")}
|
||||
<span className="capitalize hidden sm:inline">
|
||||
{storage.status.toUpperCase()}
|
||||
</span>
|
||||
</Badge>
|
||||
</div>
|
||||
</button>
|
||||
),
|
||||
) ?? <p>No storages available</p>}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
|
||||
@@ -58,6 +58,8 @@ export const env = createEnv({
|
||||
AUTH_SIGNUP_ENABLED: z.string().optional().default("true"),
|
||||
AUTH_PASSKEY_ENABLED: z.string().optional().default("true"),
|
||||
|
||||
AUTH_SYNC_OIDC_ROLES_ON_LOGIN: z.enum(["true", "false"]).default("true"),
|
||||
|
||||
PRIVATE_PATH: z.string().optional(),
|
||||
},
|
||||
client: {
|
||||
@@ -107,6 +109,8 @@ export const env = createEnv({
|
||||
AUTH_SIGNUP_ENABLED: process.env.AUTH_SIGNUP_ENABLED,
|
||||
AUTH_PASSKEY_ENABLED: process.env.AUTH_PASSKEY_ENABLED,
|
||||
|
||||
AUTH_SYNC_OIDC_ROLES_ON_LOGIN: process.env.AUTH_SYNC_OIDC_ROLES_ON_LOGIN,
|
||||
|
||||
PRIVATE_PATH:
|
||||
process.env.PRIVATE_PATH || path.join(process.cwd(), "private"),
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use server"
|
||||
import fs from "node:fs";
|
||||
import {env} from "@/env.mjs";
|
||||
import path from "path";
|
||||
@@ -6,7 +7,7 @@ import path from "path";
|
||||
/**
|
||||
* Get Public server key content
|
||||
*/
|
||||
export function getPublicServerKeyContent() {
|
||||
export async function getPublicServerKeyContent() {
|
||||
try {
|
||||
const keyPath = path.join(env.PRIVATE_PATH, '/keys/server_public.pem')
|
||||
return fs.readFileSync(keyPath, "utf8");
|
||||
@@ -23,7 +24,7 @@ export function getPublicServerKeyContent() {
|
||||
/**
|
||||
* Get Master server key
|
||||
*/
|
||||
export function getMasterServerKeyContent() {
|
||||
export async function getMasterServerKeyContent() {
|
||||
try {
|
||||
const keyPath = path.join(env.PRIVATE_PATH, '/keys/master_key.bin')
|
||||
return fs.readFileSync(keyPath);
|
||||
@@ -35,3 +36,21 @@ export function getMasterServerKeyContent() {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function downloadMasterKeyAction() {
|
||||
try {
|
||||
const keyPath = path.join(env.PRIVATE_PATH, "keys/master_key.bin");
|
||||
const fileBuffer = fs.readFileSync(keyPath);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: fileBuffer.toString("base64"),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Unable to download master key",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -97,12 +97,14 @@ export async function dispatchStorage(
|
||||
}
|
||||
|
||||
|
||||
return await dispatchViaProvider(
|
||||
const dispatchResult = await dispatchViaProvider(
|
||||
channel.provider as StorageProviderKind,
|
||||
channel.config,
|
||||
input,
|
||||
);
|
||||
|
||||
console.log(dispatchResult);
|
||||
return dispatchResult;
|
||||
|
||||
} catch (err: any) {
|
||||
return {
|
||||
|
||||
@@ -18,7 +18,7 @@ async function getS3Client(config: S3Config) {
|
||||
region: config.region ?? "us-east-1",
|
||||
accessKey: config.accessKey,
|
||||
secretKey: config.secretKey,
|
||||
port: config.port ?? 443,
|
||||
port: config.port ? Number(config.port) : 443,
|
||||
useSSL: config.ssl ?? true,
|
||||
});
|
||||
}
|
||||
|
||||
+12
-4
@@ -161,6 +161,18 @@ export const auth = betterAuth({
|
||||
issuer: p.issuerUrl,
|
||||
})),
|
||||
provisionUser: async ({ user: usr, userInfo, provider }) => {
|
||||
const existingUser = await db.query.user.findFirst({
|
||||
where: eq(drizzleDb.schemas.user.email, usr.email),
|
||||
});
|
||||
|
||||
if (existingUser && existingUser.role === "superadmin") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingUser && env.AUTH_SYNC_OIDC_ROLES_ON_LOGIN === "false") {
|
||||
return;
|
||||
}
|
||||
|
||||
const providerId = provider.providerId;
|
||||
const oidcProvider = oidcProviders.find((p) => p.id === providerId);
|
||||
const allowedGroup = oidcProvider?.allowedGroup || env.ALLOWED_GROUP;
|
||||
@@ -225,10 +237,6 @@ export const auth = betterAuth({
|
||||
roleToAssign = "superadmin";
|
||||
}
|
||||
|
||||
const existingUser = await db.query.user.findFirst({
|
||||
where: eq(drizzleDb.schemas.user.email, usr.email),
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
await db
|
||||
.update(drizzleDb.schemas.user)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {getMasterServerKeyContent} from "@/features/keys/keys.action";
|
||||
|
||||
export async function generateEdgeKey(serverUrl: string, agentId: string): Promise<string> {
|
||||
const masterKey = getMasterServerKeyContent()
|
||||
const masterKey = await getMasterServerKeyContent()
|
||||
const edgeKeyData = {
|
||||
serverUrl,
|
||||
agentId,
|
||||
|
||||
Reference in New Issue
Block a user