feat: secure KYC storage, Google OAuth, terms gating
This commit is contained in:
@@ -33,6 +33,9 @@ yarn-error.log*
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# private uploads (KYC: KTP / selfie). Never serve directly.
|
||||
/uploads/
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { signOut, useSession } from "next-auth/react";
|
||||
import { acceptTermsAction } from "@/features/auth/actions";
|
||||
|
||||
export function AcceptTermsForm() {
|
||||
const router = useRouter();
|
||||
const { update } = useSession();
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
async function handleAccept() {
|
||||
setError("");
|
||||
setLoading(true);
|
||||
const result = await acceptTermsAction();
|
||||
if (result.error) {
|
||||
setError(result.error);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
// Refresh JWT supaya middleware lihat acceptedTermsAndPrivacy=true
|
||||
await update();
|
||||
router.replace("/");
|
||||
router.refresh();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-6 rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm">
|
||||
{error && (
|
||||
<div className="mb-4 rounded-xl bg-red-50 px-4 py-3 text-sm font-medium text-red-600">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<label className="flex items-start gap-2.5 text-sm text-neutral-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={(e) => setChecked(e.target.checked)}
|
||||
className="mt-0.5 h-4 w-4 shrink-0 rounded border-neutral-300 text-primary-600 focus:ring-primary-500"
|
||||
/>
|
||||
<span>
|
||||
Saya telah membaca dan menyetujui{" "}
|
||||
<Link
|
||||
href="/terms"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||
>
|
||||
Syarat & Ketentuan
|
||||
</Link>{" "}
|
||||
dan{" "}
|
||||
<Link
|
||||
href="/privacy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-semibold text-primary-600 hover:text-primary-700"
|
||||
>
|
||||
Kebijakan Privasi
|
||||
</Link>{" "}
|
||||
SeTrip.
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div className="mt-5 flex flex-col gap-2 sm:flex-row">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAccept}
|
||||
disabled={!checked || loading}
|
||||
className="flex-1 rounded-xl bg-primary-600 py-2.5 text-sm font-bold text-white shadow-lg shadow-primary-600/20 transition-colors hover:bg-primary-700 disabled:opacity-50"
|
||||
>
|
||||
{loading ? "Memproses..." : "Setuju & Lanjutkan"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => signOut({ callbackUrl: "/login" })}
|
||||
disabled={loading}
|
||||
className="rounded-xl border border-neutral-200 bg-white px-4 py-2.5 text-sm font-medium text-neutral-600 hover:bg-neutral-50 disabled:opacity-50"
|
||||
>
|
||||
Keluar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import { userRepo } from "@/server/repositories/user.repo";
|
||||
import { AcceptTermsForm } from "./form";
|
||||
|
||||
export default async function AcceptTermsPage() {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user) redirect("/login?callbackUrl=/accept-terms");
|
||||
|
||||
// Source of truth = DB (token bisa stale).
|
||||
const user = await userRepo.findById(session.user.id);
|
||||
if (user?.acceptedTermsAndPrivacy) redirect("/");
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-xl px-4 py-10 sm:py-16">
|
||||
<h1 className="text-2xl font-bold text-neutral-900 sm:text-3xl">
|
||||
Satu langkah lagi
|
||||
</h1>
|
||||
<p className="mt-2 text-sm text-neutral-600">
|
||||
Sebelum melanjutkan, mohon baca dan setujui Syarat & Ketentuan dan
|
||||
Kebijakan Privasi SeTrip.
|
||||
</p>
|
||||
<AcceptTermsForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import { isAdminEmail } from "@/lib/admin";
|
||||
import { organizerRepo } from "@/server/repositories/organizer.repo";
|
||||
import { organizerService } from "@/server/services/organizer.service";
|
||||
import { ReviewCard } from "@/features/organizer/components/review-card";
|
||||
|
||||
type Tab = "PENDING" | "APPROVED" | "REJECTED";
|
||||
@@ -28,7 +29,23 @@ export default async function AdminVerificationsPage({ searchParams }: PageProps
|
||||
const tab: Tab =
|
||||
params.tab === "APPROVED" || params.tab === "REJECTED" ? params.tab : "PENDING";
|
||||
|
||||
const items = await organizerRepo.listByStatus(tab);
|
||||
const rows = await organizerRepo.listByStatus(tab);
|
||||
const items = rows.map((v) => ({
|
||||
id: v.id,
|
||||
fullName: v.fullName,
|
||||
nik: organizerService.decryptNik(v.nikEncrypted),
|
||||
birthDate: v.birthDate,
|
||||
address: v.address,
|
||||
bankName: v.bankName,
|
||||
bankAccountNumber: v.bankAccountNumber,
|
||||
bankAccountName: v.bankAccountName,
|
||||
status: v.status,
|
||||
rejectionReason: v.rejectionReason,
|
||||
reviewedAt: v.reviewedAt,
|
||||
createdAt: v.createdAt,
|
||||
user: v.user,
|
||||
reviewedBy: v.reviewedBy,
|
||||
}));
|
||||
|
||||
const tabs: { key: Tab; label: string }[] = [
|
||||
{ key: "PENDING", label: "Pending" },
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import { isAdminEmail } from "@/lib/admin";
|
||||
import { organizerRepo } from "@/server/repositories/organizer.repo";
|
||||
import {
|
||||
isKycKind,
|
||||
mimeFromKey,
|
||||
readDecrypted,
|
||||
} from "@/lib/secure-storage";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
interface RouteCtx {
|
||||
params: Promise<{ id: string; kind: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_req: NextRequest, ctx: RouteCtx) {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id, kind } = await ctx.params;
|
||||
if (!isKycKind(kind)) {
|
||||
return NextResponse.json({ error: "Kind tidak valid" }, { status: 400 });
|
||||
}
|
||||
|
||||
const verification = await organizerRepo.findById(id);
|
||||
if (!verification) {
|
||||
return NextResponse.json({ error: "Tidak ditemukan" }, { status: 404 });
|
||||
}
|
||||
|
||||
const isOwner = verification.userId === session.user.id;
|
||||
const isAdmin = isAdminEmail(session.user.email);
|
||||
if (!isOwner && !isAdmin) {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
}
|
||||
|
||||
const key = kind === "ktp" ? verification.ktpImageKey : verification.selfieKey;
|
||||
if (!key) {
|
||||
return NextResponse.json({ error: "File belum diunggah" }, { status: 404 });
|
||||
}
|
||||
|
||||
let plain: Buffer;
|
||||
try {
|
||||
plain = await readDecrypted(kind, key);
|
||||
} catch {
|
||||
return NextResponse.json({ error: "File tidak dapat dibuka" }, { status: 500 });
|
||||
}
|
||||
|
||||
return new NextResponse(new Uint8Array(plain), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": mimeFromKey(key),
|
||||
"Content-Length": String(plain.length),
|
||||
"Cache-Control": "private, no-store",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"Content-Disposition": `inline; filename="${kind}-${id}"`,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import {
|
||||
ALLOWED_KYC_MIME,
|
||||
MAX_KYC_FILE_BYTES,
|
||||
isKycKind,
|
||||
saveEncrypted,
|
||||
} from "@/lib/secure-storage";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
let form: FormData;
|
||||
try {
|
||||
form = await req.formData();
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Body bukan multipart/form-data" }, { status: 400 });
|
||||
}
|
||||
|
||||
const kind = String(form.get("kind") ?? "");
|
||||
const file = form.get("file");
|
||||
|
||||
if (!isKycKind(kind)) {
|
||||
return NextResponse.json({ error: "kind harus 'ktp' atau 'selfie'" }, { status: 400 });
|
||||
}
|
||||
if (!(file instanceof File)) {
|
||||
return NextResponse.json({ error: "File wajib diisi" }, { status: 400 });
|
||||
}
|
||||
if (!ALLOWED_KYC_MIME.has(file.type)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Hanya menerima JPG, PNG, atau WebP" },
|
||||
{ status: 415 },
|
||||
);
|
||||
}
|
||||
if (file.size > MAX_KYC_FILE_BYTES) {
|
||||
return NextResponse.json({ error: "File maksimal 5MB" }, { status: 413 });
|
||||
}
|
||||
|
||||
const buf = Buffer.from(await file.arrayBuffer());
|
||||
const meta = await saveEncrypted(kind, buf, file.type);
|
||||
|
||||
return NextResponse.json({
|
||||
key: meta.key,
|
||||
mime: meta.mime,
|
||||
size: meta.size,
|
||||
});
|
||||
}
|
||||
@@ -22,6 +22,12 @@ export * from './enums';
|
||||
*
|
||||
*/
|
||||
export type User = Prisma.UserModel
|
||||
/**
|
||||
* Model Account
|
||||
* Tabel link akun OAuth pihak ketiga (Google, dst). Diisi oleh PrismaAdapter NextAuth.
|
||||
* Session tidak pakai DB — kita pakai JWT, jadi Session/VerificationToken tidak perlu.
|
||||
*/
|
||||
export type Account = Prisma.AccountModel
|
||||
/**
|
||||
* Model OrganizerVerification
|
||||
*
|
||||
|
||||
@@ -46,6 +46,12 @@ export { Prisma }
|
||||
*
|
||||
*/
|
||||
export type User = Prisma.UserModel
|
||||
/**
|
||||
* Model Account
|
||||
* Tabel link akun OAuth pihak ketiga (Google, dst). Diisi oleh PrismaAdapter NextAuth.
|
||||
* Session tidak pakai DB — kita pakai JWT, jadi Session/VerificationToken tidak perlu.
|
||||
*/
|
||||
export type Account = Prisma.AccountModel
|
||||
/**
|
||||
* Model OrganizerVerification
|
||||
*
|
||||
|
||||
@@ -148,6 +148,33 @@ export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type IntNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||
}
|
||||
|
||||
export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type EnumVerificationStatusFilter<$PrismaModel = never> = {
|
||||
equals?: $Enums.VerificationStatus | Prisma.EnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||
in?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||
@@ -373,6 +400,33 @@ export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedFloatNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null
|
||||
}
|
||||
|
||||
export type NestedEnumVerificationStatusFilter<$PrismaModel = never> = {
|
||||
equals?: $Enums.VerificationStatus | Prisma.EnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||
in?: $Enums.VerificationStatus[] | Prisma.ListEnumVerificationStatusFieldRefInput<$PrismaModel>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -385,6 +385,7 @@ type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRe
|
||||
|
||||
export const ModelName = {
|
||||
User: 'User',
|
||||
Account: 'Account',
|
||||
OrganizerVerification: 'OrganizerVerification',
|
||||
Trip: 'Trip',
|
||||
TripReview: 'TripReview',
|
||||
@@ -405,7 +406,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
||||
omit: GlobalOmitOptions
|
||||
}
|
||||
meta: {
|
||||
modelProps: "user" | "organizerVerification" | "trip" | "tripReview" | "tripImage" | "tripParticipant"
|
||||
modelProps: "user" | "account" | "organizerVerification" | "trip" | "tripReview" | "tripImage" | "tripParticipant"
|
||||
txIsolationLevel: TransactionIsolationLevel
|
||||
}
|
||||
model: {
|
||||
@@ -483,6 +484,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
||||
}
|
||||
}
|
||||
}
|
||||
Account: {
|
||||
payload: Prisma.$AccountPayload<ExtArgs>
|
||||
fields: Prisma.AccountFieldRefs
|
||||
operations: {
|
||||
findUnique: {
|
||||
args: Prisma.AccountFindUniqueArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload> | null
|
||||
}
|
||||
findUniqueOrThrow: {
|
||||
args: Prisma.AccountFindUniqueOrThrowArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||
}
|
||||
findFirst: {
|
||||
args: Prisma.AccountFindFirstArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload> | null
|
||||
}
|
||||
findFirstOrThrow: {
|
||||
args: Prisma.AccountFindFirstOrThrowArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||
}
|
||||
findMany: {
|
||||
args: Prisma.AccountFindManyArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||
}
|
||||
create: {
|
||||
args: Prisma.AccountCreateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||
}
|
||||
createMany: {
|
||||
args: Prisma.AccountCreateManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
createManyAndReturn: {
|
||||
args: Prisma.AccountCreateManyAndReturnArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||
}
|
||||
delete: {
|
||||
args: Prisma.AccountDeleteArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||
}
|
||||
update: {
|
||||
args: Prisma.AccountUpdateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||
}
|
||||
deleteMany: {
|
||||
args: Prisma.AccountDeleteManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
updateMany: {
|
||||
args: Prisma.AccountUpdateManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
updateManyAndReturn: {
|
||||
args: Prisma.AccountUpdateManyAndReturnArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||
}
|
||||
upsert: {
|
||||
args: Prisma.AccountUpsertArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||
}
|
||||
aggregate: {
|
||||
args: Prisma.AccountAggregateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.AggregateAccount>
|
||||
}
|
||||
groupBy: {
|
||||
args: Prisma.AccountGroupByArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.AccountGroupByOutputType>[]
|
||||
}
|
||||
count: {
|
||||
args: Prisma.AccountCountArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.AccountCountAggregateOutputType> | number
|
||||
}
|
||||
}
|
||||
}
|
||||
OrganizerVerification: {
|
||||
payload: Prisma.$OrganizerVerificationPayload<ExtArgs>
|
||||
fields: Prisma.OrganizerVerificationFieldRefs
|
||||
@@ -907,15 +982,34 @@ export const UserScalarFieldEnum = {
|
||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||
|
||||
|
||||
export const AccountScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
type: 'type',
|
||||
provider: 'provider',
|
||||
providerAccountId: 'providerAccountId',
|
||||
refresh_token: 'refresh_token',
|
||||
access_token: 'access_token',
|
||||
expires_at: 'expires_at',
|
||||
token_type: 'token_type',
|
||||
scope: 'scope',
|
||||
id_token: 'id_token',
|
||||
session_state: 'session_state'
|
||||
} as const
|
||||
|
||||
export type AccountScalarFieldEnum = (typeof AccountScalarFieldEnum)[keyof typeof AccountScalarFieldEnum]
|
||||
|
||||
|
||||
export const OrganizerVerificationScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
fullName: 'fullName',
|
||||
nik: 'nik',
|
||||
nikEncrypted: 'nikEncrypted',
|
||||
nikHash: 'nikHash',
|
||||
birthDate: 'birthDate',
|
||||
address: 'address',
|
||||
ktpImageUrl: 'ktpImageUrl',
|
||||
selfieUrl: 'selfieUrl',
|
||||
ktpImageKey: 'ktpImageKey',
|
||||
selfieKey: 'selfieKey',
|
||||
bankName: 'bankName',
|
||||
bankAccountNumber: 'bankAccountNumber',
|
||||
bankAccountName: 'bankAccountName',
|
||||
@@ -1056,20 +1150,6 @@ export type ListDateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaM
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a field of type 'VerificationStatus'
|
||||
*/
|
||||
export type EnumVerificationStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'VerificationStatus'>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a field of type 'VerificationStatus[]'
|
||||
*/
|
||||
export type ListEnumVerificationStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'VerificationStatus[]'>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a field of type 'Int'
|
||||
*/
|
||||
@@ -1084,6 +1164,20 @@ export type ListIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel,
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a field of type 'VerificationStatus'
|
||||
*/
|
||||
export type EnumVerificationStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'VerificationStatus'>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a field of type 'VerificationStatus[]'
|
||||
*/
|
||||
export type ListEnumVerificationStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'VerificationStatus[]'>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reference to a field of type 'TripStatus'
|
||||
*/
|
||||
@@ -1221,6 +1315,7 @@ export type PrismaClientOptions = ({
|
||||
}
|
||||
export type GlobalOmitConfig = {
|
||||
user?: Prisma.UserOmit
|
||||
account?: Prisma.AccountOmit
|
||||
organizerVerification?: Prisma.OrganizerVerificationOmit
|
||||
trip?: Prisma.TripOmit
|
||||
tripReview?: Prisma.TripReviewOmit
|
||||
|
||||
@@ -52,6 +52,7 @@ export const AnyNull = runtime.AnyNull
|
||||
|
||||
export const ModelName = {
|
||||
User: 'User',
|
||||
Account: 'Account',
|
||||
OrganizerVerification: 'OrganizerVerification',
|
||||
Trip: 'Trip',
|
||||
TripReview: 'TripReview',
|
||||
@@ -90,15 +91,34 @@ export const UserScalarFieldEnum = {
|
||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||
|
||||
|
||||
export const AccountScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
type: 'type',
|
||||
provider: 'provider',
|
||||
providerAccountId: 'providerAccountId',
|
||||
refresh_token: 'refresh_token',
|
||||
access_token: 'access_token',
|
||||
expires_at: 'expires_at',
|
||||
token_type: 'token_type',
|
||||
scope: 'scope',
|
||||
id_token: 'id_token',
|
||||
session_state: 'session_state'
|
||||
} as const
|
||||
|
||||
export type AccountScalarFieldEnum = (typeof AccountScalarFieldEnum)[keyof typeof AccountScalarFieldEnum]
|
||||
|
||||
|
||||
export const OrganizerVerificationScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
fullName: 'fullName',
|
||||
nik: 'nik',
|
||||
nikEncrypted: 'nikEncrypted',
|
||||
nikHash: 'nikHash',
|
||||
birthDate: 'birthDate',
|
||||
address: 'address',
|
||||
ktpImageUrl: 'ktpImageUrl',
|
||||
selfieUrl: 'selfieUrl',
|
||||
ktpImageKey: 'ktpImageKey',
|
||||
selfieKey: 'selfieKey',
|
||||
bankName: 'bankName',
|
||||
bankAccountNumber: 'bankAccountNumber',
|
||||
bankAccountName: 'bankAccountName',
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* 🟢 You can import this file directly.
|
||||
*/
|
||||
export type * from './models/User'
|
||||
export type * from './models/Account'
|
||||
export type * from './models/OrganizerVerification'
|
||||
export type * from './models/Trip'
|
||||
export type * from './models/TripReview'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,11 +28,12 @@ export type OrganizerVerificationMinAggregateOutputType = {
|
||||
id: string | null
|
||||
userId: string | null
|
||||
fullName: string | null
|
||||
nik: string | null
|
||||
nikEncrypted: string | null
|
||||
nikHash: string | null
|
||||
birthDate: Date | null
|
||||
address: string | null
|
||||
ktpImageUrl: string | null
|
||||
selfieUrl: string | null
|
||||
ktpImageKey: string | null
|
||||
selfieKey: string | null
|
||||
bankName: string | null
|
||||
bankAccountNumber: string | null
|
||||
bankAccountName: string | null
|
||||
@@ -49,11 +50,12 @@ export type OrganizerVerificationMaxAggregateOutputType = {
|
||||
id: string | null
|
||||
userId: string | null
|
||||
fullName: string | null
|
||||
nik: string | null
|
||||
nikEncrypted: string | null
|
||||
nikHash: string | null
|
||||
birthDate: Date | null
|
||||
address: string | null
|
||||
ktpImageUrl: string | null
|
||||
selfieUrl: string | null
|
||||
ktpImageKey: string | null
|
||||
selfieKey: string | null
|
||||
bankName: string | null
|
||||
bankAccountNumber: string | null
|
||||
bankAccountName: string | null
|
||||
@@ -70,11 +72,12 @@ export type OrganizerVerificationCountAggregateOutputType = {
|
||||
id: number
|
||||
userId: number
|
||||
fullName: number
|
||||
nik: number
|
||||
nikEncrypted: number
|
||||
nikHash: number
|
||||
birthDate: number
|
||||
address: number
|
||||
ktpImageUrl: number
|
||||
selfieUrl: number
|
||||
ktpImageKey: number
|
||||
selfieKey: number
|
||||
bankName: number
|
||||
bankAccountNumber: number
|
||||
bankAccountName: number
|
||||
@@ -93,11 +96,12 @@ export type OrganizerVerificationMinAggregateInputType = {
|
||||
id?: true
|
||||
userId?: true
|
||||
fullName?: true
|
||||
nik?: true
|
||||
nikEncrypted?: true
|
||||
nikHash?: true
|
||||
birthDate?: true
|
||||
address?: true
|
||||
ktpImageUrl?: true
|
||||
selfieUrl?: true
|
||||
ktpImageKey?: true
|
||||
selfieKey?: true
|
||||
bankName?: true
|
||||
bankAccountNumber?: true
|
||||
bankAccountName?: true
|
||||
@@ -114,11 +118,12 @@ export type OrganizerVerificationMaxAggregateInputType = {
|
||||
id?: true
|
||||
userId?: true
|
||||
fullName?: true
|
||||
nik?: true
|
||||
nikEncrypted?: true
|
||||
nikHash?: true
|
||||
birthDate?: true
|
||||
address?: true
|
||||
ktpImageUrl?: true
|
||||
selfieUrl?: true
|
||||
ktpImageKey?: true
|
||||
selfieKey?: true
|
||||
bankName?: true
|
||||
bankAccountNumber?: true
|
||||
bankAccountName?: true
|
||||
@@ -135,11 +140,12 @@ export type OrganizerVerificationCountAggregateInputType = {
|
||||
id?: true
|
||||
userId?: true
|
||||
fullName?: true
|
||||
nik?: true
|
||||
nikEncrypted?: true
|
||||
nikHash?: true
|
||||
birthDate?: true
|
||||
address?: true
|
||||
ktpImageUrl?: true
|
||||
selfieUrl?: true
|
||||
ktpImageKey?: true
|
||||
selfieKey?: true
|
||||
bankName?: true
|
||||
bankAccountNumber?: true
|
||||
bankAccountName?: true
|
||||
@@ -229,11 +235,12 @@ export type OrganizerVerificationGroupByOutputType = {
|
||||
id: string
|
||||
userId: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -271,11 +278,12 @@ export type OrganizerVerificationWhereInput = {
|
||||
id?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
userId?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
fullName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
nik?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
nikEncrypted?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
nikHash?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
birthDate?: Prisma.DateTimeFilter<"OrganizerVerification"> | Date | string
|
||||
address?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
ktpImageUrl?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
selfieUrl?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
ktpImageKey?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
selfieKey?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankAccountNumber?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankAccountName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
@@ -294,11 +302,12 @@ export type OrganizerVerificationOrderByWithRelationInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
fullName?: Prisma.SortOrder
|
||||
nik?: Prisma.SortOrder
|
||||
nikEncrypted?: Prisma.SortOrder
|
||||
nikHash?: Prisma.SortOrder
|
||||
birthDate?: Prisma.SortOrder
|
||||
address?: Prisma.SortOrder
|
||||
ktpImageUrl?: Prisma.SortOrder
|
||||
selfieUrl?: Prisma.SortOrder
|
||||
ktpImageKey?: Prisma.SortOrder
|
||||
selfieKey?: Prisma.SortOrder
|
||||
bankName?: Prisma.SortOrder
|
||||
bankAccountNumber?: Prisma.SortOrder
|
||||
bankAccountName?: Prisma.SortOrder
|
||||
@@ -316,15 +325,16 @@ export type OrganizerVerificationOrderByWithRelationInput = {
|
||||
export type OrganizerVerificationWhereUniqueInput = Prisma.AtLeast<{
|
||||
id?: string
|
||||
userId?: string
|
||||
nik?: string
|
||||
nikHash?: string
|
||||
AND?: Prisma.OrganizerVerificationWhereInput | Prisma.OrganizerVerificationWhereInput[]
|
||||
OR?: Prisma.OrganizerVerificationWhereInput[]
|
||||
NOT?: Prisma.OrganizerVerificationWhereInput | Prisma.OrganizerVerificationWhereInput[]
|
||||
fullName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
nikEncrypted?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
birthDate?: Prisma.DateTimeFilter<"OrganizerVerification"> | Date | string
|
||||
address?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
ktpImageUrl?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
selfieUrl?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
ktpImageKey?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
selfieKey?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankAccountNumber?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankAccountName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
@@ -337,17 +347,18 @@ export type OrganizerVerificationWhereUniqueInput = Prisma.AtLeast<{
|
||||
updatedAt?: Prisma.DateTimeFilter<"OrganizerVerification"> | Date | string
|
||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
||||
reviewedBy?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null
|
||||
}, "id" | "userId" | "nik">
|
||||
}, "id" | "userId" | "nikHash">
|
||||
|
||||
export type OrganizerVerificationOrderByWithAggregationInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
fullName?: Prisma.SortOrder
|
||||
nik?: Prisma.SortOrder
|
||||
nikEncrypted?: Prisma.SortOrder
|
||||
nikHash?: Prisma.SortOrder
|
||||
birthDate?: Prisma.SortOrder
|
||||
address?: Prisma.SortOrder
|
||||
ktpImageUrl?: Prisma.SortOrder
|
||||
selfieUrl?: Prisma.SortOrder
|
||||
ktpImageKey?: Prisma.SortOrder
|
||||
selfieKey?: Prisma.SortOrder
|
||||
bankName?: Prisma.SortOrder
|
||||
bankAccountNumber?: Prisma.SortOrder
|
||||
bankAccountName?: Prisma.SortOrder
|
||||
@@ -370,11 +381,12 @@ export type OrganizerVerificationScalarWhereWithAggregatesInput = {
|
||||
id?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
userId?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
fullName?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
nik?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
nikEncrypted?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
nikHash?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
birthDate?: Prisma.DateTimeWithAggregatesFilter<"OrganizerVerification"> | Date | string
|
||||
address?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
ktpImageUrl?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
selfieUrl?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
ktpImageKey?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
selfieKey?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
bankName?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
bankAccountNumber?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
bankAccountName?: Prisma.StringWithAggregatesFilter<"OrganizerVerification"> | string
|
||||
@@ -390,11 +402,12 @@ export type OrganizerVerificationScalarWhereWithAggregatesInput = {
|
||||
export type OrganizerVerificationCreateInput = {
|
||||
id?: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -412,11 +425,12 @@ export type OrganizerVerificationUncheckedCreateInput = {
|
||||
id?: string
|
||||
userId: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -432,11 +446,12 @@ export type OrganizerVerificationUncheckedCreateInput = {
|
||||
export type OrganizerVerificationUpdateInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -454,11 +469,12 @@ export type OrganizerVerificationUncheckedUpdateInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -475,11 +491,12 @@ export type OrganizerVerificationCreateManyInput = {
|
||||
id?: string
|
||||
userId: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -495,11 +512,12 @@ export type OrganizerVerificationCreateManyInput = {
|
||||
export type OrganizerVerificationUpdateManyMutationInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -515,11 +533,12 @@ export type OrganizerVerificationUncheckedUpdateManyInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -551,11 +570,12 @@ export type OrganizerVerificationCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
fullName?: Prisma.SortOrder
|
||||
nik?: Prisma.SortOrder
|
||||
nikEncrypted?: Prisma.SortOrder
|
||||
nikHash?: Prisma.SortOrder
|
||||
birthDate?: Prisma.SortOrder
|
||||
address?: Prisma.SortOrder
|
||||
ktpImageUrl?: Prisma.SortOrder
|
||||
selfieUrl?: Prisma.SortOrder
|
||||
ktpImageKey?: Prisma.SortOrder
|
||||
selfieKey?: Prisma.SortOrder
|
||||
bankName?: Prisma.SortOrder
|
||||
bankAccountNumber?: Prisma.SortOrder
|
||||
bankAccountName?: Prisma.SortOrder
|
||||
@@ -572,11 +592,12 @@ export type OrganizerVerificationMaxOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
fullName?: Prisma.SortOrder
|
||||
nik?: Prisma.SortOrder
|
||||
nikEncrypted?: Prisma.SortOrder
|
||||
nikHash?: Prisma.SortOrder
|
||||
birthDate?: Prisma.SortOrder
|
||||
address?: Prisma.SortOrder
|
||||
ktpImageUrl?: Prisma.SortOrder
|
||||
selfieUrl?: Prisma.SortOrder
|
||||
ktpImageKey?: Prisma.SortOrder
|
||||
selfieKey?: Prisma.SortOrder
|
||||
bankName?: Prisma.SortOrder
|
||||
bankAccountNumber?: Prisma.SortOrder
|
||||
bankAccountName?: Prisma.SortOrder
|
||||
@@ -593,11 +614,12 @@ export type OrganizerVerificationMinOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
fullName?: Prisma.SortOrder
|
||||
nik?: Prisma.SortOrder
|
||||
nikEncrypted?: Prisma.SortOrder
|
||||
nikHash?: Prisma.SortOrder
|
||||
birthDate?: Prisma.SortOrder
|
||||
address?: Prisma.SortOrder
|
||||
ktpImageUrl?: Prisma.SortOrder
|
||||
selfieUrl?: Prisma.SortOrder
|
||||
ktpImageKey?: Prisma.SortOrder
|
||||
selfieKey?: Prisma.SortOrder
|
||||
bankName?: Prisma.SortOrder
|
||||
bankAccountNumber?: Prisma.SortOrder
|
||||
bankAccountName?: Prisma.SortOrder
|
||||
@@ -691,11 +713,12 @@ export type EnumVerificationStatusFieldUpdateOperationsInput = {
|
||||
export type OrganizerVerificationCreateWithoutUserInput = {
|
||||
id?: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -711,11 +734,12 @@ export type OrganizerVerificationCreateWithoutUserInput = {
|
||||
export type OrganizerVerificationUncheckedCreateWithoutUserInput = {
|
||||
id?: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -736,11 +760,12 @@ export type OrganizerVerificationCreateOrConnectWithoutUserInput = {
|
||||
export type OrganizerVerificationCreateWithoutReviewedByInput = {
|
||||
id?: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -757,11 +782,12 @@ export type OrganizerVerificationUncheckedCreateWithoutReviewedByInput = {
|
||||
id?: string
|
||||
userId: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -797,11 +823,12 @@ export type OrganizerVerificationUpdateToOneWithWhereWithoutUserInput = {
|
||||
export type OrganizerVerificationUpdateWithoutUserInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -817,11 +844,12 @@ export type OrganizerVerificationUpdateWithoutUserInput = {
|
||||
export type OrganizerVerificationUncheckedUpdateWithoutUserInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -857,11 +885,12 @@ export type OrganizerVerificationScalarWhereInput = {
|
||||
id?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
userId?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
fullName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
nik?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
nikEncrypted?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
nikHash?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
birthDate?: Prisma.DateTimeFilter<"OrganizerVerification"> | Date | string
|
||||
address?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
ktpImageUrl?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
selfieUrl?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
ktpImageKey?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
selfieKey?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankAccountNumber?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
bankAccountName?: Prisma.StringFilter<"OrganizerVerification"> | string
|
||||
@@ -878,11 +907,12 @@ export type OrganizerVerificationCreateManyReviewedByInput = {
|
||||
id?: string
|
||||
userId: string
|
||||
fullName: string
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
nikHash: string
|
||||
birthDate: Date | string
|
||||
address: string
|
||||
ktpImageUrl: string
|
||||
selfieUrl: string
|
||||
ktpImageKey: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -897,11 +927,12 @@ export type OrganizerVerificationCreateManyReviewedByInput = {
|
||||
export type OrganizerVerificationUpdateWithoutReviewedByInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -918,11 +949,12 @@ export type OrganizerVerificationUncheckedUpdateWithoutReviewedByInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -938,11 +970,12 @@ export type OrganizerVerificationUncheckedUpdateManyWithoutReviewedByInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nik?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikEncrypted?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
nikHash?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
birthDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
address?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieUrl?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
ktpImageKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
selfieKey?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
bankAccountName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
@@ -960,11 +993,12 @@ export type OrganizerVerificationSelect<ExtArgs extends runtime.Types.Extensions
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
fullName?: boolean
|
||||
nik?: boolean
|
||||
nikEncrypted?: boolean
|
||||
nikHash?: boolean
|
||||
birthDate?: boolean
|
||||
address?: boolean
|
||||
ktpImageUrl?: boolean
|
||||
selfieUrl?: boolean
|
||||
ktpImageKey?: boolean
|
||||
selfieKey?: boolean
|
||||
bankName?: boolean
|
||||
bankAccountNumber?: boolean
|
||||
bankAccountName?: boolean
|
||||
@@ -983,11 +1017,12 @@ export type OrganizerVerificationSelectCreateManyAndReturn<ExtArgs extends runti
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
fullName?: boolean
|
||||
nik?: boolean
|
||||
nikEncrypted?: boolean
|
||||
nikHash?: boolean
|
||||
birthDate?: boolean
|
||||
address?: boolean
|
||||
ktpImageUrl?: boolean
|
||||
selfieUrl?: boolean
|
||||
ktpImageKey?: boolean
|
||||
selfieKey?: boolean
|
||||
bankName?: boolean
|
||||
bankAccountNumber?: boolean
|
||||
bankAccountName?: boolean
|
||||
@@ -1006,11 +1041,12 @@ export type OrganizerVerificationSelectUpdateManyAndReturn<ExtArgs extends runti
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
fullName?: boolean
|
||||
nik?: boolean
|
||||
nikEncrypted?: boolean
|
||||
nikHash?: boolean
|
||||
birthDate?: boolean
|
||||
address?: boolean
|
||||
ktpImageUrl?: boolean
|
||||
selfieUrl?: boolean
|
||||
ktpImageKey?: boolean
|
||||
selfieKey?: boolean
|
||||
bankName?: boolean
|
||||
bankAccountNumber?: boolean
|
||||
bankAccountName?: boolean
|
||||
@@ -1029,11 +1065,12 @@ export type OrganizerVerificationSelectScalar = {
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
fullName?: boolean
|
||||
nik?: boolean
|
||||
nikEncrypted?: boolean
|
||||
nikHash?: boolean
|
||||
birthDate?: boolean
|
||||
address?: boolean
|
||||
ktpImageUrl?: boolean
|
||||
selfieUrl?: boolean
|
||||
ktpImageKey?: boolean
|
||||
selfieKey?: boolean
|
||||
bankName?: boolean
|
||||
bankAccountNumber?: boolean
|
||||
bankAccountName?: boolean
|
||||
@@ -1046,7 +1083,7 @@ export type OrganizerVerificationSelectScalar = {
|
||||
updatedAt?: boolean
|
||||
}
|
||||
|
||||
export type OrganizerVerificationOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "userId" | "fullName" | "nik" | "birthDate" | "address" | "ktpImageUrl" | "selfieUrl" | "bankName" | "bankAccountNumber" | "bankAccountName" | "status" | "rejectionReason" | "reviewedAt" | "reviewedById" | "verifiedAt" | "createdAt" | "updatedAt", ExtArgs["result"]["organizerVerification"]>
|
||||
export type OrganizerVerificationOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "userId" | "fullName" | "nikEncrypted" | "nikHash" | "birthDate" | "address" | "ktpImageKey" | "selfieKey" | "bankName" | "bankAccountNumber" | "bankAccountName" | "status" | "rejectionReason" | "reviewedAt" | "reviewedById" | "verifiedAt" | "createdAt" | "updatedAt", ExtArgs["result"]["organizerVerification"]>
|
||||
export type OrganizerVerificationInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
reviewedBy?: boolean | Prisma.OrganizerVerification$reviewedByArgs<ExtArgs>
|
||||
@@ -1074,19 +1111,23 @@ export type $OrganizerVerificationPayload<ExtArgs extends runtime.Types.Extensio
|
||||
*/
|
||||
fullName: string
|
||||
/**
|
||||
* Nomor Induk Kependudukan (PII — perlakukan sensitif)
|
||||
* NIK terenkripsi (AES-256-GCM, base64). Plaintext tidak disimpan.
|
||||
*/
|
||||
nik: string
|
||||
nikEncrypted: string
|
||||
/**
|
||||
* HMAC-SHA256(NIK + pepper) untuk uniqueness lookup tanpa membuka plaintext.
|
||||
*/
|
||||
nikHash: string
|
||||
birthDate: Date
|
||||
address: string
|
||||
/**
|
||||
* URL foto KTP (untuk MVP pakai hosting; pindah ke storage privat untuk produksi)
|
||||
* Storage key foto KTP (mis. `ktp/<id>.jpg`). File disimpan terenkripsi di luar /public.
|
||||
*/
|
||||
ktpImageUrl: string
|
||||
ktpImageKey: string
|
||||
/**
|
||||
* URL selfie memegang KTP
|
||||
* Storage key selfie memegang KTP.
|
||||
*/
|
||||
selfieUrl: string
|
||||
selfieKey: string
|
||||
bankName: string
|
||||
bankAccountNumber: string
|
||||
bankAccountName: string
|
||||
@@ -1525,11 +1566,12 @@ export interface OrganizerVerificationFieldRefs {
|
||||
readonly id: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly userId: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly fullName: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly nik: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly nikEncrypted: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly nikHash: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly birthDate: Prisma.FieldRef<"OrganizerVerification", 'DateTime'>
|
||||
readonly address: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly ktpImageUrl: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly selfieUrl: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly ktpImageKey: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly selfieKey: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly bankName: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly bankAccountNumber: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
readonly bankAccountName: Prisma.FieldRef<"OrganizerVerification", 'String'>
|
||||
|
||||
@@ -175,7 +175,7 @@ export type UserGroupByOutputType = {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password: string | null
|
||||
image: string | null
|
||||
acceptedTermsAndPrivacy: boolean
|
||||
acceptedAt: Date | null
|
||||
@@ -208,12 +208,13 @@ export type UserWhereInput = {
|
||||
id?: Prisma.StringFilter<"User"> | string
|
||||
name?: Prisma.StringFilter<"User"> | string
|
||||
email?: Prisma.StringFilter<"User"> | string
|
||||
password?: Prisma.StringFilter<"User"> | string
|
||||
password?: Prisma.StringNullableFilter<"User"> | string | null
|
||||
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFilter<"User"> | boolean
|
||||
acceptedAt?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||
accounts?: Prisma.AccountListRelationFilter
|
||||
trips?: Prisma.TripListRelationFilter
|
||||
participations?: Prisma.TripParticipantListRelationFilter
|
||||
tripReviews?: Prisma.TripReviewListRelationFilter
|
||||
@@ -225,12 +226,13 @@ export type UserOrderByWithRelationInput = {
|
||||
id?: Prisma.SortOrder
|
||||
name?: Prisma.SortOrder
|
||||
email?: Prisma.SortOrder
|
||||
password?: Prisma.SortOrder
|
||||
password?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
acceptedTermsAndPrivacy?: Prisma.SortOrder
|
||||
acceptedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
accounts?: Prisma.AccountOrderByRelationAggregateInput
|
||||
trips?: Prisma.TripOrderByRelationAggregateInput
|
||||
participations?: Prisma.TripParticipantOrderByRelationAggregateInput
|
||||
tripReviews?: Prisma.TripReviewOrderByRelationAggregateInput
|
||||
@@ -245,12 +247,13 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{
|
||||
OR?: Prisma.UserWhereInput[]
|
||||
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||
name?: Prisma.StringFilter<"User"> | string
|
||||
password?: Prisma.StringFilter<"User"> | string
|
||||
password?: Prisma.StringNullableFilter<"User"> | string | null
|
||||
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFilter<"User"> | boolean
|
||||
acceptedAt?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||
accounts?: Prisma.AccountListRelationFilter
|
||||
trips?: Prisma.TripListRelationFilter
|
||||
participations?: Prisma.TripParticipantListRelationFilter
|
||||
tripReviews?: Prisma.TripReviewListRelationFilter
|
||||
@@ -262,7 +265,7 @@ export type UserOrderByWithAggregationInput = {
|
||||
id?: Prisma.SortOrder
|
||||
name?: Prisma.SortOrder
|
||||
email?: Prisma.SortOrder
|
||||
password?: Prisma.SortOrder
|
||||
password?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
acceptedTermsAndPrivacy?: Prisma.SortOrder
|
||||
acceptedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
@@ -280,7 +283,7 @@ export type UserScalarWhereWithAggregatesInput = {
|
||||
id?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||
name?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||
email?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||
password?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||
password?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||
image?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolWithAggregatesFilter<"User"> | boolean
|
||||
acceptedAt?: Prisma.DateTimeNullableWithAggregatesFilter<"User"> | Date | string | null
|
||||
@@ -292,12 +295,13 @@ export type UserCreateInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||
@@ -309,12 +313,13 @@ export type UserUncheckedCreateInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||
@@ -326,12 +331,13 @@ export type UserUpdateInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||
@@ -343,12 +349,13 @@ export type UserUncheckedUpdateInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||
@@ -360,7 +367,7 @@ export type UserCreateManyInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
@@ -372,7 +379,7 @@ export type UserUpdateManyMutationInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
@@ -384,7 +391,7 @@ export type UserUncheckedUpdateManyInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
@@ -458,6 +465,20 @@ export type DateTimeFieldUpdateOperationsInput = {
|
||||
set?: Date | string
|
||||
}
|
||||
|
||||
export type UserCreateNestedOneWithoutAccountsInput = {
|
||||
create?: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAccountsInput
|
||||
connect?: Prisma.UserWhereUniqueInput
|
||||
}
|
||||
|
||||
export type UserUpdateOneRequiredWithoutAccountsNestedInput = {
|
||||
create?: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAccountsInput
|
||||
upsert?: Prisma.UserUpsertWithoutAccountsInput
|
||||
connect?: Prisma.UserWhereUniqueInput
|
||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutAccountsInput, Prisma.UserUpdateWithoutAccountsInput>, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||
}
|
||||
|
||||
export type UserCreateNestedOneWithoutOrganizerVerificationInput = {
|
||||
create?: Prisma.XOR<Prisma.UserCreateWithoutOrganizerVerificationInput, Prisma.UserUncheckedCreateWithoutOrganizerVerificationInput>
|
||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutOrganizerVerificationInput
|
||||
@@ -530,11 +551,11 @@ export type UserUpdateOneRequiredWithoutParticipationsNestedInput = {
|
||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutParticipationsInput, Prisma.UserUpdateWithoutParticipationsInput>, Prisma.UserUncheckedUpdateWithoutParticipationsInput>
|
||||
}
|
||||
|
||||
export type UserCreateWithoutOrganizerVerificationInput = {
|
||||
export type UserCreateWithoutAccountsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
@@ -543,6 +564,91 @@ export type UserCreateWithoutOrganizerVerificationInput = {
|
||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||
reviewedVerifications?: Prisma.OrganizerVerificationCreateNestedManyWithoutReviewedByInput
|
||||
}
|
||||
|
||||
export type UserUncheckedCreateWithoutAccountsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedCreateNestedManyWithoutReviewedByInput
|
||||
}
|
||||
|
||||
export type UserCreateOrConnectWithoutAccountsInput = {
|
||||
where: Prisma.UserWhereUniqueInput
|
||||
create: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||
}
|
||||
|
||||
export type UserUpsertWithoutAccountsInput = {
|
||||
update: Prisma.XOR<Prisma.UserUpdateWithoutAccountsInput, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||
create: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||
where?: Prisma.UserWhereInput
|
||||
}
|
||||
|
||||
export type UserUpdateToOneWithWhereWithoutAccountsInput = {
|
||||
where?: Prisma.UserWhereInput
|
||||
data: Prisma.XOR<Prisma.UserUpdateWithoutAccountsInput, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||
}
|
||||
|
||||
export type UserUpdateWithoutAccountsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||
reviewedVerifications?: Prisma.OrganizerVerificationUpdateManyWithoutReviewedByNestedInput
|
||||
}
|
||||
|
||||
export type UserUncheckedUpdateWithoutAccountsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||
reviewedVerifications?: Prisma.OrganizerVerificationUncheckedUpdateManyWithoutReviewedByNestedInput
|
||||
}
|
||||
|
||||
export type UserCreateWithoutOrganizerVerificationInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||
reviewedVerifications?: Prisma.OrganizerVerificationCreateNestedManyWithoutReviewedByInput
|
||||
}
|
||||
|
||||
@@ -550,12 +656,13 @@ export type UserUncheckedCreateWithoutOrganizerVerificationInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||
@@ -571,12 +678,13 @@ export type UserCreateWithoutReviewedVerificationsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||
@@ -587,12 +695,13 @@ export type UserUncheckedCreateWithoutReviewedVerificationsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||
@@ -619,12 +728,13 @@ export type UserUpdateWithoutOrganizerVerificationInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||
@@ -635,12 +745,13 @@ export type UserUncheckedUpdateWithoutOrganizerVerificationInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||
@@ -662,12 +773,13 @@ export type UserUpdateWithoutReviewedVerificationsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||
@@ -678,12 +790,13 @@ export type UserUncheckedUpdateWithoutReviewedVerificationsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||
@@ -694,12 +807,13 @@ export type UserCreateWithoutTripsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||
@@ -710,12 +824,13 @@ export type UserUncheckedCreateWithoutTripsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||
@@ -742,12 +857,13 @@ export type UserUpdateWithoutTripsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||
@@ -758,12 +874,13 @@ export type UserUncheckedUpdateWithoutTripsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||
@@ -774,12 +891,13 @@ export type UserCreateWithoutTripReviewsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||
@@ -790,12 +908,13 @@ export type UserUncheckedCreateWithoutTripReviewsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||
@@ -822,12 +941,13 @@ export type UserUpdateWithoutTripReviewsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||
@@ -838,12 +958,13 @@ export type UserUncheckedUpdateWithoutTripReviewsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||
@@ -854,12 +975,13 @@ export type UserCreateWithoutParticipationsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
|
||||
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationCreateNestedOneWithoutUserInput
|
||||
@@ -870,12 +992,13 @@ export type UserUncheckedCreateWithoutParticipationsInput = {
|
||||
id?: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
password?: string | null
|
||||
image?: string | null
|
||||
acceptedTermsAndPrivacy?: boolean
|
||||
acceptedAt?: Date | string | null
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedCreateNestedOneWithoutUserInput
|
||||
@@ -902,12 +1025,13 @@ export type UserUpdateWithoutParticipationsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
|
||||
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUpdateOneWithoutUserNestedInput
|
||||
@@ -918,12 +1042,13 @@ export type UserUncheckedUpdateWithoutParticipationsInput = {
|
||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
password?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
acceptedTermsAndPrivacy?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||
acceptedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
|
||||
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
|
||||
organizerVerification?: Prisma.OrganizerVerificationUncheckedUpdateOneWithoutUserNestedInput
|
||||
@@ -936,6 +1061,7 @@ export type UserUncheckedUpdateWithoutParticipationsInput = {
|
||||
*/
|
||||
|
||||
export type UserCountOutputType = {
|
||||
accounts: number
|
||||
trips: number
|
||||
participations: number
|
||||
tripReviews: number
|
||||
@@ -943,6 +1069,7 @@ export type UserCountOutputType = {
|
||||
}
|
||||
|
||||
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
accounts?: boolean | UserCountOutputTypeCountAccountsArgs
|
||||
trips?: boolean | UserCountOutputTypeCountTripsArgs
|
||||
participations?: boolean | UserCountOutputTypeCountParticipationsArgs
|
||||
tripReviews?: boolean | UserCountOutputTypeCountTripReviewsArgs
|
||||
@@ -959,6 +1086,13 @@ export type UserCountOutputTypeDefaultArgs<ExtArgs extends runtime.Types.Extensi
|
||||
select?: Prisma.UserCountOutputTypeSelect<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* UserCountOutputType without action
|
||||
*/
|
||||
export type UserCountOutputTypeCountAccountsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
where?: Prisma.AccountWhereInput
|
||||
}
|
||||
|
||||
/**
|
||||
* UserCountOutputType without action
|
||||
*/
|
||||
@@ -998,6 +1132,7 @@ export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
|
||||
acceptedAt?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
|
||||
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
|
||||
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
|
||||
@@ -1044,6 +1179,7 @@ export type UserSelectScalar = {
|
||||
|
||||
export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "email" | "password" | "image" | "acceptedTermsAndPrivacy" | "acceptedAt" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]>
|
||||
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
|
||||
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
|
||||
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
|
||||
@@ -1057,6 +1193,7 @@ export type UserIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensi
|
||||
export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "User"
|
||||
objects: {
|
||||
accounts: Prisma.$AccountPayload<ExtArgs>[]
|
||||
trips: Prisma.$TripPayload<ExtArgs>[]
|
||||
participations: Prisma.$TripParticipantPayload<ExtArgs>[]
|
||||
tripReviews: Prisma.$TripReviewPayload<ExtArgs>[]
|
||||
@@ -1067,7 +1204,10 @@ export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
/**
|
||||
* Hash bcrypt. Null untuk user yang sign-in via OAuth (mis. Google).
|
||||
*/
|
||||
password: string | null
|
||||
image: string | null
|
||||
/**
|
||||
* Apakah user telah menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||
@@ -1473,6 +1613,7 @@ readonly fields: UserFieldRefs;
|
||||
*/
|
||||
export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||
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>
|
||||
trips<T extends Prisma.User$tripsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
participations<T extends Prisma.User$participationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$participationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripParticipantPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
tripReviews<T extends Prisma.User$tripReviewsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripReviewsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripReviewPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
@@ -1908,6 +2049,30 @@ export type UserDeleteManyArgs<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* User.accounts
|
||||
*/
|
||||
export type User$accountsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Account
|
||||
*/
|
||||
select?: Prisma.AccountSelect<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Account
|
||||
*/
|
||||
omit?: Prisma.AccountOmit<ExtArgs> | null
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.AccountInclude<ExtArgs> | null
|
||||
where?: Prisma.AccountWhereInput
|
||||
orderBy?: Prisma.AccountOrderByWithRelationInput | Prisma.AccountOrderByWithRelationInput[]
|
||||
cursor?: Prisma.AccountWhereUniqueInput
|
||||
take?: number
|
||||
skip?: number
|
||||
distinct?: Prisma.AccountScalarFieldEnum | Prisma.AccountScalarFieldEnum[]
|
||||
}
|
||||
|
||||
/**
|
||||
* User.trips
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import { signIn } from "next-auth/react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { GoogleSignInButton } from "@/components/shared/google-sign-in-button";
|
||||
|
||||
function safeInternalPath(raw: string | null): string {
|
||||
if (!raw || !raw.startsWith("/") || raw.startsWith("//")) return "/";
|
||||
@@ -84,6 +85,14 @@ function LoginForm() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<GoogleSignInButton callbackUrl={safeInternalPath(searchParams.get("callbackUrl"))} />
|
||||
|
||||
<div className="my-4 flex items-center gap-3 text-xs text-neutral-400">
|
||||
<span className="h-px flex-1 bg-neutral-200" />
|
||||
<span>atau</span>
|
||||
<span className="h-px flex-1 bg-neutral-200" />
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="email" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||
|
||||
@@ -6,6 +6,7 @@ import { signIn } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { registerAction } from "@/features/auth/actions";
|
||||
import { GoogleSignInButton } from "@/components/shared/google-sign-in-button";
|
||||
|
||||
export default function RegisterPage() {
|
||||
const router = useRouter();
|
||||
@@ -83,6 +84,14 @@ export default function RegisterPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<GoogleSignInButton label="Daftar dengan Google" />
|
||||
|
||||
<div className="my-4 flex items-center gap-3 text-xs text-neutral-400">
|
||||
<span className="h-px flex-1 bg-neutral-200" />
|
||||
<span>atau</span>
|
||||
<span className="h-px flex-1 bg-neutral-200" />
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="name" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||
|
||||
+15
-1
@@ -14,6 +14,20 @@ export default async function VerifyPage() {
|
||||
|
||||
const verification = await organizerService.getStatusForUser(session.user.id);
|
||||
|
||||
const initial = verification
|
||||
? {
|
||||
fullName: verification.fullName,
|
||||
nik: organizerService.decryptNik(verification.nikEncrypted),
|
||||
birthDate: verification.birthDate,
|
||||
address: verification.address,
|
||||
ktpImageKey: verification.ktpImageKey,
|
||||
selfieKey: verification.selfieKey,
|
||||
bankName: verification.bankName,
|
||||
bankAccountNumber: verification.bankAccountNumber,
|
||||
bankAccountName: verification.bankAccountName,
|
||||
}
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-2xl px-4 py-8 sm:py-12">
|
||||
<div className="mb-6">
|
||||
@@ -66,7 +80,7 @@ export default async function VerifyPage() {
|
||||
)}
|
||||
|
||||
{verification?.status !== "APPROVED" && verification?.status !== "PENDING" && (
|
||||
<VerifyForm initial={verification ?? null} />
|
||||
<VerifyForm initial={initial} />
|
||||
)}
|
||||
|
||||
<p className="mt-6 text-center text-sm text-neutral-500">
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import { signIn } from "next-auth/react";
|
||||
import { useState } from "react";
|
||||
|
||||
export function GoogleSignInButton({
|
||||
callbackUrl = "/",
|
||||
label = "Lanjutkan dengan Google",
|
||||
}: {
|
||||
callbackUrl?: string;
|
||||
label?: string;
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
signIn("google", { callbackUrl });
|
||||
}}
|
||||
className="flex w-full items-center justify-center gap-2 rounded-xl border border-neutral-200 bg-white py-2.5 text-sm font-semibold text-neutral-700 shadow-sm transition-colors hover:bg-neutral-50 disabled:opacity-50"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" aria-hidden>
|
||||
<path
|
||||
fill="#4285F4"
|
||||
d="M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.48h4.84a4.13 4.13 0 0 1-1.79 2.71v2.26h2.9c1.7-1.57 2.69-3.88 2.69-6.61z"
|
||||
/>
|
||||
<path
|
||||
fill="#34A853"
|
||||
d="M9 18c2.43 0 4.47-.81 5.96-2.18l-2.9-2.26c-.8.54-1.83.86-3.06.86-2.35 0-4.34-1.59-5.05-3.72H.96v2.34A9 9 0 0 0 9 18z"
|
||||
/>
|
||||
<path
|
||||
fill="#FBBC05"
|
||||
d="M3.95 10.7A5.41 5.41 0 0 1 3.66 9c0-.59.1-1.16.29-1.7V4.96H.96A9 9 0 0 0 0 9c0 1.45.35 2.82.96 4.04l2.99-2.34z"
|
||||
/>
|
||||
<path
|
||||
fill="#EA4335"
|
||||
d="M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.58A9 9 0 0 0 9 0 9 9 0 0 0 .96 4.96l2.99 2.34C4.66 5.17 6.65 3.58 9 3.58z"
|
||||
/>
|
||||
</svg>
|
||||
{loading ? "Menghubungkan..." : label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
|
||||
+12
@@ -3,3 +3,15 @@ NEXTAUTH_SECRET="3GaP/mqi1IYbafyLfyI54ouPRDE0IUK5vFqpKJQM5hg="
|
||||
NEXTAUTH_URL="http://localhost:3000"
|
||||
NEXT_PUBLIC_SITE_URL="https://arifal.imola.ai"
|
||||
ADMIN_EMAILS=admin@setrip.id
|
||||
|
||||
# 32-byte key (hex) for AES-256-GCM encryption of KYC data (NIK + KTP/selfie files)
|
||||
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||
KYC_ENCRYPTION_KEY=
|
||||
# 32-byte hex secret used as HMAC pepper for NIK uniqueness lookup
|
||||
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||
KYC_NIK_PEPPER=
|
||||
# Absolute path for private KYC uploads (default: <cwd>/uploads/private)
|
||||
KYC_UPLOAD_DIR=
|
||||
|
||||
GOOGLE_CLIENT_ID="xxxxxxxx"
|
||||
GOOGLE_CLIENT_SECRET="xxxxxxxx"
|
||||
@@ -1,7 +1,10 @@
|
||||
"use server";
|
||||
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import { registerSchema } from "./schemas";
|
||||
import { authService } from "@/server/services/auth.service";
|
||||
import { userRepo } from "@/server/repositories/user.repo";
|
||||
|
||||
export async function registerAction(formData: FormData) {
|
||||
const raw = {
|
||||
@@ -29,3 +32,16 @@ export async function registerAction(formData: FormData) {
|
||||
return { error: (err as Error).message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function acceptTermsAction() {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user) {
|
||||
return { error: "Kamu harus login terlebih dahulu" };
|
||||
}
|
||||
try {
|
||||
await userRepo.markAcceptedTerms(session.user.id);
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
return { error: (err as Error).message };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ export async function submitVerificationAction(formData: FormData) {
|
||||
nik: formData.get("nik") as string,
|
||||
birthDate: formData.get("birthDate") as string,
|
||||
address: formData.get("address") as string,
|
||||
ktpImageUrl: formData.get("ktpImageUrl") as string,
|
||||
selfieUrl: formData.get("selfieUrl") as string,
|
||||
ktpImageKey: formData.get("ktpImageKey") as string,
|
||||
selfieKey: formData.get("selfieKey") as string,
|
||||
bankName: formData.get("bankName") as string,
|
||||
bankAccountNumber: formData.get("bankAccountNumber") as string,
|
||||
bankAccountName: formData.get("bankAccountName") as string,
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
import { reviewVerificationAction } from "@/features/organizer/actions";
|
||||
|
||||
type Verification = {
|
||||
id: string;
|
||||
fullName: string;
|
||||
/** NIK plaintext, sudah di-decrypt di server sebelum sampai ke komponen ini. */
|
||||
nik: string;
|
||||
birthDate: Date;
|
||||
address: string;
|
||||
ktpImageUrl: string;
|
||||
selfieUrl: string;
|
||||
bankName: string;
|
||||
bankAccountNumber: string;
|
||||
bankAccountName: string;
|
||||
@@ -90,8 +88,14 @@ export function ReviewCard({ verification }: { verification: Verification }) {
|
||||
</div>
|
||||
|
||||
<div className="mt-5 grid gap-4 sm:grid-cols-2">
|
||||
<ImagePreview label="Foto KTP" url={verification.ktpImageUrl} />
|
||||
<ImagePreview label="Selfie + KTP" url={verification.selfieUrl} />
|
||||
<ImagePreview
|
||||
label="Foto KTP"
|
||||
src={`/api/files/kyc/${verification.id}/ktp`}
|
||||
/>
|
||||
<ImagePreview
|
||||
label="Selfie + KTP"
|
||||
src={`/api/files/kyc/${verification.id}/selfie`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{verification.status === "REJECTED" && verification.rejectionReason && (
|
||||
@@ -196,26 +200,25 @@ function Field({
|
||||
);
|
||||
}
|
||||
|
||||
function ImagePreview({ label, url }: { label: string; url: string }) {
|
||||
function ImagePreview({ label, src }: { label: string; src: string }) {
|
||||
return (
|
||||
<div>
|
||||
<p className="mb-1.5 text-xs font-semibold uppercase tracking-wide text-neutral-500">
|
||||
{label}
|
||||
</p>
|
||||
<a
|
||||
href={url}
|
||||
href={src}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block overflow-hidden rounded-xl border border-neutral-200 bg-neutral-100"
|
||||
>
|
||||
<div className="relative aspect-[4/3] w-full">
|
||||
<Image
|
||||
src={url}
|
||||
{/* Secure endpoint sends Cache-Control: private,no-store. Use plain <img> to skip Next/Image optimizer. */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={src}
|
||||
alt={label}
|
||||
fill
|
||||
unoptimized
|
||||
className="object-cover"
|
||||
sizes="(min-width: 640px) 50vw, 100vw"
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -9,13 +9,18 @@ type Initial = {
|
||||
nik: string;
|
||||
birthDate: Date;
|
||||
address: string;
|
||||
ktpImageUrl: string;
|
||||
selfieUrl: string;
|
||||
ktpImageKey: string;
|
||||
selfieKey: string;
|
||||
bankName: string;
|
||||
bankAccountNumber: string;
|
||||
bankAccountName: string;
|
||||
} | null;
|
||||
|
||||
type UploadKind = "ktp" | "selfie";
|
||||
|
||||
const ACCEPT_MIME = "image/jpeg,image/png,image/webp";
|
||||
const MAX_BYTES = 5 * 1024 * 1024;
|
||||
|
||||
function toYmd(d: Date): string {
|
||||
const y = d.getUTCFullYear();
|
||||
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
||||
@@ -27,12 +32,20 @@ export function VerifyForm({ initial }: { initial: Initial }) {
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [ktpKey, setKtpKey] = useState(initial?.ktpImageKey ?? "");
|
||||
const [selfieKey, setSelfieKey] = useState(initial?.selfieKey ?? "");
|
||||
|
||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
setError("");
|
||||
if (!ktpKey || !selfieKey) {
|
||||
setError("Foto KTP dan selfie wajib diunggah");
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const formData = new FormData(e.currentTarget);
|
||||
formData.set("ktpImageKey", ktpKey);
|
||||
formData.set("selfieKey", selfieKey);
|
||||
const result = await submitVerificationAction(formData);
|
||||
setLoading(false);
|
||||
if (result.error) {
|
||||
@@ -119,36 +132,24 @@ export function VerifyForm({ initial }: { initial: Initial }) {
|
||||
<section>
|
||||
<h2 className="mb-3 text-base font-bold text-neutral-900">🖼️ Foto</h2>
|
||||
<p className="mb-3 text-xs text-neutral-500">
|
||||
Upload foto ke hosting (imgur, imgbb, dll) lalu paste URL-nya. Foto akan
|
||||
dilihat tim admin saat review.
|
||||
Foto disimpan terenkripsi di server SeTrip dan hanya bisa dilihat oleh
|
||||
tim admin saat review. Maks 5MB, JPG/PNG/WebP.
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||
URL Foto KTP
|
||||
</label>
|
||||
<input
|
||||
name="ktpImageUrl"
|
||||
type="url"
|
||||
required
|
||||
defaultValue={initial?.ktpImageUrl ?? ""}
|
||||
className={inputCls}
|
||||
placeholder="https://i.imgur.com/xxxx.jpg"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||
URL Selfie dengan KTP
|
||||
</label>
|
||||
<input
|
||||
name="selfieUrl"
|
||||
type="url"
|
||||
required
|
||||
defaultValue={initial?.selfieUrl ?? ""}
|
||||
className={inputCls}
|
||||
placeholder="https://i.imgur.com/yyyy.jpg"
|
||||
/>
|
||||
</div>
|
||||
<FileUpload
|
||||
label="Foto KTP"
|
||||
kind="ktp"
|
||||
value={ktpKey}
|
||||
onChange={setKtpKey}
|
||||
onError={setError}
|
||||
/>
|
||||
<FileUpload
|
||||
label="Selfie dengan KTP"
|
||||
kind="selfie"
|
||||
value={selfieKey}
|
||||
onChange={setSelfieKey}
|
||||
onError={setError}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -213,3 +214,91 @@ export function VerifyForm({ initial }: { initial: Initial }) {
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function FileUpload({
|
||||
label,
|
||||
kind,
|
||||
value,
|
||||
onChange,
|
||||
onError,
|
||||
}: {
|
||||
label: string;
|
||||
kind: UploadKind;
|
||||
value: string;
|
||||
onChange: (key: string) => void;
|
||||
onError: (msg: string) => void;
|
||||
}) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [previewUrl, setPreviewUrl] = useState<string>("");
|
||||
|
||||
async function onPick(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
if (file.size > MAX_BYTES) {
|
||||
onError(`${label} maksimal 5MB`);
|
||||
e.target.value = "";
|
||||
return;
|
||||
}
|
||||
if (!ACCEPT_MIME.split(",").includes(file.type)) {
|
||||
onError(`${label} harus JPG, PNG, atau WebP`);
|
||||
e.target.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
setBusy(true);
|
||||
onError("");
|
||||
try {
|
||||
const fd = new FormData();
|
||||
fd.set("kind", kind);
|
||||
fd.set("file", file);
|
||||
const res = await fetch("/api/upload/kyc", { method: "POST", body: fd });
|
||||
const json = await res.json();
|
||||
if (!res.ok) {
|
||||
onError(json.error ?? `Gagal mengunggah ${label}`);
|
||||
return;
|
||||
}
|
||||
onChange(json.key);
|
||||
const obj = URL.createObjectURL(file);
|
||||
setPreviewUrl((old) => {
|
||||
if (old) URL.revokeObjectURL(old);
|
||||
return obj;
|
||||
});
|
||||
} catch {
|
||||
onError(`Gagal mengunggah ${label}`);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
e.target.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||
{label}
|
||||
</label>
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="inline-flex cursor-pointer items-center rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2 text-sm font-medium text-neutral-700 hover:bg-neutral-100">
|
||||
{busy ? "Mengunggah..." : value ? "Ganti file" : "Pilih file"}
|
||||
<input
|
||||
type="file"
|
||||
accept={ACCEPT_MIME}
|
||||
onChange={onPick}
|
||||
disabled={busy}
|
||||
className="sr-only"
|
||||
/>
|
||||
</label>
|
||||
{value && !busy && (
|
||||
<span className="text-xs text-neutral-500">✓ Terunggah</span>
|
||||
)}
|
||||
</div>
|
||||
{previewUrl && (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={previewUrl}
|
||||
alt={`${label} preview`}
|
||||
className="mt-2 max-h-40 rounded-lg border border-neutral-200"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,18 +22,14 @@ export const submitVerificationSchema = z.object({
|
||||
.trim()
|
||||
.min(5, "Alamat minimal 5 karakter")
|
||||
.max(LIMITS.MAX_ADDRESS_LENGTH, `Alamat maksimal ${LIMITS.MAX_ADDRESS_LENGTH} karakter`),
|
||||
ktpImageUrl: z
|
||||
ktpImageKey: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Foto KTP wajib diisi")
|
||||
.max(LIMITS.MAX_URL_LENGTH, "URL foto KTP terlalu panjang")
|
||||
.pipe(z.url("URL foto KTP tidak valid")),
|
||||
selfieUrl: z
|
||||
.regex(/^ktp\/[A-Za-z0-9_-]+\.(jpg|png|webp)$/, "Foto KTP wajib diunggah"),
|
||||
selfieKey: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Foto selfie dengan KTP wajib diisi")
|
||||
.max(LIMITS.MAX_URL_LENGTH, "URL foto selfie terlalu panjang")
|
||||
.pipe(z.url("URL foto selfie tidak valid")),
|
||||
.regex(/^selfie\/[A-Za-z0-9_-]+\.(jpg|png|webp)$/, "Foto selfie wajib diunggah"),
|
||||
bankName: z
|
||||
.string()
|
||||
.trim()
|
||||
|
||||
+27
-1
@@ -1,10 +1,22 @@
|
||||
import { AuthOptions } from "next-auth";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import GoogleProvider from "next-auth/providers/google";
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Adapter dipakai untuk persist User + Account saat OAuth (Google).
|
||||
// Session tetap pakai JWT supaya kompatibel dengan CredentialsProvider.
|
||||
export const authOptions: AuthOptions = {
|
||||
adapter: PrismaAdapter(prisma),
|
||||
providers: [
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
// Auto-link kalau email Google sama dengan email user yang sudah register
|
||||
// via Credentials. Aman karena Google selalu memverifikasi email pemilik akun.
|
||||
allowDangerousEmailAccountLinking: true,
|
||||
}),
|
||||
CredentialsProvider({
|
||||
name: "credentials",
|
||||
credentials: {
|
||||
@@ -24,6 +36,10 @@ export const authOptions: AuthOptions = {
|
||||
throw new Error("Email tidak ditemukan");
|
||||
}
|
||||
|
||||
if (!user.password) {
|
||||
throw new Error("Akun ini terdaftar via Google. Silakan login dengan Google.");
|
||||
}
|
||||
|
||||
const isPasswordValid = await bcrypt.compare(
|
||||
credentials.password,
|
||||
user.password
|
||||
@@ -46,15 +62,25 @@ export const authOptions: AuthOptions = {
|
||||
strategy: "jwt",
|
||||
},
|
||||
callbacks: {
|
||||
async jwt({ token, user }) {
|
||||
async jwt({ token, user, trigger }) {
|
||||
if (user) {
|
||||
token.id = user.id;
|
||||
}
|
||||
// Hidrasi `acceptedTermsAndPrivacy` dari DB pada login pertama dan setiap
|
||||
// kali client memanggil `useSession().update()` (setelah user accept).
|
||||
if (token.id && (trigger === "update" || token.acceptedTermsAndPrivacy === undefined)) {
|
||||
const dbUser = await prisma.user.findUnique({
|
||||
where: { id: token.id as string },
|
||||
select: { acceptedTermsAndPrivacy: true },
|
||||
});
|
||||
token.acceptedTermsAndPrivacy = dbUser?.acceptedTermsAndPrivacy ?? false;
|
||||
}
|
||||
return token;
|
||||
},
|
||||
async session({ session, token }) {
|
||||
if (session.user) {
|
||||
session.user.id = token.id as string;
|
||||
session.user.acceptedTermsAndPrivacy = token.acceptedTermsAndPrivacy ?? false;
|
||||
}
|
||||
return session;
|
||||
},
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
const ALGO = "aes-256-gcm";
|
||||
const IV_LEN = 12;
|
||||
const TAG_LEN = 16;
|
||||
|
||||
function readKey(envName: string): Buffer {
|
||||
const hex = process.env[envName];
|
||||
if (!hex) throw new Error(`Missing env ${envName}`);
|
||||
if (hex.length !== 64) {
|
||||
throw new Error(`${envName} must be 64 hex chars (32 bytes)`);
|
||||
}
|
||||
return Buffer.from(hex, "hex");
|
||||
}
|
||||
|
||||
function getEncKey(): Buffer {
|
||||
return readKey("KYC_ENCRYPTION_KEY");
|
||||
}
|
||||
|
||||
function getNikPepper(): Buffer {
|
||||
return readKey("KYC_NIK_PEPPER");
|
||||
}
|
||||
|
||||
/** Encrypt a Buffer with AES-256-GCM. Output layout: [iv(12) | tag(16) | ciphertext]. */
|
||||
export function encryptBuffer(plain: Buffer): Buffer {
|
||||
const iv = crypto.randomBytes(IV_LEN);
|
||||
const cipher = crypto.createCipheriv(ALGO, getEncKey(), iv);
|
||||
const ct = Buffer.concat([cipher.update(plain), cipher.final()]);
|
||||
const tag = cipher.getAuthTag();
|
||||
return Buffer.concat([iv, tag, ct]);
|
||||
}
|
||||
|
||||
export function decryptBuffer(blob: Buffer): Buffer {
|
||||
if (blob.length < IV_LEN + TAG_LEN) throw new Error("Ciphertext too short");
|
||||
const iv = blob.subarray(0, IV_LEN);
|
||||
const tag = blob.subarray(IV_LEN, IV_LEN + TAG_LEN);
|
||||
const ct = blob.subarray(IV_LEN + TAG_LEN);
|
||||
const decipher = crypto.createDecipheriv(ALGO, getEncKey(), iv);
|
||||
decipher.setAuthTag(tag);
|
||||
return Buffer.concat([decipher.update(ct), decipher.final()]);
|
||||
}
|
||||
|
||||
/** Encrypt UTF-8 string -> base64 string. Used for short PII like NIK. */
|
||||
export function encryptString(plain: string): string {
|
||||
return encryptBuffer(Buffer.from(plain, "utf8")).toString("base64");
|
||||
}
|
||||
|
||||
export function decryptString(b64: string): string {
|
||||
return decryptBuffer(Buffer.from(b64, "base64")).toString("utf8");
|
||||
}
|
||||
|
||||
/** Deterministic HMAC-SHA256 of a normalized value, hex-encoded. Used for unique-lookup of NIK without storing plaintext. */
|
||||
export function hmacHex(value: string): string {
|
||||
return crypto.createHmac("sha256", getNikPepper()).update(value).digest("hex");
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
import crypto from "node:crypto";
|
||||
import { encryptBuffer, decryptBuffer } from "@/lib/crypto";
|
||||
|
||||
export type KycKind = "ktp" | "selfie";
|
||||
|
||||
const KIND_DIRS: Record<KycKind, string> = {
|
||||
ktp: "ktp",
|
||||
selfie: "selfie",
|
||||
};
|
||||
|
||||
/** Bytes. ~5MB matches the form limit; raise here if you change the upload route. */
|
||||
export const MAX_KYC_FILE_BYTES = 5 * 1024 * 1024;
|
||||
|
||||
export const ALLOWED_KYC_MIME = new Set([
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/webp",
|
||||
]);
|
||||
|
||||
const EXT_BY_MIME: Record<string, string> = {
|
||||
"image/jpeg": "jpg",
|
||||
"image/png": "png",
|
||||
"image/webp": "webp",
|
||||
};
|
||||
|
||||
function rootDir(): string {
|
||||
const fromEnv = process.env.KYC_UPLOAD_DIR;
|
||||
if (fromEnv && fromEnv.trim().length > 0) return fromEnv;
|
||||
return path.join(process.cwd(), "uploads", "private");
|
||||
}
|
||||
|
||||
function dirFor(kind: KycKind): string {
|
||||
return path.join(rootDir(), KIND_DIRS[kind]);
|
||||
}
|
||||
|
||||
/** Storage key written into DB: `<kind>/<id>.<ext>`. The kind segment is enforced to match the route. */
|
||||
export type StoredFileMeta = {
|
||||
key: string;
|
||||
mime: string;
|
||||
size: number;
|
||||
};
|
||||
|
||||
export function isKycKind(value: string): value is KycKind {
|
||||
return value === "ktp" || value === "selfie";
|
||||
}
|
||||
|
||||
/** Resolve a storage key (`ktp/abc.jpg`) to an absolute path inside the upload dir. Throws on traversal. */
|
||||
function resolveKey(kind: KycKind, key: string): string {
|
||||
const expectedPrefix = `${KIND_DIRS[kind]}/`;
|
||||
if (!key.startsWith(expectedPrefix)) {
|
||||
throw new Error("Storage key does not match kind");
|
||||
}
|
||||
const relative = key.slice(expectedPrefix.length);
|
||||
if (!/^[A-Za-z0-9_-]+\.(jpg|png|webp)$/.test(relative)) {
|
||||
throw new Error("Storage key has invalid characters");
|
||||
}
|
||||
const abs = path.join(dirFor(kind), relative);
|
||||
const dir = dirFor(kind);
|
||||
if (!abs.startsWith(dir + path.sep) && abs !== dir) {
|
||||
throw new Error("Storage key escapes upload directory");
|
||||
}
|
||||
return abs;
|
||||
}
|
||||
|
||||
export async function saveEncrypted(
|
||||
kind: KycKind,
|
||||
data: Buffer,
|
||||
mime: string,
|
||||
): Promise<StoredFileMeta> {
|
||||
if (!ALLOWED_KYC_MIME.has(mime)) throw new Error("Tipe file tidak didukung");
|
||||
if (data.length === 0) throw new Error("File kosong");
|
||||
if (data.length > MAX_KYC_FILE_BYTES) throw new Error("File terlalu besar");
|
||||
|
||||
const ext = EXT_BY_MIME[mime];
|
||||
const id = crypto.randomBytes(16).toString("hex");
|
||||
const key = `${KIND_DIRS[kind]}/${id}.${ext}`;
|
||||
const abs = resolveKey(kind, key);
|
||||
await fs.mkdir(path.dirname(abs), { recursive: true });
|
||||
const blob = encryptBuffer(data);
|
||||
await fs.writeFile(abs, blob, { mode: 0o600 });
|
||||
return { key, mime, size: data.length };
|
||||
}
|
||||
|
||||
export async function readDecrypted(kind: KycKind, key: string): Promise<Buffer> {
|
||||
const abs = resolveKey(kind, key);
|
||||
const blob = await fs.readFile(abs);
|
||||
return decryptBuffer(blob);
|
||||
}
|
||||
|
||||
export async function deleteFile(kind: KycKind, key: string): Promise<void> {
|
||||
const abs = resolveKey(kind, key);
|
||||
await fs.rm(abs, { force: true });
|
||||
}
|
||||
|
||||
export function mimeFromKey(key: string): string {
|
||||
if (key.endsWith(".jpg")) return "image/jpeg";
|
||||
if (key.endsWith(".png")) return "image/png";
|
||||
if (key.endsWith(".webp")) return "image/webp";
|
||||
return "application/octet-stream";
|
||||
}
|
||||
Generated
+11
@@ -8,6 +8,7 @@
|
||||
"name": "setrip",
|
||||
"version": "0.2.0",
|
||||
"dependencies": {
|
||||
"@next-auth/prisma-adapter": "^1.0.7",
|
||||
"@prisma/adapter-pg": "^7.7.0",
|
||||
"@prisma/client": "^7.7.0",
|
||||
"axios": "^1.15.0",
|
||||
@@ -1645,6 +1646,16 @@
|
||||
"@tybys/wasm-util": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@next-auth/prisma-adapter": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.7.tgz",
|
||||
"integrity": "sha512-Cdko4KfcmKjsyHFrWwZ//lfLUbcLqlyFqjd/nYE2m3aZ7tjMNUjpks47iw7NTCnXf+5UWz5Ypyt1dSs1EP5QJw==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"@prisma/client": ">=2.26.0 || >=3",
|
||||
"next-auth": "^4"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "16.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.3.tgz",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"seed": "npx tsx prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next-auth/prisma-adapter": "^1.0.7",
|
||||
"@prisma/adapter-pg": "^7.7.0",
|
||||
"@prisma/client": "^7.7.0",
|
||||
"axios": "^1.15.0",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `ktpImageUrl` on the `OrganizerVerification` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `nik` on the `OrganizerVerification` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `selfieUrl` on the `OrganizerVerification` table. All the data in the column will be lost.
|
||||
- A unique constraint covering the columns `[nikHash]` on the table `OrganizerVerification` will be added. If there are existing duplicate values, this will fail.
|
||||
- Added the required column `ktpImageKey` to the `OrganizerVerification` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `nikEncrypted` to the `OrganizerVerification` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `nikHash` to the `OrganizerVerification` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `selfieKey` to the `OrganizerVerification` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- DropIndex
|
||||
DROP INDEX "OrganizerVerification_nik_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "OrganizerVerification" DROP COLUMN "ktpImageUrl",
|
||||
DROP COLUMN "nik",
|
||||
DROP COLUMN "selfieUrl",
|
||||
ADD COLUMN "ktpImageKey" TEXT NOT NULL,
|
||||
ADD COLUMN "nikEncrypted" TEXT NOT NULL,
|
||||
ADD COLUMN "nikHash" TEXT NOT NULL,
|
||||
ADD COLUMN "selfieKey" TEXT NOT NULL;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "OrganizerVerification_nikHash_key" ON "OrganizerVerification"("nikHash");
|
||||
@@ -0,0 +1,26 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ALTER COLUMN "password" DROP NOT NULL;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Account" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerAccountId" TEXT NOT NULL,
|
||||
"refresh_token" TEXT,
|
||||
"access_token" TEXT,
|
||||
"expires_at" INTEGER,
|
||||
"token_type" TEXT,
|
||||
"scope" TEXT,
|
||||
"id_token" TEXT,
|
||||
"session_state" TEXT,
|
||||
|
||||
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
+32
-7
@@ -11,7 +11,8 @@ model User {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
email String @unique
|
||||
password String
|
||||
/// Hash bcrypt. Null untuk user yang sign-in via OAuth (mis. Google).
|
||||
password String?
|
||||
image String?
|
||||
/// Apakah user telah menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||
acceptedTermsAndPrivacy Boolean @default(false)
|
||||
@@ -20,6 +21,7 @@ model User {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
accounts Account[]
|
||||
trips Trip[]
|
||||
participations TripParticipant[]
|
||||
tripReviews TripReview[]
|
||||
@@ -28,6 +30,27 @@ model User {
|
||||
reviewedVerifications OrganizerVerification[] @relation("OrganizerVerificationReviewer")
|
||||
}
|
||||
|
||||
/// Tabel link akun OAuth pihak ketiga (Google, dst). Diisi oleh PrismaAdapter NextAuth.
|
||||
/// Session tidak pakai DB — kita pakai JWT, jadi Session/VerificationToken tidak perlu.
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model OrganizerVerification {
|
||||
id String @id @default(cuid())
|
||||
userId String @unique
|
||||
@@ -35,15 +58,17 @@ model OrganizerVerification {
|
||||
|
||||
/// Nama lengkap sesuai KTP
|
||||
fullName String
|
||||
/// Nomor Induk Kependudukan (PII — perlakukan sensitif)
|
||||
nik String @unique
|
||||
/// NIK terenkripsi (AES-256-GCM, base64). Plaintext tidak disimpan.
|
||||
nikEncrypted String
|
||||
/// HMAC-SHA256(NIK + pepper) untuk uniqueness lookup tanpa membuka plaintext.
|
||||
nikHash String @unique
|
||||
birthDate DateTime
|
||||
address String
|
||||
|
||||
/// URL foto KTP (untuk MVP pakai hosting; pindah ke storage privat untuk produksi)
|
||||
ktpImageUrl String
|
||||
/// URL selfie memegang KTP
|
||||
selfieUrl String
|
||||
/// Storage key foto KTP (mis. `ktp/<id>.jpg`). File disimpan terenkripsi di luar /public.
|
||||
ktpImageKey String
|
||||
/// Storage key selfie memegang KTP.
|
||||
selfieKey String
|
||||
|
||||
bankName String
|
||||
bankAccountNumber String
|
||||
|
||||
+11
-6
@@ -2,6 +2,7 @@ import "dotenv/config";
|
||||
import { PrismaClient } from "../app/generated/prisma/client";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { encryptString, hmacHex } from "../lib/crypto";
|
||||
|
||||
const adapter = new PrismaPg({
|
||||
connectionString: process.env.DATABASE_URL!,
|
||||
@@ -97,16 +98,19 @@ async function main() {
|
||||
// ==================== ORGANIZER VERIFICATIONS ====================
|
||||
|
||||
const verifiedAt = new Date();
|
||||
const dedeNik = "3201010101010001";
|
||||
const panjiNik = "3201010101010002";
|
||||
await prisma.organizerVerification.createMany({
|
||||
data: [
|
||||
{
|
||||
userId: dede.id,
|
||||
fullName: "Dede Inoen",
|
||||
nik: "3201010101010001",
|
||||
nikEncrypted: encryptString(dedeNik),
|
||||
nikHash: hmacHex(dedeNik),
|
||||
birthDate: new Date(Date.UTC(1990, 0, 1)),
|
||||
address: "Jl. Pendaki No. 1, Garut, Jawa Barat",
|
||||
ktpImageUrl: "https://placehold.co/600x400/png?text=KTP+Dede",
|
||||
selfieUrl: "https://placehold.co/600x400/png?text=Selfie+Dede",
|
||||
ktpImageKey: "ktp/seed-dede.jpg",
|
||||
selfieKey: "selfie/seed-dede.jpg",
|
||||
bankName: "BCA",
|
||||
bankAccountNumber: "1234567890",
|
||||
bankAccountName: "Dede Inoen",
|
||||
@@ -117,11 +121,12 @@ async function main() {
|
||||
{
|
||||
userId: panji.id,
|
||||
fullName: "Panji Petualang",
|
||||
nik: "3201010101010002",
|
||||
nikEncrypted: encryptString(panjiNik),
|
||||
nikHash: hmacHex(panjiNik),
|
||||
birthDate: new Date(Date.UTC(1985, 5, 15)),
|
||||
address: "Jl. Adventure No. 7, Kuningan, Jawa Barat",
|
||||
ktpImageUrl: "https://placehold.co/600x400/png?text=KTP+Panji",
|
||||
selfieUrl: "https://placehold.co/600x400/png?text=Selfie+Panji",
|
||||
ktpImageKey: "ktp/seed-panji.jpg",
|
||||
selfieKey: "selfie/seed-panji.jpg",
|
||||
bankName: "Mandiri",
|
||||
bankAccountNumber: "9876543210",
|
||||
bankAccountName: "Panji Petualang",
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
import { getToken } from "next-auth/jwt";
|
||||
|
||||
// Path yang boleh diakses oleh user yang login tapi belum accept Terms & Privacy.
|
||||
const ALLOWED_WHEN_NOT_ACCEPTED = [
|
||||
"/accept-terms",
|
||||
"/terms",
|
||||
"/privacy",
|
||||
];
|
||||
|
||||
export async function proxy(req: NextRequest) {
|
||||
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
|
||||
if (!token) return NextResponse.next();
|
||||
if (token.acceptedTermsAndPrivacy) return NextResponse.next();
|
||||
|
||||
const { pathname } = req.nextUrl;
|
||||
if (pathname.startsWith("/api/auth")) return NextResponse.next();
|
||||
if (
|
||||
ALLOWED_WHEN_NOT_ACCEPTED.some(
|
||||
(p) => pathname === p || pathname.startsWith(`${p}/`),
|
||||
)
|
||||
) {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
const url = req.nextUrl.clone();
|
||||
url.pathname = "/accept-terms";
|
||||
url.search = "";
|
||||
return NextResponse.redirect(url);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
// Lewati internal Next.js dan asset statis. Sisanya diperiksa proxy.
|
||||
"/((?!_next/static|_next/image|favicon.ico|images/|.*\\.(?:png|jpg|jpeg|svg|webp|ico|css|js|map|txt|xml)$).*)",
|
||||
],
|
||||
};
|
||||
@@ -10,8 +10,8 @@ export const organizerRepo = {
|
||||
return prisma.organizerVerification.findUnique({ where: { id } });
|
||||
},
|
||||
|
||||
async findByNik(nik: string) {
|
||||
return prisma.organizerVerification.findUnique({ where: { nik } });
|
||||
async findByNikHash(nikHash: string) {
|
||||
return prisma.organizerVerification.findUnique({ where: { nikHash } });
|
||||
},
|
||||
|
||||
async upsertForUser(
|
||||
|
||||
@@ -27,4 +27,11 @@ export const userRepo = {
|
||||
async create(data: Prisma.UserCreateInput) {
|
||||
return prisma.user.create({ data });
|
||||
},
|
||||
|
||||
async markAcceptedTerms(id: string) {
|
||||
return prisma.user.update({
|
||||
where: { id },
|
||||
data: { acceptedTermsAndPrivacy: true, acceptedAt: new Date() },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { organizerRepo } from "@/server/repositories/organizer.repo";
|
||||
import { decryptString, encryptString, hmacHex } from "@/lib/crypto";
|
||||
|
||||
type SubmitInput = {
|
||||
fullName: string;
|
||||
nik: string;
|
||||
birthDate: Date;
|
||||
address: string;
|
||||
ktpImageUrl: string;
|
||||
selfieUrl: string;
|
||||
ktpImageKey: string;
|
||||
selfieKey: string;
|
||||
bankName: string;
|
||||
bankAccountNumber: string;
|
||||
bankAccountName: string;
|
||||
@@ -22,18 +23,20 @@ export const organizerService = {
|
||||
throw new Error("Pengajuan kamu masih dalam proses review");
|
||||
}
|
||||
|
||||
const dupNik = await organizerRepo.findByNik(data.nik);
|
||||
const nikHash = hmacHex(data.nik);
|
||||
const dupNik = await organizerRepo.findByNikHash(nikHash);
|
||||
if (dupNik && dupNik.userId !== userId) {
|
||||
throw new Error("NIK ini sudah dipakai akun lain");
|
||||
}
|
||||
|
||||
return organizerRepo.upsertForUser(userId, {
|
||||
fullName: data.fullName,
|
||||
nik: data.nik,
|
||||
nikEncrypted: encryptString(data.nik),
|
||||
nikHash,
|
||||
birthDate: data.birthDate,
|
||||
address: data.address,
|
||||
ktpImageUrl: data.ktpImageUrl,
|
||||
selfieUrl: data.selfieUrl,
|
||||
ktpImageKey: data.ktpImageKey,
|
||||
selfieKey: data.selfieKey,
|
||||
bankName: data.bankName,
|
||||
bankAccountNumber: data.bankAccountNumber,
|
||||
bankAccountName: data.bankAccountName,
|
||||
@@ -74,4 +77,9 @@ export const organizerService = {
|
||||
const v = await organizerRepo.findByUserId(userId);
|
||||
return v?.status === "APPROVED";
|
||||
},
|
||||
|
||||
/** Reveal NIK plaintext. Caller must enforce authorization (owner or admin). */
|
||||
decryptNik(nikEncrypted: string): string {
|
||||
return decryptString(nikEncrypted);
|
||||
},
|
||||
};
|
||||
|
||||
Vendored
+2
@@ -7,6 +7,7 @@ declare module "next-auth" {
|
||||
name: string;
|
||||
email: string;
|
||||
image?: string | null;
|
||||
acceptedTermsAndPrivacy: boolean;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -14,5 +15,6 @@ declare module "next-auth" {
|
||||
declare module "next-auth/jwt" {
|
||||
interface JWT {
|
||||
id: string;
|
||||
acceptedTermsAndPrivacy?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user