Merge pull request 'feature/audit-log' (#6) from feature/audit-log into main
Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
@@ -33,10 +33,9 @@
|
||||
"@tanstack/react-router-ssr-query": "^1.131.7",
|
||||
"@tanstack/react-start": "^1.132.0",
|
||||
"@tanstack/router-plugin": "^1.132.0",
|
||||
"better-auth": "^1.4.7",
|
||||
"better-auth": "^1.4.10",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"i18next": "^25.7.3",
|
||||
"next-themes": "^0.4.6",
|
||||
"prisma": "^7.1.0",
|
||||
"radix-ui": "^1.4.3",
|
||||
|
||||
1157
pnpm-lock.yaml
generated
1157
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
16
prisma/migrations/20260108130508_audit/migration.sql
Normal file
16
prisma/migrations/20260108130508_audit/migration.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "audit" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"action" TEXT NOT NULL,
|
||||
"tableName" TEXT NOT NULL,
|
||||
"recordId" TEXT NOT NULL,
|
||||
"oldValue" TEXT,
|
||||
"newValue" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "audit_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "audit" ADD CONSTRAINT "audit_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -18,6 +18,7 @@ model User {
|
||||
updatedAt DateTime @updatedAt
|
||||
sessions Session[]
|
||||
accounts Account[]
|
||||
audit Audit[]
|
||||
|
||||
role String?
|
||||
banned Boolean? @default(false)
|
||||
@@ -142,3 +143,18 @@ model Setting {
|
||||
|
||||
@@map("setting")
|
||||
}
|
||||
|
||||
model Audit {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
action String
|
||||
tableName String
|
||||
recordId String
|
||||
oldValue String?
|
||||
newValue String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("audit")
|
||||
}
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
"locales": ["en", "vi"],
|
||||
"modules": [
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js",
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@latest/dist/index.js"
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
|
||||
],
|
||||
"plugin.inlang.messageFormat": {
|
||||
"pathPattern": "./messages/{locale}.json"
|
||||
},
|
||||
"plugin.inlang.i18next": {
|
||||
"pathPattern": "./messages/{locale}.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,17 +73,6 @@ export default function Header() {
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
{/* <Link
|
||||
to="/demo/start/ssr"
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="flex-1 flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
|
||||
activeProps={{
|
||||
className:
|
||||
'flex-1 flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',
|
||||
}}
|
||||
>
|
||||
<span className="font-medium">Start - SSR Demos</span>
|
||||
</Link> */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { authClient } from '@/lib/auth-client';
|
||||
import { m } from '@/paraglide/messages';
|
||||
import { KeyIcon } from '@phosphor-icons/react';
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import i18next from 'i18next';
|
||||
import { toast } from 'sonner';
|
||||
import z from 'zod';
|
||||
import { Button } from '../ui/button';
|
||||
@@ -72,9 +71,14 @@ const ChangePasswordForm = () => {
|
||||
},
|
||||
onError: (ctx) => {
|
||||
console.log(ctx.error.code);
|
||||
toast.error(i18next.t(`backend_${ctx.error.code}` as any), {
|
||||
richColors: true,
|
||||
});
|
||||
toast.error(
|
||||
(
|
||||
m[`backend_${ctx.error.code}` as keyof typeof m] as () => string
|
||||
)(),
|
||||
{
|
||||
richColors: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ProfileInput, profileUpdateSchema } from '@/service/profile.schema';
|
||||
import { UserCircleIcon } from '@phosphor-icons/react';
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import i18next from 'i18next';
|
||||
import { useRef } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { useAuth } from '../auth/auth-provider';
|
||||
@@ -67,9 +66,16 @@ const ProfileForm = () => {
|
||||
});
|
||||
},
|
||||
onError: (ctx) => {
|
||||
toast.error(i18next.t(`backend.${ctx.error.code}` as any), {
|
||||
richColors: true,
|
||||
});
|
||||
toast.error(
|
||||
(
|
||||
m[
|
||||
`backend_${ctx.error.code}` as keyof typeof m
|
||||
] as () => string
|
||||
)(),
|
||||
{
|
||||
richColors: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -27,10 +27,7 @@ const SettingsForm = () => {
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: updateAdminSettings,
|
||||
onSuccess: () => {
|
||||
// setLocale(variables.data.site_language as Locale);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...settingQueries.all, 'list'],
|
||||
});
|
||||
queryClient.invalidateQueries(settingQueries.listAdmin());
|
||||
toast.success(m.settings_messages_update_success(), {
|
||||
richColors: true,
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import { m } from '@/paraglide/messages';
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { createLink, useNavigate } from '@tanstack/react-router';
|
||||
import i18next from 'i18next';
|
||||
import { toast } from 'sonner';
|
||||
import z from 'zod';
|
||||
import { Button } from '../ui/button';
|
||||
@@ -14,7 +13,7 @@ import { Input } from '../ui/input';
|
||||
const SignInFormSchema = z.object({
|
||||
email: z
|
||||
.string()
|
||||
.nonempty(m.common_is_required({ field: m.login_page_form_email }))
|
||||
.nonempty(m.common_is_required({ field: m.login_page_form_email() }))
|
||||
.email(m.login_page_messages_email_invalid()),
|
||||
password: z.string().nonempty(
|
||||
m.common_is_required({
|
||||
@@ -52,9 +51,14 @@ const SignInForm = () => {
|
||||
});
|
||||
},
|
||||
onError: (ctx) => {
|
||||
toast.error(i18next.t(`backend.${ctx.error.code}` as any), {
|
||||
richColors: true,
|
||||
});
|
||||
toast.error(
|
||||
(
|
||||
m[`backend_${ctx.error.code}` as keyof typeof m] as () => string
|
||||
)(),
|
||||
{
|
||||
richColors: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from '@phosphor-icons/react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { createLink, Link, useNavigate } from '@tanstack/react-router';
|
||||
import i18next from 'i18next';
|
||||
import { toast } from 'sonner';
|
||||
import { useAuth } from '../auth/auth-provider';
|
||||
import AvatarUser from '../avatar/AvatarUser';
|
||||
@@ -49,9 +48,14 @@ const NavUser = () => {
|
||||
});
|
||||
},
|
||||
onError: (ctx) => {
|
||||
toast.error(i18next.t(`backend_${ctx.error.code}` as any), {
|
||||
richColors: true,
|
||||
});
|
||||
toast.error(
|
||||
(
|
||||
m[`backend_${ctx.error.code}` as keyof typeof m] as () => string
|
||||
)(),
|
||||
{
|
||||
richColors: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { createServerFn } from '@tanstack/react-start'
|
||||
|
||||
export const getPunkSongs = createServerFn({
|
||||
method: 'GET',
|
||||
}).handler(async () => [
|
||||
{ id: 1, name: 'Teenage Dirtbag', artist: 'Wheatus' },
|
||||
{ id: 2, name: 'Smells Like Teen Spirit', artist: 'Nirvana' },
|
||||
{ id: 3, name: 'The Middle', artist: 'Jimmy Eat World' },
|
||||
{ id: 4, name: 'My Own Worst Enemy', artist: 'Lit' },
|
||||
{ id: 5, name: 'Fat Lip', artist: 'Sum 41' },
|
||||
{ id: 6, name: 'All the Small Things', artist: 'blink-182' },
|
||||
{ id: 7, name: 'Beverly Hills', artist: 'Weezer' },
|
||||
])
|
||||
@@ -57,3 +57,8 @@ export type Invitation = Prisma.InvitationModel
|
||||
*
|
||||
*/
|
||||
export type Setting = Prisma.SettingModel
|
||||
/**
|
||||
* Model Audit
|
||||
*
|
||||
*/
|
||||
export type Audit = Prisma.AuditModel
|
||||
|
||||
@@ -79,3 +79,8 @@ export type Invitation = Prisma.InvitationModel
|
||||
*
|
||||
*/
|
||||
export type Setting = Prisma.SettingModel
|
||||
/**
|
||||
* Model Audit
|
||||
*
|
||||
*/
|
||||
export type Audit = Prisma.AuditModel
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -391,7 +391,8 @@ export const ModelName = {
|
||||
Organization: 'Organization',
|
||||
Member: 'Member',
|
||||
Invitation: 'Invitation',
|
||||
Setting: 'Setting'
|
||||
Setting: 'Setting',
|
||||
Audit: 'Audit'
|
||||
} as const
|
||||
|
||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||
@@ -407,7 +408,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
||||
omit: GlobalOmitOptions
|
||||
}
|
||||
meta: {
|
||||
modelProps: "user" | "session" | "account" | "verification" | "organization" | "member" | "invitation" | "setting"
|
||||
modelProps: "user" | "session" | "account" | "verification" | "organization" | "member" | "invitation" | "setting" | "audit"
|
||||
txIsolationLevel: TransactionIsolationLevel
|
||||
}
|
||||
model: {
|
||||
@@ -1003,6 +1004,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
||||
}
|
||||
}
|
||||
}
|
||||
Audit: {
|
||||
payload: Prisma.$AuditPayload<ExtArgs>
|
||||
fields: Prisma.AuditFieldRefs
|
||||
operations: {
|
||||
findUnique: {
|
||||
args: Prisma.AuditFindUniqueArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload> | null
|
||||
}
|
||||
findUniqueOrThrow: {
|
||||
args: Prisma.AuditFindUniqueOrThrowArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>
|
||||
}
|
||||
findFirst: {
|
||||
args: Prisma.AuditFindFirstArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload> | null
|
||||
}
|
||||
findFirstOrThrow: {
|
||||
args: Prisma.AuditFindFirstOrThrowArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>
|
||||
}
|
||||
findMany: {
|
||||
args: Prisma.AuditFindManyArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>[]
|
||||
}
|
||||
create: {
|
||||
args: Prisma.AuditCreateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>
|
||||
}
|
||||
createMany: {
|
||||
args: Prisma.AuditCreateManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
createManyAndReturn: {
|
||||
args: Prisma.AuditCreateManyAndReturnArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>[]
|
||||
}
|
||||
delete: {
|
||||
args: Prisma.AuditDeleteArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>
|
||||
}
|
||||
update: {
|
||||
args: Prisma.AuditUpdateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>
|
||||
}
|
||||
deleteMany: {
|
||||
args: Prisma.AuditDeleteManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
updateMany: {
|
||||
args: Prisma.AuditUpdateManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
updateManyAndReturn: {
|
||||
args: Prisma.AuditUpdateManyAndReturnArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>[]
|
||||
}
|
||||
upsert: {
|
||||
args: Prisma.AuditUpsertArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditPayload>
|
||||
}
|
||||
aggregate: {
|
||||
args: Prisma.AuditAggregateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.AggregateAudit>
|
||||
}
|
||||
groupBy: {
|
||||
args: Prisma.AuditGroupByArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.AuditGroupByOutputType>[]
|
||||
}
|
||||
count: {
|
||||
args: Prisma.AuditCountArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.AuditCountAggregateOutputType> | number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} & {
|
||||
other: {
|
||||
@@ -1157,6 +1232,20 @@ export const SettingScalarFieldEnum = {
|
||||
export type SettingScalarFieldEnum = (typeof SettingScalarFieldEnum)[keyof typeof SettingScalarFieldEnum]
|
||||
|
||||
|
||||
export const AuditScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
action: 'action',
|
||||
tableName: 'tableName',
|
||||
recordId: 'recordId',
|
||||
oldValue: 'oldValue',
|
||||
newValue: 'newValue',
|
||||
createdAt: 'createdAt'
|
||||
} as const
|
||||
|
||||
export type AuditScalarFieldEnum = (typeof AuditScalarFieldEnum)[keyof typeof AuditScalarFieldEnum]
|
||||
|
||||
|
||||
export const SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
@@ -1338,6 +1427,7 @@ export type GlobalOmitConfig = {
|
||||
member?: Prisma.MemberOmit
|
||||
invitation?: Prisma.InvitationOmit
|
||||
setting?: Prisma.SettingOmit
|
||||
audit?: Prisma.AuditOmit
|
||||
}
|
||||
|
||||
/* Types for Logging */
|
||||
|
||||
@@ -58,7 +58,8 @@ export const ModelName = {
|
||||
Organization: 'Organization',
|
||||
Member: 'Member',
|
||||
Invitation: 'Invitation',
|
||||
Setting: 'Setting'
|
||||
Setting: 'Setting',
|
||||
Audit: 'Audit'
|
||||
} as const
|
||||
|
||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||
@@ -192,6 +193,20 @@ export const SettingScalarFieldEnum = {
|
||||
export type SettingScalarFieldEnum = (typeof SettingScalarFieldEnum)[keyof typeof SettingScalarFieldEnum]
|
||||
|
||||
|
||||
export const AuditScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
action: 'action',
|
||||
tableName: 'tableName',
|
||||
recordId: 'recordId',
|
||||
oldValue: 'oldValue',
|
||||
newValue: 'newValue',
|
||||
createdAt: 'createdAt'
|
||||
} as const
|
||||
|
||||
export type AuditScalarFieldEnum = (typeof AuditScalarFieldEnum)[keyof typeof AuditScalarFieldEnum]
|
||||
|
||||
|
||||
export const SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
|
||||
@@ -16,4 +16,5 @@ export type * from './models/Organization.ts'
|
||||
export type * from './models/Member.ts'
|
||||
export type * from './models/Invitation.ts'
|
||||
export type * from './models/Setting.ts'
|
||||
export type * from './models/Audit.ts'
|
||||
export type * from './commonInputTypes.ts'
|
||||
1445
src/generated/prisma/models/Audit.ts
Normal file
1445
src/generated/prisma/models/Audit.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -232,6 +232,7 @@ export type UserWhereInput = {
|
||||
banExpires?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null
|
||||
sessions?: Prisma.SessionListRelationFilter
|
||||
accounts?: Prisma.AccountListRelationFilter
|
||||
audit?: Prisma.AuditListRelationFilter
|
||||
members?: Prisma.MemberListRelationFilter
|
||||
invitations?: Prisma.InvitationListRelationFilter
|
||||
}
|
||||
@@ -250,6 +251,7 @@ export type UserOrderByWithRelationInput = {
|
||||
banExpires?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
sessions?: Prisma.SessionOrderByRelationAggregateInput
|
||||
accounts?: Prisma.AccountOrderByRelationAggregateInput
|
||||
audit?: Prisma.AuditOrderByRelationAggregateInput
|
||||
members?: Prisma.MemberOrderByRelationAggregateInput
|
||||
invitations?: Prisma.InvitationOrderByRelationAggregateInput
|
||||
}
|
||||
@@ -271,6 +273,7 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{
|
||||
banExpires?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null
|
||||
sessions?: Prisma.SessionListRelationFilter
|
||||
accounts?: Prisma.AccountListRelationFilter
|
||||
audit?: Prisma.AuditListRelationFilter
|
||||
members?: Prisma.MemberListRelationFilter
|
||||
invitations?: Prisma.InvitationListRelationFilter
|
||||
}, "id" | "email">
|
||||
@@ -323,6 +326,7 @@ export type UserCreateInput = {
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput
|
||||
}
|
||||
@@ -341,6 +345,7 @@ export type UserUncheckedCreateInput = {
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput
|
||||
}
|
||||
@@ -359,6 +364,7 @@ export type UserUpdateInput = {
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
@@ -377,6 +383,7 @@ export type UserUncheckedUpdateInput = {
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
@@ -550,6 +557,20 @@ export type UserUpdateOneRequiredWithoutInvitationsNestedInput = {
|
||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutInvitationsInput, Prisma.UserUpdateWithoutInvitationsInput>, Prisma.UserUncheckedUpdateWithoutInvitationsInput>
|
||||
}
|
||||
|
||||
export type UserCreateNestedOneWithoutAuditInput = {
|
||||
create?: Prisma.XOR<Prisma.UserCreateWithoutAuditInput, Prisma.UserUncheckedCreateWithoutAuditInput>
|
||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAuditInput
|
||||
connect?: Prisma.UserWhereUniqueInput
|
||||
}
|
||||
|
||||
export type UserUpdateOneRequiredWithoutAuditNestedInput = {
|
||||
create?: Prisma.XOR<Prisma.UserCreateWithoutAuditInput, Prisma.UserUncheckedCreateWithoutAuditInput>
|
||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAuditInput
|
||||
upsert?: Prisma.UserUpsertWithoutAuditInput
|
||||
connect?: Prisma.UserWhereUniqueInput
|
||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutAuditInput, Prisma.UserUpdateWithoutAuditInput>, Prisma.UserUncheckedUpdateWithoutAuditInput>
|
||||
}
|
||||
|
||||
export type UserCreateWithoutSessionsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
@@ -563,6 +584,7 @@ export type UserCreateWithoutSessionsInput = {
|
||||
banReason?: string | null
|
||||
banExpires?: Date | string | null
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput
|
||||
}
|
||||
@@ -580,6 +602,7 @@ export type UserUncheckedCreateWithoutSessionsInput = {
|
||||
banReason?: string | null
|
||||
banExpires?: Date | string | null
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput
|
||||
}
|
||||
@@ -613,6 +636,7 @@ export type UserUpdateWithoutSessionsInput = {
|
||||
banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
@@ -630,6 +654,7 @@ export type UserUncheckedUpdateWithoutSessionsInput = {
|
||||
banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
@@ -647,6 +672,7 @@ export type UserCreateWithoutAccountsInput = {
|
||||
banReason?: string | null
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput
|
||||
}
|
||||
@@ -664,6 +690,7 @@ export type UserUncheckedCreateWithoutAccountsInput = {
|
||||
banReason?: string | null
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput
|
||||
}
|
||||
@@ -697,6 +724,7 @@ export type UserUpdateWithoutAccountsInput = {
|
||||
banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
@@ -714,6 +742,7 @@ export type UserUncheckedUpdateWithoutAccountsInput = {
|
||||
banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
@@ -732,6 +761,7 @@ export type UserCreateWithoutMembersInput = {
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput
|
||||
}
|
||||
|
||||
@@ -749,6 +779,7 @@ export type UserUncheckedCreateWithoutMembersInput = {
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput
|
||||
}
|
||||
|
||||
@@ -782,6 +813,7 @@ export type UserUpdateWithoutMembersInput = {
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
|
||||
@@ -799,6 +831,7 @@ export type UserUncheckedUpdateWithoutMembersInput = {
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
|
||||
@@ -816,6 +849,7 @@ export type UserCreateWithoutInvitationsInput = {
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberCreateNestedManyWithoutUserInput
|
||||
}
|
||||
|
||||
@@ -833,6 +867,7 @@ export type UserUncheckedCreateWithoutInvitationsInput = {
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput
|
||||
}
|
||||
|
||||
@@ -866,6 +901,7 @@ export type UserUpdateWithoutInvitationsInput = {
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
|
||||
@@ -883,9 +919,98 @@ export type UserUncheckedUpdateWithoutInvitationsInput = {
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
|
||||
export type UserCreateWithoutAuditInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
emailVerified?: boolean
|
||||
image?: string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
role?: string | null
|
||||
banned?: boolean | null
|
||||
banReason?: string | null
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput
|
||||
}
|
||||
|
||||
export type UserUncheckedCreateWithoutAuditInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
emailVerified?: boolean
|
||||
image?: string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
role?: string | null
|
||||
banned?: boolean | null
|
||||
banReason?: string | null
|
||||
banExpires?: Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput
|
||||
invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput
|
||||
}
|
||||
|
||||
export type UserCreateOrConnectWithoutAuditInput = {
|
||||
where: Prisma.UserWhereUniqueInput
|
||||
create: Prisma.XOR<Prisma.UserCreateWithoutAuditInput, Prisma.UserUncheckedCreateWithoutAuditInput>
|
||||
}
|
||||
|
||||
export type UserUpsertWithoutAuditInput = {
|
||||
update: Prisma.XOR<Prisma.UserUpdateWithoutAuditInput, Prisma.UserUncheckedUpdateWithoutAuditInput>
|
||||
create: Prisma.XOR<Prisma.UserCreateWithoutAuditInput, Prisma.UserUncheckedCreateWithoutAuditInput>
|
||||
where?: Prisma.UserWhereInput
|
||||
}
|
||||
|
||||
export type UserUpdateToOneWithWhereWithoutAuditInput = {
|
||||
where?: Prisma.UserWhereInput
|
||||
data: Prisma.XOR<Prisma.UserUpdateWithoutAuditInput, Prisma.UserUncheckedUpdateWithoutAuditInput>
|
||||
}
|
||||
|
||||
export type UserUpdateWithoutAuditInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
role?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banned?: Prisma.NullableBoolFieldUpdateOperationsInput | boolean | null
|
||||
banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
|
||||
export type UserUncheckedUpdateWithoutAuditInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
role?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banned?: Prisma.NullableBoolFieldUpdateOperationsInput | boolean | null
|
||||
banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput
|
||||
invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count Type UserCountOutputType
|
||||
@@ -894,6 +1019,7 @@ export type UserUncheckedUpdateWithoutInvitationsInput = {
|
||||
export type UserCountOutputType = {
|
||||
sessions: number
|
||||
accounts: number
|
||||
audit: number
|
||||
members: number
|
||||
invitations: number
|
||||
}
|
||||
@@ -901,6 +1027,7 @@ export type UserCountOutputType = {
|
||||
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
sessions?: boolean | UserCountOutputTypeCountSessionsArgs
|
||||
accounts?: boolean | UserCountOutputTypeCountAccountsArgs
|
||||
audit?: boolean | UserCountOutputTypeCountAuditArgs
|
||||
members?: boolean | UserCountOutputTypeCountMembersArgs
|
||||
invitations?: boolean | UserCountOutputTypeCountInvitationsArgs
|
||||
}
|
||||
@@ -929,6 +1056,13 @@ export type UserCountOutputTypeCountAccountsArgs<ExtArgs extends runtime.Types.E
|
||||
where?: Prisma.AccountWhereInput
|
||||
}
|
||||
|
||||
/**
|
||||
* UserCountOutputType without action
|
||||
*/
|
||||
export type UserCountOutputTypeCountAuditArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
where?: Prisma.AuditWhereInput
|
||||
}
|
||||
|
||||
/**
|
||||
* UserCountOutputType without action
|
||||
*/
|
||||
@@ -958,6 +1092,7 @@ export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
|
||||
banExpires?: boolean
|
||||
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||
audit?: boolean | Prisma.User$auditArgs<ExtArgs>
|
||||
members?: boolean | Prisma.User$membersArgs<ExtArgs>
|
||||
invitations?: boolean | Prisma.User$invitationsArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||
@@ -1009,6 +1144,7 @@ export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = run
|
||||
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||
audit?: boolean | Prisma.User$auditArgs<ExtArgs>
|
||||
members?: boolean | Prisma.User$membersArgs<ExtArgs>
|
||||
invitations?: boolean | Prisma.User$invitationsArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||
@@ -1021,6 +1157,7 @@ export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
||||
objects: {
|
||||
sessions: Prisma.$SessionPayload<ExtArgs>[]
|
||||
accounts: Prisma.$AccountPayload<ExtArgs>[]
|
||||
audit: Prisma.$AuditPayload<ExtArgs>[]
|
||||
members: Prisma.$MemberPayload<ExtArgs>[]
|
||||
invitations: Prisma.$InvitationPayload<ExtArgs>[]
|
||||
}
|
||||
@@ -1432,6 +1569,7 @@ export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Typ
|
||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||
sessions<T extends Prisma.User$sessionsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$sessionsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$SessionPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
accounts<T extends Prisma.User$accountsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$accountsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AccountPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
audit<T extends Prisma.User$auditArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$auditArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AuditPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
members<T extends Prisma.User$membersArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$membersArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MemberPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
invitations<T extends Prisma.User$invitationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$invitationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$InvitationPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
/**
|
||||
@@ -1909,6 +2047,30 @@ export type User$accountsArgs<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||
distinct?: Prisma.AccountScalarFieldEnum | Prisma.AccountScalarFieldEnum[]
|
||||
}
|
||||
|
||||
/**
|
||||
* User.audit
|
||||
*/
|
||||
export type User$auditArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Audit
|
||||
*/
|
||||
select?: Prisma.AuditSelect<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Audit
|
||||
*/
|
||||
omit?: Prisma.AuditOmit<ExtArgs> | null
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.AuditInclude<ExtArgs> | null
|
||||
where?: Prisma.AuditWhereInput
|
||||
orderBy?: Prisma.AuditOrderByWithRelationInput | Prisma.AuditOrderByWithRelationInput[]
|
||||
cursor?: Prisma.AuditWhereUniqueInput
|
||||
take?: number
|
||||
skip?: number
|
||||
distinct?: Prisma.AuditScalarFieldEnum | Prisma.AuditScalarFieldEnum[]
|
||||
}
|
||||
|
||||
/**
|
||||
* User.members
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
owner,
|
||||
} from '@/lib/auth/organization-permissions';
|
||||
import { ac, admin, user } from '@/lib/auth/permissions';
|
||||
import { createAuditLog } from '@/service/audit.api';
|
||||
import { betterAuth } from 'better-auth';
|
||||
import { prismaAdapter } from 'better-auth/adapters/prisma';
|
||||
import { admin as adminPlugin, organization } from 'better-auth/plugins';
|
||||
@@ -67,6 +68,73 @@ export const auth = betterAuth({
|
||||
});
|
||||
},
|
||||
},
|
||||
update: {
|
||||
before: async (user, ctx) => {
|
||||
if (ctx?.context.session && ctx?.path === '/update-user') {
|
||||
const newUser = JSON.parse(JSON.stringify(user));
|
||||
const keys = Object.keys(newUser);
|
||||
const oldUser = Object.fromEntries(
|
||||
Object.entries(ctx?.context.session?.user).filter(([key]) =>
|
||||
keys.includes(key),
|
||||
),
|
||||
);
|
||||
await createAuditLog({
|
||||
action: 'update',
|
||||
tableName: 'user',
|
||||
recordId: ctx?.context.session?.user.id,
|
||||
oldValue: JSON.stringify(oldUser),
|
||||
newValue: JSON.stringify(newUser),
|
||||
userId: ctx?.context.session?.user.id,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
account: {
|
||||
update: {
|
||||
after: async (account, context) => {
|
||||
if (context?.path === '/change-password') {
|
||||
await createAuditLog({
|
||||
action: 'change_password',
|
||||
tableName: 'account',
|
||||
recordId: account.id,
|
||||
oldValue: 'Change Password',
|
||||
newValue: 'Change Password',
|
||||
userId: account.userId,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
session: {
|
||||
create: {
|
||||
after: async (session, context) => {
|
||||
if (context?.path.includes('/sign-in')) {
|
||||
await createAuditLog({
|
||||
action: 'sign_in',
|
||||
tableName: 'session',
|
||||
recordId: session.id,
|
||||
oldValue: '',
|
||||
newValue: JSON.stringify(session),
|
||||
userId: session.userId,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
after: async (session, context) => {
|
||||
if (context?.path === '/sign-out') {
|
||||
await createAuditLog({
|
||||
action: 'sign_out',
|
||||
tableName: 'session',
|
||||
recordId: session.id,
|
||||
oldValue: JSON.stringify(session),
|
||||
newValue: '',
|
||||
userId: session.userId,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { getRequestHeaders } from '@tanstack/react-start/server';
|
||||
import { auth } from '../auth';
|
||||
import { authClient } from '../auth-client';
|
||||
|
||||
export type Session = typeof auth.$Infer.Session;
|
||||
|
||||
@@ -12,8 +11,3 @@ export const getSession = createServerFn({ method: 'GET' }).handler(
|
||||
return session;
|
||||
},
|
||||
);
|
||||
|
||||
export const sessionPush = () => {
|
||||
const session = authClient.getSession();
|
||||
return session;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Route as appIndexRouteImport } from './routes/(app)/index'
|
||||
import { Route as authSignUpRouteImport } from './routes/(auth)/sign-up'
|
||||
import { Route as authSignInRouteImport } from './routes/(auth)/sign-in'
|
||||
import { Route as appauthRouteRouteImport } from './routes/(app)/(auth)/route'
|
||||
import { Route as ApiAuthSplatRouteImport } from './routes/api/auth.$'
|
||||
import { Route as ApiAuthSplatRouteImport } from './routes/api.auth.$'
|
||||
import { Route as appauthSettingsRouteImport } from './routes/(app)/(auth)/settings'
|
||||
import { Route as appauthDashboardRouteImport } from './routes/(app)/(auth)/dashboard'
|
||||
import { Route as appauthAccountRouteRouteImport } from './routes/(app)/(auth)/account/route'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createFileRoute, Outlet, redirect } from '@tanstack/react-router';
|
||||
import { createFileRoute, Outlet } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/(app)/(auth)')({
|
||||
beforeLoad: async ({ context }) => {
|
||||
if (!context.userSession) {
|
||||
throw redirect({ to: '/sign-in' });
|
||||
}
|
||||
},
|
||||
// beforeLoad: async ({ context }) => {
|
||||
// if (!context.userSession) {
|
||||
// throw redirect({ to: '/sign-in' });
|
||||
// }
|
||||
// },
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import NotFound from '@/components/NotFound';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { getLocale } from '@/paraglide/runtime';
|
||||
import { sessionQueries } from '@/service/queries';
|
||||
import {
|
||||
CheckIcon,
|
||||
InfoIcon,
|
||||
@@ -20,43 +19,41 @@ import React from 'react';
|
||||
import TanStackQueryDevtools from '../integrations/tanstack-query/devtools';
|
||||
import appCss from '../styles.css?url';
|
||||
|
||||
interface MyRouterContext {
|
||||
queryClient: QueryClient;
|
||||
}
|
||||
|
||||
export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||
beforeLoad: async ({ context }) => {
|
||||
const userSession = await context.queryClient.fetchQuery(
|
||||
sessionQueries.user(),
|
||||
);
|
||||
return { userSession };
|
||||
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()(
|
||||
{
|
||||
// beforeLoad: async ({ context }) => {
|
||||
// const userSession = await context.queryClient.fetchQuery(
|
||||
// sessionQueries.user(),
|
||||
// );
|
||||
// return { userSession };
|
||||
// },
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: 'utf-8',
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1',
|
||||
},
|
||||
{
|
||||
title: 'Fuware',
|
||||
},
|
||||
{
|
||||
description: 'Fuware is a platform for managing your business.',
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
href: appCss,
|
||||
},
|
||||
],
|
||||
}),
|
||||
shellComponent: RootDocument,
|
||||
notFoundComponent: () => <NotFound />,
|
||||
},
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: 'utf-8',
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1',
|
||||
},
|
||||
{
|
||||
title: 'Fuware',
|
||||
},
|
||||
{
|
||||
description: 'Fuware is a platform for managing your business.',
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
href: appCss,
|
||||
},
|
||||
],
|
||||
}),
|
||||
shellComponent: RootDocument,
|
||||
notFoundComponent: () => <NotFound />,
|
||||
});
|
||||
);
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -79,20 +76,20 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
warning: <WarningIcon className="text-yellow-500" size={16} />,
|
||||
}}
|
||||
/>
|
||||
<React.Suspense>
|
||||
<TanStackDevtools
|
||||
config={{
|
||||
position: 'bottom-right',
|
||||
}}
|
||||
plugins={[
|
||||
{
|
||||
name: 'Tanstack Router',
|
||||
render: <TanStackRouterDevtoolsPanel />,
|
||||
},
|
||||
TanStackQueryDevtools,
|
||||
]}
|
||||
/>
|
||||
</React.Suspense>
|
||||
{/* <React.Suspense> */}
|
||||
<TanStackDevtools
|
||||
config={{
|
||||
position: 'bottom-right',
|
||||
}}
|
||||
plugins={[
|
||||
{
|
||||
name: 'Tanstack Router',
|
||||
render: <TanStackRouterDevtoolsPanel />,
|
||||
},
|
||||
TanStackQueryDevtools,
|
||||
]}
|
||||
/>
|
||||
{/* </React.Suspense> */}
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
15
src/service/audit.api.ts
Normal file
15
src/service/audit.api.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { prisma } from '@/db';
|
||||
import { Audit } from '@/generated/prisma/client';
|
||||
|
||||
export async function createAuditLog(data: Omit<Audit, 'id' | 'createdAt'>) {
|
||||
try {
|
||||
await prisma.audit.create({
|
||||
data: {
|
||||
...data,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { getSession } from '@/lib/auth/session';
|
||||
// import { sessionPush } from '@/lib/auth/session';
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
import { getAdminSettings, getUserSettings } from './setting.api';
|
||||
|
||||
@@ -9,7 +8,6 @@ export const sessionQueries = {
|
||||
queryOptions({
|
||||
queryKey: [...sessionQueries.all, 'session'],
|
||||
queryFn: () => getSession(),
|
||||
// queryFn: () => sessionPush(),
|
||||
staleTime: 1000 * 60 * 20,
|
||||
retry: false,
|
||||
}),
|
||||
|
||||
@@ -1,54 +1,87 @@
|
||||
import { prisma } from '@/db';
|
||||
import { Setting } from '@/generated/prisma/client';
|
||||
import { authMiddleware } from '@/lib/middleware';
|
||||
import { extractDiffObjects } from '@/utils/help';
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { createAuditLog } from './audit.api';
|
||||
import { settingSchema, userSettingSchema } from './setting.schema';
|
||||
// import { settingSchema } from './setting.schema';
|
||||
|
||||
type AdminSettingReturn = {
|
||||
[key: string]: Setting;
|
||||
[key: string]: Pick<Setting, 'id' | 'key' | 'value'> | string;
|
||||
};
|
||||
|
||||
async function getAllAdminSettings(valueOnly = false) {
|
||||
const settings = await prisma.setting.findMany({
|
||||
where: {
|
||||
relation: 'admin',
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
key: true,
|
||||
value: true,
|
||||
},
|
||||
});
|
||||
|
||||
const results: AdminSettingReturn = {};
|
||||
|
||||
settings.forEach((setting) => {
|
||||
results[setting.key] = valueOnly ? setting.value : setting;
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Settings for admin
|
||||
export const getAdminSettings = createServerFn({ method: 'GET' })
|
||||
.middleware([authMiddleware])
|
||||
.handler(async () => {
|
||||
const settings = await prisma.setting.findMany({
|
||||
where: {
|
||||
relation: 'admin',
|
||||
},
|
||||
});
|
||||
|
||||
const results: AdminSettingReturn = {};
|
||||
|
||||
settings.forEach((setting) => {
|
||||
results[setting.key] = setting;
|
||||
});
|
||||
|
||||
return results;
|
||||
return await getAllAdminSettings();
|
||||
});
|
||||
|
||||
export const updateAdminSettings = createServerFn({ method: 'POST' })
|
||||
.inputValidator(settingSchema)
|
||||
.middleware([authMiddleware])
|
||||
.handler(async ({ data }) => {
|
||||
// Update each setting
|
||||
const updates = Object.entries(data).map(([key, value]) =>
|
||||
prisma.setting.upsert({
|
||||
where: { key },
|
||||
update: { value },
|
||||
create: {
|
||||
key,
|
||||
value,
|
||||
description: key, // or provide proper descriptions
|
||||
relation: 'admin',
|
||||
},
|
||||
}),
|
||||
);
|
||||
.handler(async ({ data, context }) => {
|
||||
try {
|
||||
const oldSetting = await getAllAdminSettings(true);
|
||||
|
||||
await prisma.$transaction(updates);
|
||||
// Update each setting
|
||||
const updates = Object.entries(data).map(([key, value]) =>
|
||||
prisma.setting.upsert({
|
||||
where: { key },
|
||||
update: { value },
|
||||
create: {
|
||||
key,
|
||||
value,
|
||||
description: key, // or provide proper descriptions
|
||||
relation: 'admin',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return { success: true };
|
||||
const updated = await prisma.$transaction(updates);
|
||||
|
||||
const [oldValue, newValue] = extractDiffObjects(oldSetting, data);
|
||||
const keyEdit = Object.keys(oldValue);
|
||||
const listId = updated
|
||||
.filter((s) => keyEdit.includes(s.key))
|
||||
.map((s) => s.id)
|
||||
.join(', ');
|
||||
|
||||
await createAuditLog({
|
||||
action: 'update',
|
||||
tableName: 'setting',
|
||||
recordId: listId,
|
||||
oldValue: JSON.stringify(oldValue),
|
||||
newValue: JSON.stringify(newValue),
|
||||
userId: context.user.id,
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// Setting for user
|
||||
@@ -65,10 +98,15 @@ export const getUserSettings = createServerFn({ method: 'GET' })
|
||||
relation: 'user',
|
||||
key: context.user.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
key: true,
|
||||
value: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
settings: settings as Setting,
|
||||
settings,
|
||||
value: JSON.parse(settings.value) as UserSetting,
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -82,7 +120,17 @@ export const updateUserSettings = createServerFn({ method: 'POST' })
|
||||
.handler(async ({ data, context }) => {
|
||||
// Update each setting
|
||||
try {
|
||||
await prisma.setting.upsert({
|
||||
const oldSetting = await prisma.setting.findUnique({
|
||||
where: {
|
||||
relation: 'user',
|
||||
key: context.user.id,
|
||||
},
|
||||
select: {
|
||||
value: true,
|
||||
},
|
||||
});
|
||||
|
||||
const updated = await prisma.setting.upsert({
|
||||
where: { key: context.user.id },
|
||||
update: { value: JSON.stringify(data) },
|
||||
create: {
|
||||
@@ -93,6 +141,17 @@ export const updateUserSettings = createServerFn({ method: 'POST' })
|
||||
},
|
||||
});
|
||||
|
||||
if (oldSetting) {
|
||||
await createAuditLog({
|
||||
action: 'update',
|
||||
tableName: 'setting',
|
||||
recordId: updated.id,
|
||||
oldValue: oldSetting.value,
|
||||
newValue: JSON.stringify(data),
|
||||
userId: context.user.id,
|
||||
});
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
27
src/utils/help.ts
Normal file
27
src/utils/help.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export function jsonSupport(jsonSTR: string) {
|
||||
try {
|
||||
const data = JSON.parse(jsonSTR);
|
||||
|
||||
return JSON.stringify(data, undefined, 2);
|
||||
} catch {
|
||||
return jsonSTR;
|
||||
}
|
||||
}
|
||||
|
||||
type AnyRecord = Record<string, unknown>;
|
||||
|
||||
export function extractDiffObjects<T extends AnyRecord>(
|
||||
a: T,
|
||||
b: T,
|
||||
): [Partial<T>, Partial<T>] {
|
||||
return (Object.keys(a) as (keyof T)[]).reduce(
|
||||
([accA, accB], key) => {
|
||||
if (a[key] !== b[key]) {
|
||||
accA[key] = a[key];
|
||||
accB[key] = b[key];
|
||||
}
|
||||
return [accA, accB];
|
||||
},
|
||||
[{}, {}] as [Partial<T>, Partial<T>],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user