UNPKG

studiocms

Version:

Astro Native CMS for AstroDB. Built from the ground up by the Astro community.

434 lines (433 loc) 17.8 kB
import { and, eq } from "astro:db"; import { GhostUserDefaults } from "../../../consts.js"; import { Effect, genLogger } from "../../../effect.js"; import { AstroDB, SDKCore_Generators } from "../effect/index.js"; import { SDKCoreError, StudioCMS_SDK_Error } from "../errors.js"; import { tsEmailVerificationTokens, tsOAuthAccounts, tsPermissions, tsSessionTable, tsUsers } from "../tables.js"; class SDKCore_AUTH extends Effect.Service()( "studiocms/sdk/SDKCore/modules/auth", { dependencies: [AstroDB.Default, SDKCore_Generators.Default], effect: genLogger("studiocms/sdk/SDKCore/modules/auth/effect")(function* () { const [dbService, { generateToken }] = yield* Effect.all([AstroDB, SDKCore_Generators]); const AUTH = { verifyEmail: { /** * Retrieves an email verification token by its ID. * * @param id - The ID of the email verification token to retrieve. * @returns A promise that resolves to the email verification token if found, otherwise undefined. * @throws {StudioCMS_SDK_Error} If an error occurs while retrieving the token. */ get: dbService.makeQuery( (ex, id) => ex( (db) => db.select().from(tsEmailVerificationTokens).where(eq(tsEmailVerificationTokens.id, id)).get() ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.verifyEmail.get Error: ${cause}`) }) ) }) ) ), /** * Creates a new email verification token in the database. * * @param userId - The ID of the user to create the token for. * @returns A promise that resolves to the created email verification token. * @throws {StudioCMS_SDK_Error} If an error occurs while creating the token. */ create: (userId) => Effect.gen(function* () { yield* dbService.execute( (db) => db.delete(tsEmailVerificationTokens).where(eq(tsEmailVerificationTokens.userId, userId)) ); const token = yield* generateToken(userId); return yield* dbService.execute( (db) => db.insert(tsEmailVerificationTokens).values({ id: crypto.randomUUID(), userId, token, expiresAt: new Date(Date.now() + 1e3 * 60 * 10) }).returning().get() ); }).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.verifyEmail.create Error: ${cause}`) }) ) }) ), /** * Deletes an email verification token from the database. * * @param userId - The ID of the user associated with the token. * @returns A promise that resolves to the deletion response. * @throws {StudioCMS_SDK_Error} If an error occurs while deleting the token. */ delete: dbService.makeQuery( (ex, userId) => ex( (db) => db.delete(tsEmailVerificationTokens).where(eq(tsEmailVerificationTokens.userId, userId)) ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.verifyEmail.delete Error: ${cause}`) }) ) }) ) ) }, oAuth: { /** * Creates a new OAuth account in the database. * * @param data - The data to insert into the OAuth account table. * @returns A promise that resolves to the inserted OAuth account. * @throws {StudioCMS_SDK_Error} If an error occurs while creating the OAuth account. */ create: dbService.makeQuery( (ex, data) => ex((db) => db.insert(tsOAuthAccounts).values(data).returning().get()).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.oAuth.create Error: ${cause}`) }) ) }) ) ), /** * Deletes an OAuth account from the database. * * @param userId - The ID of the user associated with the OAuth account. * @param provider - The provider of the OAuth account. * @returns A promise that resolves to a deletion response. * @throws {StudioCMS_SDK_Error} If an error occurs while deleting the OAuth account. */ delete: (userId, provider) => Effect.gen(function* () { yield* dbService.execute( (db) => db.delete(tsOAuthAccounts).where( and(eq(tsOAuthAccounts.userId, userId), eq(tsOAuthAccounts.provider, provider)) ) ); return { status: "success", message: "OAuth account deleted" }; }).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.oAuth.delete Error: ${cause}`) }) ) }) ), /** * Searches for OAuth accounts based on the provider ID and user ID. * * @param providerId - The provider ID to search for. * @param userId - The user ID to search for. * @returns A promise that resolves to the OAuth account data if found, otherwise undefined. * @throws {StudioCMS_SDK_Error} If an error occurs while searching for the OAuth account. */ searchProvidersForId: (providerId, userId) => dbService.execute( (db) => db.select().from(tsOAuthAccounts).where( and( eq(tsOAuthAccounts.providerUserId, providerId), eq(tsOAuthAccounts.userId, userId) ) ).get() ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error( `AUTH.oAuth.searchProviderForId Error: ${cause}` ) }) ) }) ) }, permission: { /** * Checks the current status of a user's permissions. */ currentStatus: dbService.makeQuery( (ex, userId) => ex( (db) => db.select().from(tsPermissions).where(eq(tsPermissions.user, userId)).get() ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error( `AUTH.permission.currentStatus Error: ${cause}` ) }) ) }) ) ) }, session: { /** * Creates a new session in the database. * * @param data - The data to insert into the session table. * @returns A promise that resolves to the inserted session. * @throws {StudioCMS_SDK_Error} If an error occurs while creating the session. */ create: dbService.makeQuery( (ex, data) => ex( (db) => db.insert(tsSessionTable).values(data).returning({ id: tsSessionTable.id, userId: tsSessionTable.userId, expiresAt: tsSessionTable.expiresAt }).get() ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.session.create Error: ${cause}`) }) ) }) ) ), /** * Gets a session with the associated user. * * @param sessionId - The ID of the session to search for. * @returns A promise that resolves to the session with the associated user. * @throws {StudioCMS_SDK_Error} If an error occurs while getting the session with the user. */ sessionWithUser: dbService.makeQuery( (ex, sessionId) => ex( (db) => db.select({ user: tsUsers, session: tsSessionTable }).from(tsSessionTable).innerJoin(tsUsers, eq(tsSessionTable.userId, tsUsers.id)).where(eq(tsSessionTable.id, sessionId)) ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error( `AUTH.session.sessionWithUser Error: ${cause}` ) }) ) }) ) ), /** * Deletes a session from the database. * * @param sessionId - The ID of the session to delete. * @returns A promise that resolves to a deletion response. * @throws {StudioCMS_SDK_Error} If an error occurs while deleting the session. */ delete: dbService.makeQuery( (ex, sessionId) => ex((db) => db.delete(tsSessionTable).where(eq(tsSessionTable.id, sessionId))).pipe( Effect.map(() => ({ status: "success", message: "Session deleted" })), Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.session.delete Error: ${cause}`) }) ) }) ) ), /** * Updates the expiration date of a session. * * @param sessionId - The ID of the session to update. * @param newDate - The new expiration date for the session. * @returns A promise that resolves to the updated session. * @throws {StudioCMS_SDK_Error} If an error occurs while updating the session. */ update: (sessionId, newDate) => dbService.execute( (db) => db.update(tsSessionTable).set({ expiresAt: newDate }).where(eq(tsSessionTable.id, sessionId)).returning() ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.session.update Error: ${cause}`) }) ) }) ) }, user: { /** * Creates a new user in the database. * * @param newUserData - The data to insert into the users table. * @returns A promise that resolves to the inserted user. * @throws {StudioCMS_SDK_Error} If an error occurs while creating the user. */ create: (newUserData, rank) => Effect.gen(function* () { const newUser = yield* dbService.execute( (db) => db.insert(tsUsers).values(newUserData).returning().get() ); yield* dbService.execute( (db) => db.insert(tsPermissions).values({ user: newUser.id, rank: rank || "visitor" }) ); return newUser; }).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.user.create Error: ${cause}`) }) ) }) ), /** * Updates a user in the database. * * @param userId - The ID of the user to update. * @param userData - The data to update the user with. * @returns A promise that resolves to the updated user. * @throws {StudioCMS_SDK_Error} If an error occurs while updating the user. */ update: (userId, userData) => dbService.execute( (db) => db.update(tsUsers).set(userData).where(eq(tsUsers.id, userId)).returning().get() ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.user.update Error: ${cause}`) }) ) }) ), /** * Searches for users based on the provided username or email. * * @param username - The username to search for. * @param email - The email to search for. * @returns A promise that resolves to an object containing the search results for the username and email. * @throws {StudioCMS_SDK_Error} If an error occurs while searching for the username or email. */ searchUsersForUsernameOrEmail: (username, email) => Effect.gen(function* () { let usernameSearch = []; let emailSearch = []; if (username) { usernameSearch = yield* dbService.execute( (db) => db.select().from(tsUsers).where(eq(tsUsers.username, username)) ); } if (email) { emailSearch = yield* dbService.execute( (db) => db.select().from(tsUsers).where(eq(tsUsers.email, email)) ); } return { usernameSearch, emailSearch }; }).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error( `AUTH.user.searchUsersForUsernameOrEmail Error: ${cause}` ) }) ) }) ), ghost: { /** * Verifies if the ghost user exists in the database. * * @returns A promise that resolves to a boolean indicating if the ghost user exists. * @throws {StudioCMS_SDK_Error} If an error occurs while verifying the ghost user. */ verifyExists: () => Effect.gen(function* () { const ghostUser = yield* dbService.execute( (db) => db.select().from(tsUsers).where(eq(tsUsers.id, GhostUserDefaults.id)).get() ); if (!ghostUser) return false; return true; }).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error( `AUTH.user.ghost.verifyExists Error: ${cause}` ) }) ) }) ), /** * Creates the ghost user in the database. * * @returns A promise that resolves to the inserted ghost user. * @throws {StudioCMS_SDK_Error} If an error occurs while creating the ghost user. */ create: () => dbService.execute((db) => db.insert(tsUsers).values(GhostUserDefaults).returning().get()).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.user.ghost.create Error: ${cause}`) }) ) }) ), /** * Gets the ghost user from the database. * * @returns A promise that resolves to the ghost user. * @throws {StudioCMS_SDK_Error} If an error occurs while getting the ghost user. */ get: () => dbService.execute( (db) => db.select().from(tsUsers).where(eq(tsUsers.id, GhostUserDefaults.id)).get() ).pipe( Effect.catchTags({ LibSQLClientError: (cause) => Effect.fail( new SDKCoreError({ type: "LibSQLDatabaseError", cause: new StudioCMS_SDK_Error(`AUTH.user.ghost.get Error: ${cause}`) }) ) }) ) } } }; return AUTH; }) } ) { } export { SDKCore_AUTH };