Skip to content

Commit

Permalink
refactor: create tx context
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Dec 2, 2024
1 parent 004584e commit fe0776a
Show file tree
Hide file tree
Showing 33 changed files with 314 additions and 185 deletions.
21 changes: 0 additions & 21 deletions apps/backend/src/db.ts

This file was deleted.

71 changes: 39 additions & 32 deletions apps/backend/src/modules/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { None, Option, Some } from "@undb/domain"
import { env } from "@undb/env"
import { createLogger } from "@undb/logger"
import { type IMailService, injectMailService } from "@undb/mail"
import { type IQueryBuilder, getCurrentTransaction, injectQueryBuilder } from "@undb/persistence/server"
import { type IQueryBuilder, type ITxContext, injectQueryBuilder, injectTxCTX } from "@undb/persistence/server"
import { type ISpaceService, injectSpaceService } from "@undb/space"
import { Context, Elysia, t } from "elysia"
import type { Session, User } from "lucia"
Expand All @@ -25,7 +25,6 @@ import { alphabet, generateRandomString, sha256 } from "oslo/crypto"
import { encodeHex } from "oslo/encoding"
import { omit } from "radash"
import { v7 } from "uuid"
import { withTransaction } from "../../db"
import { injectLucia } from "./auth.provider"
import { OAuth } from "./oauth/oauth"

Expand All @@ -52,10 +51,12 @@ export class Auth {
private readonly mailService: IMailService,
@injectLucia()
private readonly lucia: Lucia,
@injectTxCTX()
private readonly txContext: ITxContext,
) {}

async #generateEmailVerificationCode(userId: string, email: string): Promise<string> {
const tx = getCurrentTransaction()
const tx = this.txContext.getCurrentTransaction()
await tx.deleteFrom("undb_email_verification_code").where("user_id", "=", userId).execute()
const code = env.UNDB_MOCK_MAIL_CODE || generateRandomString(6, alphabet("0-9"))
await tx
Expand All @@ -71,29 +72,32 @@ export class Auth {
}

async #verifyVerificationCode(user: User, code: string): Promise<boolean> {
return (getCurrentTransaction() ?? this.queryBuilder).transaction().execute(async (tx) => {
const databaseCode = await tx
.selectFrom("undb_email_verification_code")
.selectAll()
.where("user_id", "=", user.id)
.executeTakeFirst()
if (!databaseCode || databaseCode.code !== code) {
return false
}
await tx.deleteFrom("undb_email_verification_code").where("id", "=", databaseCode.id).execute()
return this.txContext
.getCurrentTransaction()
.transaction()
.execute(async (tx) => {
const databaseCode = await tx
.selectFrom("undb_email_verification_code")
.selectAll()
.where("user_id", "=", user.id)
.executeTakeFirst()
if (!databaseCode || databaseCode.code !== code) {
return false
}
await tx.deleteFrom("undb_email_verification_code").where("id", "=", databaseCode.id).execute()

if (!isWithinExpirationDate(new Date(databaseCode.expires_at))) {
return false
}
if (databaseCode.email !== user.email) {
return false
}
return true
})
if (!isWithinExpirationDate(new Date(databaseCode.expires_at))) {
return false
}
if (databaseCode.email !== user.email) {
return false
}
return true
})
}

async #createPasswordResetToken(userId: string): Promise<string> {
const db = getCurrentTransaction() ?? this.queryBuilder
const db = this.txContext.getCurrentTransaction()
await db.deleteFrom("undb_password_reset_token").where("user_id", "=", userId).execute()
const tokenId = generateIdFromEntropySize(25) // 40 character
const tokenHash = encodeHex(await sha256(new TextEncoder().encode(tokenId)))
Expand Down Expand Up @@ -206,8 +210,9 @@ export class Auth {
},
})

