Skip to content
This repository has been archived by the owner on Sep 17, 2024. It is now read-only.

Commit

Permalink
chore: migrate database query api (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanFlurry authored Aug 24, 2024
1 parent 22132b7 commit d03bd83
Show file tree
Hide file tree
Showing 111 changed files with 3,122 additions and 2,449 deletions.
48 changes: 0 additions & 48 deletions modules/auth/db/migrations/20240310214734_init/migration.sql

This file was deleted.

12 changes: 0 additions & 12 deletions modules/auth/db/migrations/20240312024843_init/migration.sql

This file was deleted.

2 changes: 0 additions & 2 deletions modules/auth/db/migrations/20240312033322_/migration.sql

This file was deleted.

21 changes: 0 additions & 21 deletions modules/auth/db/migrations/20240312035811_/migration.sql

This file was deleted.

3 changes: 0 additions & 3 deletions modules/auth/db/migrations/migration_lock.toml

This file was deleted.

20 changes: 0 additions & 20 deletions modules/auth_email/db/migrations/20240807214202_/migration.sql

This file was deleted.

3 changes: 0 additions & 3 deletions modules/auth_email/db/migrations/migration_lock.toml

This file was deleted.

21 changes: 0 additions & 21 deletions modules/auth_email/db/schema.prisma

This file was deleted.

18 changes: 18 additions & 0 deletions modules/auth_email/db/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { schema, Query } from "./schema.gen.ts";

export const verifications = schema.table('verifications', {
id: Query.uuid("id").primaryKey().defaultRandom(),

email: Query.text("email").notNull(),

code: Query.text("code").notNull().unique(),
token: Query.text("token").notNull().unique(),

attemptCount: Query.integer("attempt_count").notNull().default(0),
maxAttemptCount: Query.integer("max_attempt_count").notNull(),

createdAt: Query.timestamp("created_at").notNull().defaultNow(),
expireAt: Query.timestamp("expire_at").notNull(),
completedAt: Query.timestamp("completed_at"),
});

4 changes: 2 additions & 2 deletions modules/auth_email/tests/already_used.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { RuntimeError, test, TestContext } from "../module.gen.ts";
import { faker } from "https://deno.land/x/[email protected]/mod.ts";
import { getVerification } from "../utils/tests.ts";
import { getVerification } from "./common.ts";
import {
assertEquals,
assertRejects,
} from "https://deno.land/[email protected]/assert/mod.ts";
import { checkLogin } from "../utils/tests.ts";
import { checkLogin } from "./common.ts";

async function signUpEmailPass(
ctx: TestContext,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TestContext } from "../module.gen.ts";
import { TestContext, Query, Database } from "../module.gen.ts";
import {
assertEquals,
assertExists,
Expand All @@ -8,14 +8,12 @@ export async function getVerification(ctx: TestContext, email: string) {
// Get a valid verification
const { verification: { token: verificationToken } } = await ctx.modules.authEmail
.sendVerification({ email });
const { code } = await ctx.db.verifications
.findFirstOrThrow({
where: {
token: verificationToken,
},
const verification = await ctx.db.query.verifications.findFirst({
where: Query.eq(Database.verifications.token, verificationToken),
});
assertExists(verification);

return { verificationToken, code };
return { verificationToken, code: verification.code };
}

export async function verifyProvider(
Expand All @@ -30,7 +28,7 @@ export async function verifyProvider(
assertEquals(emailProvider, provider);

// Verify that the provider data is correct
const { data } = await ctx.modules.identities.get({
const { data } = await ctx.modules.identities.fetch({
userToken,
info: emailProvider,
});
Expand Down
2 changes: 1 addition & 1 deletion modules/auth_email/tests/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
IDENTITY_INFO_PASSWORD,
IDENTITY_INFO_PASSWORDLESS,
} from "../utils/provider.ts";
import { checkLogin, getVerification, verifyProvider } from "../utils/tests.ts";
import { checkLogin, getVerification, verifyProvider } from "./common.ts";

// MARK: Test Email/No Pass
test("connect_email_and_login_passwordless", async (ctx: TestContext) => {
Expand Down
2 changes: 1 addition & 1 deletion modules/auth_email/tests/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
IDENTITY_INFO_PASSWORD,
IDENTITY_INFO_PASSWORDLESS,
} from "../utils/provider.ts";
import { checkLogin, getVerification, verifyProvider } from "../utils/tests.ts";
import { checkLogin, getVerification, verifyProvider } from "./common.ts";

// MARK: Test Email/No Pass
test("create_with_email_and_login_passwordless", async (ctx: TestContext) => {
Expand Down
76 changes: 30 additions & 46 deletions modules/auth_email/utils/code_management.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import { RuntimeError, ScriptContext, Module } from "../module.gen.ts";
import { RuntimeError, ScriptContext, Module, Database, Query } from "../module.gen.ts";

const MAX_ATTEMPT_COUNT = 3;
const EXPIRATION_TIME = 60 * 60 * 1000;



export async function createVerification(ctx: ScriptContext, email: string) {
// Create verification
const code = Module.tokens.generateRandomCodeSecure("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 8);
const verification = await ctx.db.verifications.create({
data: {
const verification = await ctx.db.insert(Database.verifications)
.values({
token: Module.tokens.genSecureId(),
email,
code,
maxAttemptCount: MAX_ATTEMPT_COUNT,
expireAt: new Date(Date.now() + EXPIRATION_TIME),
},
select: { token: true },
});
})
.returning();

return { verification, code };
return { verification: verification[0]!, code };
}

export async function verifyCode(
Expand All @@ -31,62 +28,49 @@ export async function verifyCode(

const code = codeInput.toUpperCase();

return await ctx.db.$transaction(async (tx) => {
const verification = await tx.verifications.update({
where: {
token: verificationToken,
},
data: {
attemptCount: {
increment: 1,
},
},
select: {
email: true,
code: true,
expireAt: true,
completedAt: true,
attemptCount: true,
maxAttemptCount: true,
},
});
if (!verification) {
return await ctx.db.transaction(async (tx) => {
const verification = await tx.update(Database.verifications)
.set({
attemptCount: Query.sql`${Database.verifications.attemptCount} + 1`,
})
.where(Query.eq(Database.verifications.token, verificationToken))
.returning();
if (!verification[0]) {
throw new RuntimeError("verification_code_invalid");
}
if (verification.attemptCount >= verification.maxAttemptCount) {
if (verification[0]!.attemptCount >= verification[0]!.maxAttemptCount) {
throw new RuntimeError("verification_code_attempt_limit");
}
if (verification.completedAt !== null) {
if (verification[0]!.completedAt !== null) {
throw new RuntimeError("verification_code_already_used");
}
if (verification.code !== code) {
if (verification[0]!.code !== code) {
// Same error as above to prevent exploitation
throw new RuntimeError("verification_code_invalid");
}
if (verification.expireAt < new Date()) {
if (verification[0]!.expireAt < new Date()) {
throw new RuntimeError("verification_code_expired");
}

const completedAt = new Date();

// Mark as used
const verificationConfirmation = await tx.verifications
.update({
where: {
token: verificationToken,
completedAt: null,
},
data: {
completedAt,
},
});
if (verificationConfirmation === null) {
const verificationConfirmation = await tx.update(Database.verifications)
.set({
completedAt,
})
.where(Query.and(
Query.eq(Database.verifications.token, verificationToken),
Query.isNull(Database.verifications.completedAt)
))
.returning();
if (verificationConfirmation.length === 0) {
throw new RuntimeError("verification_code_already_used");
}

return {
email: verificationConfirmation.email,
email: verificationConfirmation[0]!.email,
completedAt,
};
});
}
}

This file was deleted.

3 changes: 0 additions & 3 deletions modules/currency/db/migrations/migration_lock.toml

This file was deleted.

9 changes: 0 additions & 9 deletions modules/currency/db/schema.prisma

This file was deleted.

7 changes: 7 additions & 0 deletions modules/currency/db/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { schema, Query } from "./schema.gen.ts";

export const userWallets = schema.table("user_wallets", {
userId: Query.uuid("user_id").primaryKey().defaultRandom(),
balance: Query.integer("balance").notNull(),
});

Loading

0 comments on commit d03bd83

Please sign in to comment.