♻️ refactor: rewrite OAuth provision business logic for new user schema

This commit is contained in:
2026-05-28 21:29:20 +07:00
parent 57d19d4302
commit 96b5101742
10 changed files with 69 additions and 23 deletions

View File

@ -88,6 +88,7 @@ Table user_sessions {
browser_name String
browser_version String
ip_login String
valid_until DateTime [not null]
login_at DateTime [default: `now()`, not null]
logout_at DateTime
user_id String [not null]

View File

@ -0,0 +1,12 @@
-- DropForeignKey
ALTER TABLE "user_preferences" DROP CONSTRAINT "user_preferences_char_as_partner_id_fkey";
-- AlterTable
ALTER TABLE "user_oauth_accounts" ALTER COLUMN "provider_token" SET DATA TYPE TEXT,
ALTER COLUMN "refresh_token" SET DATA TYPE TEXT;
-- AlterTable
ALTER TABLE "user_preferences" ALTER COLUMN "char_as_partner_id" DROP NOT NULL;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_char_as_partner_id_fkey" FOREIGN KEY ("char_as_partner_id") REFERENCES "characters"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@ -0,0 +1,8 @@
/*
Warnings:
- Added the required column `valid_until` to the `user_sessions` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "user_sessions" ADD COLUMN "valid_until" TIMESTAMPTZ NOT NULL;

View File

@ -175,8 +175,8 @@ model UserOauthAccounts {
provider_name auth_provider
provider_sub String? @db.VarChar(255)
provider_email String? @db.VarChar(255)
provider_token String? @db.VarChar(255)
refresh_token String? @db.VarChar(255)
provider_token String? @db.Text
refresh_token String? @db.Text
expires_at DateTime? @db.Timestamptz()
created_at DateTime @default(now()) @db.Timestamptz()
updated_at DateTime @updatedAt @db.Timestamptz()
@ -194,7 +194,8 @@ model UserSession {
browser_name String? @db.VarChar(50)
browser_version String? @db.VarChar(50)
ip_login String? @db.Inet()
login_at DateTime @default(now()) @db.Timestamptz()
valid_until DateTime @db.Timestamptz()
login_at DateTime @default(now()) @db.Timestamptz()
logout_at DateTime? @db.Timestamptz()
user_id String @db.Uuid

View File

@ -0,0 +1,28 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { prisma } from "../../../../utils/databases/prisma/connection";
import { createUserViaOauth } from "../../../user/user.types";
export const createUserWithOAuthCredentialsRepository = async (payload: createUserViaOauth) => {
try {
const { oauthProvider, ...userData } = payload;
return await prisma.user.create({
data: {
...userData,
oauth_accounts: {
create: {
provider_name: oauthProvider.providerName,
provider_sub: oauthProvider.sub,
provider_token: oauthProvider.token,
refresh_token: oauthProvider.refreshToken,
expires_at: oauthProvider.expiresAt,
},
},
preferences: {
create: {},
},
},
});
} catch (error) {
throw new AppError(500, "Error creating user with OAuth credentials", error);
}
};

View File

@ -5,6 +5,7 @@ import { createUserViaOauth } from "../../../user/user.types";
import { createUserService } from "../../../user/services/internal/createUser.service";
import { AppError } from "../../../../helpers/error/instances/app";
import { findAuthIdentityByEmailAndProviderRepository } from "../../repositories/READ/findAuthIdentityByEmailAndProvider.repository";
import { createUserWithOAuthCredentialsRepository } from "../../repositories/WRITE/createUserWithOAuthCredentials.repository";
export const OAuthUserProvisionService = async (payload: createUserViaOauth, userHeaderInfo: UserHeaderInformation) => {
try {
@ -18,7 +19,7 @@ export const OAuthUserProvisionService = async (payload: createUserViaOauth, use
return await createUserSessionService(checkExistingUser.id, userHeaderInfo);
} else if (!checkExistingUser) {
// No user with this email, create new user
const createdUser = await createUserService(payload);
const createdUser = await createUserWithOAuthCredentialsRepository(payload);
return await createUserSessionService(createdUser.id, userHeaderInfo);
}

View File

@ -20,7 +20,7 @@ export interface createUserViaRegisterInput {
}
export interface createUserViaOauth extends createUserViaRegisterInput {
oauthProvider: {
providerName: string;
providerName: "google" | "github";
sub: string;
token: string;
refreshToken?: string;

View File

@ -1,36 +1,28 @@
import { Prisma } from "@prisma/client";
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
import { userSessionModel } from "../userSession.model";
import { generateUUIDv7 } from "../../../helpers/databases/uuidv7";
export const createUserSessionRepository = async (
data: Omit<Prisma.UserSessionUncheckedCreateInput, "id">,
) => {
export const createUserSessionRepository = async (data: Omit<Prisma.UserSessionUncheckedCreateInput, "id">) => {
try {
return await userSessionModel.create({
data: {
id: generateUUIDv7(),
...data,
},
select: {
id: true,
isAuthenticated: true,
validUntil: true,
browser_name: true,
ip_login: true,
valid_until: true,
user: {
select: {
id: true,
name: true,
email: true,
fullname: true,
username: true,
avatar: true,
birthDate: true,
bioProfile: true,
preference: {
omit: {
updatedAt: true,
createdAt: true,
},
},
datebirth: true,
bio: true,
preferences: {},
},
},
},

View File

@ -29,7 +29,7 @@ export const createUserSessionService = async (userId: string, userHeaderInfo: U
await createUserSessionInRedisService({
userId,
sessionId: createUserSession.id,
validUntil: createUserSession.validUntil,
validUntil: createUserSession.valid_until,
});
// create a jwt token with a payload containing the created user session, then return jwt

View File

@ -9,4 +9,7 @@ const pool = new pg.Pool({
});
const adapter = new PrismaPg(pool);
export const prisma = new PrismaClient({ adapter });
export const prisma = new PrismaClient({
adapter,
log: process.env.ENABLE_PRISMA_LOG === "true" ? ["query", "error", "warn", "info"] : [],
});