refactor service layer

This commit is contained in:
Michael Dausmann
2023-04-05 22:42:36 +10:00
parent f2bbe8596a
commit 5350a5c712
11 changed files with 98 additions and 98 deletions

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { MembershipWithAccount } from '~~/lib/services/user.account.service';
import { storeToRefs } from 'pinia';
import { MembershipWithAccount } from '~~/lib/services/service.types';
const supabase = useSupabaseAuthClient();
const user = useSupabaseUser();

View File

@@ -1,41 +1,8 @@
import { ACCOUNT_ACCESS, User, Membership, Account, Plan } from '@prisma/client';
import { ACCOUNT_ACCESS } from '@prisma/client';
import prisma_client from '~~/prisma/prisma.client';
import { UtilService } from './util.service';
const config = useRuntimeConfig();
export type MembershipWithAccount = (Membership & {account: Account});
export type FullDBUser = (User & { memberships: MembershipWithAccount[]; });
export type MembershipWithUser = (Membership & { user: User});
export type AccountWithMembers = (Account & {members: MembershipWithUser[]});
export default class UserAccountService {
async getUserBySupabaseId(supabase_uid: string): Promise<FullDBUser | null> {
return prisma_client.user.findFirst({
where: { supabase_uid },
include: { memberships: {include: {
account: true
}}}
});
}
async getFullUserBySupabaseId(supabase_uid: string): Promise<FullDBUser | null> {
return prisma_client.user.findFirst({
where: { supabase_uid },
include: { memberships: {include: {
account: true
}}}
});
}
async getUserById(user_id: number): Promise<FullDBUser | null> {
return prisma_client.user.findFirstOrThrow({
where: { id: user_id },
include: { memberships: {include: {
account: true
}}}
});
}
import { AccountWithMembers, MembershipWithAccount, MembershipWithUser } from './service.types';
export default class AccountService {
async getAccountById(account_id: number): Promise<AccountWithMembers> {
return prisma_client.account.findFirstOrThrow({
where: { id: account_id },
@@ -99,40 +66,6 @@ export default class UserAccountService {
}
async createUser( supabase_uid: string, display_name: string, email: string ): Promise<FullDBUser | null> {
const trialPlan = await prisma_client.plan.findFirstOrThrow({ where: { name: config.initialPlanName}});
return prisma_client.user.create({
data:{
supabase_uid: supabase_uid,
display_name: display_name,
email: email,
memberships: {
create: {
account: {
create: {
name: display_name,
current_period_ends: UtilService.addMonths(new Date(), config.initialPlanActiveMonths),
plan_id: trialPlan.id,
features: trialPlan.features,
max_notes: trialPlan.max_notes,
max_members: trialPlan.max_members,
plan_name: trialPlan.name,
}
},
access: ACCOUNT_ACCESS.OWNER
}
}
},
include: { memberships: {include: {
account: true
}}}
});
}
async deleteUser(user_id: number) {
return prisma_client.user.delete({ where: { id: user_id } });
}
async joinUserToAccount(user_id: number, account_id: number): Promise<MembershipWithAccount> {
const account = await prisma_client.account.findUnique({
where: {

View File

@@ -0,0 +1,60 @@
import { ACCOUNT_ACCESS } from '@prisma/client';
import prisma_client from '~~/prisma/prisma.client';
import { FullDBUser } from './service.types';
import { UtilService } from './util.service';
const config = useRuntimeConfig();
export default class AuthService {
async getFullUserBySupabaseId(supabase_uid: string): Promise<FullDBUser | null> {
return prisma_client.user.findFirst({
where: { supabase_uid },
include: { memberships: {include: {
account: true
}}}
});
}
async getUserById(user_id: number): Promise<FullDBUser | null> {
return prisma_client.user.findFirstOrThrow({
where: { id: user_id },
include: { memberships: {include: {
account: true
}}}
});
}
async createUser( supabase_uid: string, display_name: string, email: string ): Promise<FullDBUser | null> {
const trialPlan = await prisma_client.plan.findFirstOrThrow({ where: { name: config.initialPlanName}});
return prisma_client.user.create({
data:{
supabase_uid: supabase_uid,
display_name: display_name,
email: email,
memberships: {
create: {
account: {
create: {
name: display_name,
current_period_ends: UtilService.addMonths(new Date(), config.initialPlanActiveMonths),
plan_id: trialPlan.id,
features: trialPlan.features,
max_notes: trialPlan.max_notes,
max_members: trialPlan.max_members,
plan_name: trialPlan.name,
}
},
access: ACCOUNT_ACCESS.OWNER
}
}
},
include: { memberships: {include: {
account: true
}}}
});
}
async deleteUser(user_id: number) {
return prisma_client.user.delete({ where: { id: user_id } });
}
}

View File

@@ -0,0 +1,5 @@
import { User, Membership, Account } from '@prisma/client';
export type MembershipWithAccount = (Membership & {account: Account});
export type FullDBUser = (User & { memberships: MembershipWithAccount[]; });
export type MembershipWithUser = (Membership & { user: User});
export type AccountWithMembers = (Account & {members: MembershipWithUser[]});

View File

@@ -1,6 +1,7 @@
import { ACCOUNT_ACCESS } from '@prisma/client';
import Stripe from 'stripe';
import UserAccountService, { AccountWithMembers } from '~~/lib/services/user.account.service';
import AccountService from '~~/lib/services/account.service';
import { AccountWithMembers } from '~~/lib/services/service.types';
const config = useRuntimeConfig();
const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2022-11-15' });
@@ -11,8 +12,8 @@ export default defineEventHandler(async (event) => {
account_id = +account_id
console.log(`session.post.ts recieved price_id:${price_id}, account_id:${account_id}`);
const userService = new UserAccountService();
const account: AccountWithMembers = await userService.getAccountById(account_id);
const accountService = new AccountService();
const account: AccountWithMembers = await accountService.getAccountById(account_id);
let customer_id: string
if(!account.stripe_customer_id){
// need to pre-emptively create a Stripe user for this account so we know who they are when the webhook comes back
@@ -20,7 +21,7 @@ export default defineEventHandler(async (event) => {
console.log(`Creating account with name ${account.name} and email ${owner?.user.email}`);
const customer = await stripe.customers.create({ name: account.name, email: owner?.user.email });
customer_id = customer.id;
userService.updateAccountStipeCustomerId(account_id, customer.id);
accountService.updateAccountStipeCustomerId(account_id, customer.id);
} else {
customer_id = account.stripe_customer_id;
}

View File

@@ -1,5 +1,5 @@
import Stripe from 'stripe';
import UserAccountService from '~~/lib/services/user.account.service';
import AccountService from '~~/lib/services/account.service';
const config = useRuntimeConfig();
const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2022-11-15' });
@@ -36,13 +36,13 @@ export default defineEventHandler(async (event) => {
throw createError({ statusCode: 400, statusMessage: `Error validating Webhook Event` });
}
const userService = new UserAccountService();
const accountService = new AccountService();
let current_period_ends: Date = new Date(subscription.current_period_end * 1000);
current_period_ends.setDate(current_period_ends.getDate() + config.subscriptionGraceDays);
console.log(`updating stripe sub details subscription.current_period_end:${subscription.current_period_end}, subscription.id:${subscription.id}, stripe_product_id:${stripe_product_id}`);
userService.updateStripeSubscriptionDetailsForAccount(subscription.customer.toString(), subscription.id, current_period_ends, stripe_product_id);
accountService.updateStripeSubscriptionDetailsForAccount(subscription.customer.toString(), subscription.id, current_period_ends, stripe_product_id);
}
}
return `handled ${stripeEvent.type}.`;

View File

@@ -2,7 +2,8 @@ import { inferAsyncReturnType, TRPCError } from '@trpc/server'
import { H3Event } from 'h3';
import { serverSupabaseUser } from '#supabase/server'
import { User } from '@supabase/supabase-js';
import UserAccountService, { FullDBUser } from '~~/lib/services/user.account.service';
import { FullDBUser } from '~~/lib/services/service.types';
import AuthService from '~~/lib/services/auth.service';
export async function createContext(event: H3Event){
let user: User | null = null;
@@ -12,11 +13,11 @@ export async function createContext(event: H3Event){
user = await serverSupabaseUser(event);
}
if (!dbUser && user) {
const userService = new UserAccountService();
dbUser = await userService.getFullUserBySupabaseId(user.id);
const authService = new AuthService();
dbUser = await authService.getFullUserBySupabaseId(user.id);
if (!dbUser && user) {
dbUser = await userService.createUser(user.id, user.user_metadata.full_name?user.user_metadata.full_name:"no name supplied", user.email?user.email:"no@email.supplied" );
dbUser = await authService.createUser(user.id, user.user_metadata.full_name?user.user_metadata.full_name:"no name supplied", user.email?user.email:"no@email.supplied" );
console.log(`\n Created DB User \n ${JSON.stringify(dbUser)}\n`);
}
}

View File

@@ -1,14 +1,15 @@
import UserAccountService from '~~/lib/services/user.account.service';
import { router, adminProcedure } from '../trpc'
import { ACCOUNT_ACCESS } from '@prisma/client';
import { z } from 'zod';
import AccountService from '~~/lib/services/account.service';
import { MembershipWithAccount } from '~~/lib/services/service.types';
export const accountRouter = router({
changeAccountName: adminProcedure
.input(z.object({ account_id: z.number(), new_name: z.string() }))
.query(async ({ ctx, input }) => {
const uaService = new UserAccountService();
const account = await uaService.changeAccountName(input.account_id, input.new_name);
const accountService = new AccountService();
const account = await accountService.changeAccountName(input.account_id, input.new_name);
return {
account,
@@ -17,8 +18,8 @@ export const accountRouter = router({
changeAccountPlan: adminProcedure
.input(z.object({ account_id: z.number(), plan_id: z.number() }))
.query(async ({ ctx, input }) => {
const uaService = new UserAccountService();
const account = await uaService.changeAccountPlan(input.account_id, input.plan_id);
const accountService = new AccountService();
const account = await accountService.changeAccountPlan(input.account_id, input.plan_id);
return {
account,
@@ -27,8 +28,8 @@ export const accountRouter = router({
joinUserToAccount: adminProcedure
.input(z.object({ account_id: z.number(), user_id: z.number() }))
.query(async ({ ctx, input }) => {
const uaService = new UserAccountService();
const membership = (ctx.dbUser?.id)?await uaService.joinUserToAccount(input.user_id, input.account_id):null;
const accountService = new AccountService();
const membership: MembershipWithAccount| null = (ctx.dbUser?.id)?await accountService.joinUserToAccount(input.user_id, input.account_id):null;
return {
membership,
}
@@ -36,8 +37,8 @@ export const accountRouter = router({
changeUserAccessWithinAccount: adminProcedure
.input(z.object({ user_id: z.number(), account_id: z.number(), access: z.enum([ACCOUNT_ACCESS.ADMIN, ACCOUNT_ACCESS.OWNER, ACCOUNT_ACCESS.READ_ONLY, ACCOUNT_ACCESS.READ_WRITE]) }))
.query(async ({ ctx, input }) => {
const uaService = new UserAccountService();
const membership = await uaService.changeUserAccessWithinAccount(input.user_id, input.account_id, input.access);
const accountService = new AccountService();
const membership = await accountService.changeUserAccessWithinAccount(input.user_id, input.account_id, input.access);
return {
membership,
@@ -46,8 +47,8 @@ export const accountRouter = router({
claimOwnershipOfAccount: adminProcedure
.input(z.object({ account_id: z.number() }))
.query(async ({ ctx, input }) => {
const uaService = new UserAccountService();
const membership = await uaService.claimOwnershipOfAccount(ctx.dbUser.id, input.account_id);
const accountService = new AccountService();
const membership = await accountService.claimOwnershipOfAccount(ctx.dbUser.id, input.account_id);
return {
membership,
@@ -56,8 +57,8 @@ export const accountRouter = router({
getAccountMembers: adminProcedure
.input(z.object({ account_id: z.number() }))
.query(async ({ ctx, input }) => {
const uaService = new UserAccountService();
const memberships = await uaService.getAccountMembers(input.account_id);
const accountService = new AccountService();
const memberships = await accountService.getAccountMembers(input.account_id);
return {
memberships,

View File

@@ -39,7 +39,6 @@ const isAdminForInputAccountId = t.middleware(({ next, rawInput, ctx }) => {
if (!result.success) throw new TRPCError({ code: 'BAD_REQUEST' });
const { account_id } = result.data;
const test_membership = ctx.dbUser.memberships.find(membership => membership.account_id == account_id);
console.log(`isAdminForInputAccountId test_membership?.access:${test_membership?.access}`);
if(!test_membership || (test_membership?.access !== ACCOUNT_ACCESS.ADMIN && test_membership?.access !== ACCOUNT_ACCESS.OWNER)) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}

View File

@@ -1,6 +1,6 @@
import { ACCOUNT_ACCESS } from ".prisma/client"
import { defineStore } from "pinia"
import { MembershipWithUser } from "~~/lib/services/user.account.service"
import { MembershipWithUser } from "~~/lib/services/service.types";
import { useAuthStore } from './auth.store'

View File

@@ -1,5 +1,5 @@
import { defineStore } from "pinia"
import { FullDBUser, MembershipWithAccount } from "~~/lib/services/user.account.service"
import { FullDBUser, MembershipWithAccount } from "~~/lib/services/service.types";
interface State {
dbUser?: FullDBUser