await withTransaction(this.queryBuilder)(async () => {
await getCurrentTransaction()
await this.txContext.withTransaction(async () => {
await this.txContext
.getCurrentTransaction()
.insertInto("undb_user")
.values({
email: adminEmail,
Expand Down Expand Up @@ -326,8 +331,9 @@ export class Auth {
},
})

await withTransaction(this.queryBuilder)(async () => {
await getCurrentTransaction()
await this.txContext.withTransaction(async () => {
await this.txContext
.getCurrentTransaction()
.insertInto("undb_user")
.values({
email,
Expand Down Expand Up @@ -470,9 +476,9 @@ export class Auth {
.post(
"/api/reset-password",
async (ctx) => {
return withTransaction(this.queryBuilder)(async () => {
return this.txContext.withTransaction(async () => {
const email = ctx.body.email
const tx = getCurrentTransaction() ?? this.queryBuilder
const tx = this.txContext.getCurrentTransaction()
const user = await tx.selectFrom("undb_user").selectAll().where("email", "=", email).executeTakeFirst()
if (!user) {
return new Response(null, {
Expand Down Expand Up @@ -505,8 +511,8 @@ export class Auth {
.post(
"/api/reset-password/:token",
async (ctx) => {
return withTransaction(this.queryBuilder)(async () => {
const tx = getCurrentTransaction() ?? this.queryBuilder
return this.txContext.withTransaction(async () => {
const tx = this.txContext.getCurrentTransaction()

const password = ctx.body.password
const verificationToken = ctx.params.token
Expand Down Expand Up @@ -589,7 +595,8 @@ export class Auth {
}

await this.lucia.invalidateUserSessions(user.id)
await (getCurrentTransaction() ?? this.queryBuilder)
await this.txContext
.getCurrentTransaction()
.updateTable("undb_user")
.set("email_verified", true)
.where("id", "=", user.id)
Expand All @@ -615,7 +622,7 @@ export class Auth {
.get(
"/invitation/:invitationId/accept",
async (ctx) => {
return withTransaction(this.queryBuilder)(async () => {
return this.txContext.withTransaction(async () => {
const { invitationId } = ctx.params
await this.commandBus.execute(new AcceptInvitationCommand({ id: invitationId }))

Expand Down
11 changes: 6 additions & 5 deletions apps/backend/src/modules/auth/oauth/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import { type ISpaceMemberService, injectSpaceMemberService } from "@undb/authz"
import { setContextValue } from "@undb/context/server"
import { singleton } from "@undb/di"
import { createLogger } from "@undb/logger"
import { type IQueryBuilder, getCurrentTransaction, injectQueryBuilder } from "@undb/persistence/server"
import { type IQueryBuilder, type ITxContext, injectQueryBuilder, injectTxCTX } from "@undb/persistence/server"
import { type ISpaceService, injectSpaceService } from "@undb/space"
import { GitHub } from "arctic"
import { Elysia } from "elysia"
import { type Lucia, generateIdFromEntropySize } from "lucia"
import { serializeCookie } from "oslo/cookie"
import { OAuth2RequestError, generateState } from "oslo/oauth2"
import { withTransaction } from "../../../db"
import { injectLucia } from "../auth.provider"
import { injectGithubProvider } from "./github.provider"

Expand All @@ -26,6 +25,8 @@ export class GithubOAuth {
private readonly github: GitHub,
@injectLucia()
private readonly lucia: Lucia,
@injectTxCTX()
private readonly txContext: ITxContext,
) {}

private logger = createLogger(GithubOAuth.name)
Expand All @@ -34,7 +35,7 @@ export class GithubOAuth {
return new Elysia()
.get("/login/github", async (ctx) => {
const state = generateState()
const url = await this.github.createAuthorizationURL(state, { scopes: ["user:email"] })
const url = this.github.createAuthorizationURL(state, ["user:email"])
return new Response(null, {
status: 302,
headers: {
Expand Down Expand Up @@ -143,8 +144,8 @@ export class GithubOAuth {
})
}
const userId = generateIdFromEntropySize(10) // 16 characters long
const space = await withTransaction(this.queryBuilder)(async () => {
const tx = getCurrentTransaction()
const space = await this.txContext.withTransaction(async () => {
const tx = this.txContext.getCurrentTransaction()
await tx
.insertInto("undb_user")
.values({
Expand Down
13 changes: 6 additions & 7 deletions apps/backend/src/modules/auth/oauth/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import { type ISpaceMemberService, injectSpaceMemberService } from "@undb/authz"
import { setContextValue } from "@undb/context/server"
import { singleton } from "@undb/di"
import { createLogger } from "@undb/logger"
import { type IQueryBuilder, getCurrentTransaction, injectQueryBuilder } from "@undb/persistence/server"
import { type IQueryBuilder, type ITxContext, injectQueryBuilder, injectTxCTX } from "@undb/persistence/server"
import { type ISpaceService, injectSpaceService } from "@undb/space"
import { Google, generateCodeVerifier } from "arctic"
import { env } from "bun"
import { Elysia } from "elysia"
import { type Lucia, generateIdFromEntropySize } from "lucia"
import { OAuth2RequestError, generateState } from "oslo/oauth2"
import { withTransaction } from "../../../db"
import { injectLucia } from "../auth.provider"
import { injectGoogleProvider } from "./google.provider"

Expand All @@ -26,6 +25,8 @@ export class GoogleOAuth {
private readonly google: Google,
@injectLucia()
private readonly lucia: Lucia,
@injectTxCTX()
private readonly txContext: ITxContext,
) {}

private logger = createLogger(GoogleOAuth.name)
Expand All @@ -35,9 +36,7 @@ export class GoogleOAuth {
.get("/login/google", async (ctx) => {
const state = generateState()
const codeVerifier = generateCodeVerifier()
const url = await this.google.createAuthorizationURL(state, codeVerifier, {
scopes: ["email", "profile"],
})
const url = this.google.createAuthorizationURL(state, codeVerifier, ["email", "profile"])

ctx.cookie["state"].set({
value: state,
Expand Down Expand Up @@ -133,8 +132,8 @@ export class GoogleOAuth {
})
}
const userId = generateIdFromEntropySize(10) // 16 characters long
const space = await withTransaction(this.queryBuilder)(async () => {
const tx = getCurrentTransaction()
const space = await this.txContext.withTransaction(async () => {
const tx = this.txContext.getCurrentTransaction()
await tx
.insertInto("undb_user")
.values({
Expand Down
26 changes: 14 additions & 12 deletions apps/backend/src/modules/openapi/record.openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
import { CommandBus, QueryBus } from "@undb/cqrs"
import { inject, singleton } from "@undb/di"
import { Option, type ICommandBus, type IQueryBus, type PaginatedDTO } from "@undb/domain"
import { injectQueryBuilder, type IQueryBuilder } from "@undb/persistence/server"
import type { ITxContext } from "@undb/persistence/server"
import { injectQueryBuilder, injectTxCTX, type IQueryBuilder } from "@undb/persistence/server"
import {
GetAggregatesQuery,
GetPivotDataQuery,
Expand All @@ -22,7 +23,6 @@ import {
} from "@undb/queries"
import { RecordDO, type IRecordReadableValueDTO } from "@undb/table"
import Elysia, { t } from "elysia"
import { withTransaction } from "../../db"

@singleton()
export class RecordOpenApi {
Expand All @@ -34,6 +34,8 @@ export class RecordOpenApi {
private readonly commandBus: ICommandBus,
@injectQueryBuilder()
private readonly qb: IQueryBuilder,
@injectTxCTX()
private readonly txContext: ITxContext,
) {}

public route() {
Expand Down Expand Up @@ -184,7 +186,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(new CreateRecordCommand({ baseName, tableName, values: ctx.body.values })),
)
},
Expand All @@ -203,7 +205,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(new CreateRecordsCommand({ baseName, tableName, records: ctx.body.records })),
)
},
Expand All @@ -222,7 +224,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(
new UpdateRecordCommand({
tableName,
Expand All @@ -248,7 +250,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(
new BulkUpdateRecordsCommand({
tableName,
Expand Down Expand Up @@ -278,7 +280,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(new DuplicateRecordCommand({ baseName, tableName, id: ctx.params.recordId })),
)
},
Expand All @@ -298,7 +300,7 @@ export class RecordOpenApi {
const tableName = decodeURIComponent(ctx.params.tableName)
const recordId = ctx.params.recordId
const field = ctx.params.field
return withTransaction(this.qb)(async () => {
return this.txContext.withTransaction(async () => {
const result = (await this.commandBus.execute(
new TriggerRecordButtonCommand({ baseName, tableName, recordId, field }),
)) as Option<RecordDO>
Expand Down Expand Up @@ -326,7 +328,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(
new BulkDuplicateRecordsCommand({
baseName,
Expand All @@ -352,7 +354,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(new DeleteRecordCommand({ baseName, tableName, id: ctx.params.recordId })),
)
},
Expand All @@ -370,7 +372,7 @@ export class RecordOpenApi {
async (ctx) => {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(
new BulkDeleteRecordsCommand({
baseName,
Expand All @@ -397,7 +399,7 @@ export class RecordOpenApi {
const baseName = decodeURIComponent(ctx.params.baseName)
const tableName = decodeURIComponent(ctx.params.tableName)
const formName = decodeURIComponent(ctx.params.formName)
return withTransaction(this.qb)(() =>
return this.txContext.withTransaction(() =>
this.commandBus.execute(
new SubmitFormCommand({ baseName, tableName, form: formName, values: ctx.body.values }),
),
Expand Down
Loading

0 comments on commit fe0776a

Please sign in to comment.