From 28539e16c335674f72b66fe0614ab4dc0887e4c6 Mon Sep 17 00:00:00 2001 From: Michael Dausmann Date: Sat, 17 Feb 2024 12:25:55 +1100 Subject: [PATCH] version 1.4.2 patches and changelog --- CHANGELOG.md | 7 + ...1_4_2-service-refactor-to-namespaces.patch | 615 ++++++++++++++++++ ...tor-to-namespaces_authcontextremoved.patch | 588 +++++++++++++++++ 3 files changed, 1210 insertions(+) create mode 100644 patches/1_4_2-service-refactor-to-namespaces.patch create mode 100644 patches/1_4_2-service-refactor-to-namespaces_authcontextremoved.patch diff --git a/CHANGELOG.md b/CHANGELOG.md index 9223a36..e976107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Version 1.4.2 +- Added Favicons and web manifest and referenced in nuxt.config (I used https://favicon.io/favicon-converter/ to generate the icon assets, seems to work well) +- Added patch folder to hold patch files, should make it easier to update repos based on earlier versions +- Refactor service classes into namespaces to avoid pointless service instantiation (1_4_2-service-refactor-to-namespaces_authcontextremoved.patch or 1_4_2-service-refactor-to-namespaces.patch) +- Added an Acount Deletion page - you will need to show you have one of these (along with privacy and terms pages) for several signups e.g. Facebook login +- Added seoMeta - required for Facebook login + ## Version 1.4.1 - Refactor some components and explicitly split out client only components diff --git a/patches/1_4_2-service-refactor-to-namespaces.patch b/patches/1_4_2-service-refactor-to-namespaces.patch new file mode 100644 index 0000000..8611fa5 --- /dev/null +++ b/patches/1_4_2-service-refactor-to-namespaces.patch @@ -0,0 +1,615 @@ +From 32ac5e68868d66c04cc7918d18e2988b66214519 Mon Sep 17 00:00:00 2001 +From: Michael Dausmann +Date: Sat, 17 Feb 2024 10:29:45 +1100 +Subject: [PATCH] service refactor to namespaces + +--- + lib/services/account.service.ts | 50 +++++++++++-------- + lib/services/auth.service.ts | 12 +++-- + lib/services/notes.service.ts | 26 +++++----- + lib/services/util.service.ts | 8 +-- + server/api/note.ts | 5 +- + server/middleware/authContext.ts | 7 ++- + server/routes/create-checkout-session.post.ts | 7 ++- + server/routes/webhook.post.ts | 6 +-- + server/trpc/routers/account.router.ts | 32 ++++-------- + server/trpc/routers/notes.router.ts | 17 +++---- + 10 files changed, 80 insertions(+), 90 deletions(-) + +diff --git a/lib/services/account.service.ts b/lib/services/account.service.ts +index f60c15a..e934c8c 100644 +--- a/lib/services/account.service.ts ++++ b/lib/services/account.service.ts +@@ -14,15 +14,17 @@ import { AccountLimitError } from './errors'; + + const config = useRuntimeConfig(); + +-export default class AccountService { +- async getAccountById(account_id: number): Promise { ++export namespace AccountService { ++ export async function getAccountById( ++ account_id: number ++ ): Promise { + return prisma_client.account.findFirstOrThrow({ + where: { id: account_id }, + ...accountWithMembers + }); + } + +- async getAccountByJoinPassword( ++ export async function getAccountByJoinPassword( + join_password: string + ): Promise { + return prisma_client.account.findFirstOrThrow({ +@@ -31,14 +33,16 @@ export default class AccountService { + }); + } + +- async getAccountMembers(account_id: number): Promise { ++ export async function getAccountMembers( ++ account_id: number ++ ): Promise { + return prisma_client.membership.findMany({ + where: { account_id }, + ...membershipWithUser + }); + } + +- async updateAccountStipeCustomerId( ++ export async function updateAccountStipeCustomerId( + account_id: number, + stripe_customer_id: string + ) { +@@ -50,7 +54,7 @@ export default class AccountService { + }); + } + +- async updateStripeSubscriptionDetailsForAccount( ++ export async function updateStripeSubscriptionDetailsForAccount( + stripe_customer_id: string, + stripe_subscription_id: string, + current_period_ends: Date, +@@ -93,7 +97,7 @@ export default class AccountService { + } + } + +- async acceptPendingMembership( ++ export async function acceptPendingMembership( + account_id: number, + membership_id: number + ): Promise { +@@ -118,7 +122,7 @@ export default class AccountService { + }); + } + +- async deleteMembership( ++ export async function deleteMembership( + account_id: number, + membership_id: number + ): Promise { +@@ -140,7 +144,7 @@ export default class AccountService { + }); + } + +- async joinUserToAccount( ++ export async function joinUserToAccount( + user_id: number, + account_id: number, + pending: boolean +@@ -179,7 +183,10 @@ export default class AccountService { + }); + } + +- async changeAccountName(account_id: number, new_name: string) { ++ export async function changeAccountName( ++ account_id: number, ++ new_name: string ++ ) { + return prisma_client.account.update({ + where: { id: account_id }, + data: { +@@ -188,7 +195,7 @@ export default class AccountService { + }); + } + +- async changeAccountPlan(account_id: number, plan_id: number) { ++ export async function changeAccountPlan(account_id: number, plan_id: number) { + const plan = await prisma_client.plan.findFirstOrThrow({ + where: { id: plan_id } + }); +@@ -202,7 +209,7 @@ export default class AccountService { + }); + } + +- async rotateJoinPassword(account_id: number) { ++ export async function rotateJoinPassword(account_id: number) { + const join_password: string = generator.generate({ + length: 10, + numbers: true +@@ -217,7 +224,7 @@ export default class AccountService { + // User must already be an ADMIN for the Account + // Existing OWNER memberships are downgraded to ADMIN + // In future, some sort of Billing/Stripe tie in here e.g. changing email details on the Account, not sure. +- async claimOwnershipOfAccount( ++ export async function claimOwnershipOfAccount( + user_id: number, + account_id: number + ): Promise { +@@ -278,7 +285,7 @@ export default class AccountService { + } + + // Upgrade access of a membership. Cannot use this method to upgrade to or downgrade from OWNER access +- async changeUserAccessWithinAccount( ++ export async function changeUserAccessWithinAccount( + user_id: number, + account_id: number, + access: ACCOUNT_ACCESS +@@ -333,15 +340,14 @@ export default class AccountService { + Note.. for each usage limit, you will need another pair of check/increment methods and of course the count and max limit in the account schema + + How to use in a service method.... +- async someServiceMethod(account_id: number, .....etc) { +- const accountService = new AccountService(); +- const account = await accountService.checkAIGenCount(account_id); ++ export async function someServiceMethod(account_id: number, .....etc) { ++ const account = await AccountService.checkAIGenCount(account_id); + ... User is under the limit so do work +- await accountService.incrementAIGenCount(account); ++ await AccountService.incrementAIGenCount(account); + } + */ + +- async getAccountWithPeriodRollover(account_id: number) { ++ export async function getAccountWithPeriodRollover(account_id: number) { + const account = await prisma_client.account.findFirstOrThrow({ + where: { id: account_id } + }); +@@ -366,8 +372,8 @@ export default class AccountService { + return account; + } + +- async checkAIGenCount(account_id: number) { +- const account = await this.getAccountWithPeriodRollover(account_id); ++ export async function checkAIGenCount(account_id: number) { ++ const account = await getAccountWithPeriodRollover(account_id); + + if (account.ai_gen_count >= account.ai_gen_max_pm) { + throw new AccountLimitError( +@@ -378,7 +384,7 @@ export default class AccountService { + return account; + } + +- async incrementAIGenCount(account: any) { ++ export async function incrementAIGenCount(account: any) { + return await prisma_client.account.update({ + where: { id: account.id }, + data: { +diff --git a/lib/services/auth.service.ts b/lib/services/auth.service.ts +index 350b6d2..da275ef 100644 +--- a/lib/services/auth.service.ts ++++ b/lib/services/auth.service.ts +@@ -6,8 +6,8 @@ import generator from 'generate-password-ts'; + + const config = useRuntimeConfig(); + +-export default class AuthService { +- async getFullUserBySupabaseId( ++export namespace AuthService { ++ export async function getFullUserBySupabaseId( + supabase_uid: string + ): Promise { + return prisma_client.user.findFirst({ +@@ -16,14 +16,16 @@ export default class AuthService { + }); + } + +- async getUserById(user_id: number): Promise { ++ export async function getUserById( ++ user_id: number ++ ): Promise { + return prisma_client.user.findFirstOrThrow({ + where: { id: user_id }, + ...fullDBUser + }); + } + +- async createUser( ++ export async function createUser( + supabase_uid: string, + display_name: string, + email: string +@@ -65,7 +67,7 @@ export default class AuthService { + }); + } + +- async deleteUser(user_id: number): Promise { ++ export async function deleteUser(user_id: number): Promise { + return prisma_client.user.delete({ + where: { id: user_id }, + ...fullDBUser +diff --git a/lib/services/notes.service.ts b/lib/services/notes.service.ts +index c3b19ab..370e38e 100644 +--- a/lib/services/notes.service.ts ++++ b/lib/services/notes.service.ts +@@ -1,22 +1,22 @@ + import prisma_client from '~~/prisma/prisma.client'; + import { openai } from './openai.client'; + import { AccountLimitError } from './errors'; +-import AccountService from './account.service'; ++import { AccountService } from './account.service'; + +-export default class NotesService { +- async getAllNotes() { ++export namespace NotesService { ++ export async function getAllNotes() { + return prisma_client.note.findMany(); + } + +- async getNoteById(id: number) { ++ export async function getNoteById(id: number) { + return prisma_client.note.findUniqueOrThrow({ where: { id } }); + } + +- async getNotesForAccountId(account_id: number) { ++ export async function getNotesForAccountId(account_id: number) { + return prisma_client.note.findMany({ where: { account_id } }); + } + +- async createNote(account_id: number, note_text: string) { ++ export async function createNote(account_id: number, note_text: string) { + const account = await prisma_client.account.findFirstOrThrow({ + where: { id: account_id }, + include: { notes: true } +@@ -31,17 +31,19 @@ export default class NotesService { + return prisma_client.note.create({ data: { account_id, note_text } }); + } + +- async updateNote(id: number, note_text: string) { ++ export async function updateNote(id: number, note_text: string) { + return prisma_client.note.update({ where: { id }, data: { note_text } }); + } + +- async deleteNote(id: number) { ++ export async function deleteNote(id: number) { + return prisma_client.note.delete({ where: { id } }); + } + +- async generateAINoteFromPrompt(userPrompt: string, account_id: number) { +- const accountService = new AccountService(); +- const account = await accountService.checkAIGenCount(account_id); ++ export async function generateAINoteFromPrompt( ++ userPrompt: string, ++ account_id: number ++ ) { ++ const account = await AccountService.checkAIGenCount(account_id); + + const prompt = ` + Write an interesting short note about ${userPrompt}. +@@ -56,7 +58,7 @@ export default class NotesService { + n: 1 + }); + +- await accountService.incrementAIGenCount(account); ++ await AccountService.incrementAIGenCount(account); + + return completion.data.choices[0].text; + } +diff --git a/lib/services/util.service.ts b/lib/services/util.service.ts +index b9bd697..2e3760e 100644 +--- a/lib/services/util.service.ts ++++ b/lib/services/util.service.ts +@@ -1,5 +1,5 @@ +-export class UtilService { +- public static addMonths(date: Date, months: number): Date { ++export namespace UtilService { ++ export function addMonths(date: Date, months: number): Date { + const d = date.getDate(); + date.setMonth(date.getMonth() + +months); + if (date.getDate() != d) { +@@ -8,12 +8,12 @@ export class UtilService { + return date; + } + +- public static getErrorMessage(error: unknown) { ++ export function getErrorMessage(error: unknown) { + if (error instanceof Error) return error.message; + return String(error); + } + +- public static stringifySafely(obj: any) { ++ export function stringifySafely(obj: any) { + let cache: any[] = []; + let str = JSON.stringify(obj, function (key, value) { + if (typeof value === 'object' && value !== null) { +diff --git a/server/api/note.ts b/server/api/note.ts +index 19e0edb..c570dae 100644 +--- a/server/api/note.ts ++++ b/server/api/note.ts +@@ -1,6 +1,6 @@ + import { H3Event, getQuery } from 'h3'; + import { defineProtectedEventHandler } from '../defineProtectedEventHandler'; +-import NotesService from '~/lib/services/notes.service'; ++import { NotesService } from '~/lib/services/notes.service'; + + // Example API Route with query params ... /api/note?note_id=41 + export default defineProtectedEventHandler(async (event: H3Event) => { +@@ -14,8 +14,7 @@ export default defineProtectedEventHandler(async (event: H3Event) => { + } + } + +- const notesService = new NotesService(); +- const note = await notesService.getNoteById(+note_id); ++ const note = await NotesService.getNoteById(+note_id); + + return { + note +diff --git a/server/middleware/authContext.ts b/server/middleware/authContext.ts +index a9382fc..a583c3e 100644 +--- a/server/middleware/authContext.ts ++++ b/server/middleware/authContext.ts +@@ -1,6 +1,6 @@ + import { defineEventHandler, parseCookies, setCookie, getCookie } from 'h3'; + import { serverSupabaseUser } from '#supabase/server'; +-import AuthService from '~/lib/services/auth.service'; ++import { AuthService } from '~/lib/services/auth.service'; + + import { User } from '@supabase/supabase-js'; + import { FullDBUser } from '~~/lib/services/service.types'; +@@ -27,11 +27,10 @@ export default defineEventHandler(async event => { + if (user) { + event.context.user = user; + +- const authService = new AuthService(); +- let dbUser = await authService.getFullUserBySupabaseId(user.id); ++ let dbUser = await AuthService.getFullUserBySupabaseId(user.id); + + if (!dbUser && user) { +- dbUser = await authService.createUser( ++ dbUser = await AuthService.createUser( + user.id, + user.user_metadata.full_name + ? user.user_metadata.full_name +diff --git a/server/routes/create-checkout-session.post.ts b/server/routes/create-checkout-session.post.ts +index aafe408..63de03a 100644 +--- a/server/routes/create-checkout-session.post.ts ++++ b/server/routes/create-checkout-session.post.ts +@@ -1,6 +1,6 @@ + import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum'; + import Stripe from 'stripe'; +-import AccountService from '~~/lib/services/account.service'; ++import { AccountService } from '~~/lib/services/account.service'; + import { AccountWithMembers } from '~~/lib/services/service.types'; + + const config = useRuntimeConfig(); +@@ -14,8 +14,7 @@ export default defineEventHandler(async event => { + `session.post.ts recieved price_id:${price_id}, account_id:${account_id}` + ); + +- const accountService = new AccountService(); +- const account: AccountWithMembers = await accountService.getAccountById( ++ const account: AccountWithMembers = await AccountService.getAccountById( + account_id + ); + let customer_id: string; +@@ -32,7 +31,7 @@ export default defineEventHandler(async event => { + email: owner?.user.email + }); + customer_id = customer.id; +- accountService.updateAccountStipeCustomerId(account_id, customer.id); ++ AccountService.updateAccountStipeCustomerId(account_id, customer.id); + } else { + customer_id = account.stripe_customer_id; + } +diff --git a/server/routes/webhook.post.ts b/server/routes/webhook.post.ts +index ca226b8..4f14c44 100644 +--- a/server/routes/webhook.post.ts ++++ b/server/routes/webhook.post.ts +@@ -1,5 +1,5 @@ + import Stripe from 'stripe'; +-import AccountService from '~~/lib/services/account.service'; ++import { AccountService } from '~~/lib/services/account.service'; + + const config = useRuntimeConfig(); + const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2022-11-15' }); +@@ -56,8 +56,6 @@ export default defineEventHandler(async event => { + }); + } + +- const accountService = new AccountService(); +- + let current_period_ends: Date = new Date( + subscription.current_period_end * 1000 + ); +@@ -68,7 +66,7 @@ export default defineEventHandler(async event => { + console.log( + `updating stripe sub details subscription.current_period_end:${subscription.current_period_end}, subscription.id:${subscription.id}, stripe_product_id:${stripe_product_id}` + ); +- accountService.updateStripeSubscriptionDetailsForAccount( ++ AccountService.updateStripeSubscriptionDetailsForAccount( + subscription.customer.toString(), + subscription.id, + current_period_ends, +diff --git a/server/trpc/routers/account.router.ts b/server/trpc/routers/account.router.ts +index eb03b6b..e8c52db 100644 +--- a/server/trpc/routers/account.router.ts ++++ b/server/trpc/routers/account.router.ts +@@ -8,7 +8,7 @@ import { + } from '../trpc'; + import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum'; + import { z } from 'zod'; +-import AccountService from '~~/lib/services/account.service'; ++import { AccountService } from '~~/lib/services/account.service'; + import { MembershipWithAccount } from '~~/lib/services/service.types'; + + /* +@@ -48,8 +48,7 @@ export const accountRouter = router({ + changeAccountName: adminProcedure + .input(z.object({ new_name: z.string() })) + .mutation(async ({ ctx, input }) => { +- const accountService = new AccountService(); +- const account = await accountService.changeAccountName( ++ const account = await AccountService.changeAccountName( + ctx.activeAccountId!, + input.new_name + ); +@@ -58,8 +57,7 @@ export const accountRouter = router({ + }; + }), + rotateJoinPassword: adminProcedure.mutation(async ({ ctx }) => { +- const accountService = new AccountService(); +- const account = await accountService.rotateJoinPassword( ++ const account = await AccountService.rotateJoinPassword( + ctx.activeAccountId! + ); + return { +@@ -69,8 +67,7 @@ export const accountRouter = router({ + getAccountByJoinPassword: publicProcedure + .input(z.object({ join_password: z.string() })) + .query(async ({ input }) => { +- const accountService = new AccountService(); +- const account = await accountService.getAccountByJoinPassword( ++ const account = await AccountService.getAccountByJoinPassword( + input.join_password + ); + return { +@@ -80,9 +77,8 @@ export const accountRouter = router({ + joinUserToAccountPending: publicProcedure // this uses a passed account id rather than using the active account because user is usually active on their personal or some other account when they attempt to join a new account + .input(z.object({ account_id: z.number(), user_id: z.number() })) + .mutation(async ({ input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.joinUserToAccount( ++ await AccountService.joinUserToAccount( + input.user_id, + input.account_id, + true +@@ -94,9 +90,8 @@ export const accountRouter = router({ + acceptPendingMembership: adminProcedure + .input(z.object({ membership_id: z.number() })) + .query(async ({ ctx, input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.acceptPendingMembership( ++ await AccountService.acceptPendingMembership( + ctx.activeAccountId!, + input.membership_id + ); +@@ -107,9 +102,8 @@ export const accountRouter = router({ + rejectPendingMembership: adminProcedure + .input(z.object({ membership_id: z.number() })) + .query(async ({ ctx, input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.deleteMembership( ++ await AccountService.deleteMembership( + ctx.activeAccountId!, + input.membership_id + ); +@@ -120,9 +114,8 @@ export const accountRouter = router({ + deleteMembership: ownerProcedure + .input(z.object({ membership_id: z.number() })) + .query(async ({ ctx, input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.deleteMembership( ++ await AccountService.deleteMembership( + ctx.activeAccountId!, + input.membership_id + ); +@@ -143,8 +136,7 @@ export const accountRouter = router({ + }) + ) + .mutation(async ({ ctx, input }) => { +- const accountService = new AccountService(); +- const membership = await accountService.changeUserAccessWithinAccount( ++ const membership = await AccountService.changeUserAccessWithinAccount( + input.user_id, + ctx.activeAccountId!, + input.access +@@ -154,8 +146,7 @@ export const accountRouter = router({ + }; + }), + claimOwnershipOfAccount: adminProcedure.mutation(async ({ ctx }) => { +- const accountService = new AccountService(); +- const memberships = await accountService.claimOwnershipOfAccount( ++ const memberships = await AccountService.claimOwnershipOfAccount( + ctx.dbUser!.id, + ctx.activeAccountId! + ); +@@ -164,8 +155,7 @@ export const accountRouter = router({ + }; + }), + getAccountMembers: adminProcedure.query(async ({ ctx }) => { +- const accountService = new AccountService(); +- const memberships = await accountService.getAccountMembers( ++ const memberships = await AccountService.getAccountMembers( + ctx.activeAccountId! + ); + return { +diff --git a/server/trpc/routers/notes.router.ts b/server/trpc/routers/notes.router.ts +index 44f7d28..34fa4f4 100644 +--- a/server/trpc/routers/notes.router.ts ++++ b/server/trpc/routers/notes.router.ts +@@ -1,4 +1,4 @@ +-import NotesService from '~~/lib/services/notes.service'; ++import { NotesService } from '~~/lib/services/notes.service'; + import { + accountHasSpecialFeature, + adminProcedure, +@@ -11,9 +11,8 @@ import { z } from 'zod'; + + export const notesRouter = router({ + getForActiveAccount: memberProcedure.query(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const notes = ctx.activeAccountId +- ? await notesService.getNotesForAccountId(ctx.activeAccountId) ++ ? await NotesService.getNotesForAccountId(ctx.activeAccountId) + : []; + return { + notes +@@ -22,8 +21,7 @@ export const notesRouter = router({ + getById: publicProcedure + .input(z.object({ note_id: z.number() })) + .query(async ({ ctx, input }) => { +- const notesService = new NotesService(); +- const note = await notesService.getNoteById(input.note_id); ++ const note = await NotesService.getNoteById(input.note_id); + return { + note + }; +@@ -31,9 +29,8 @@ export const notesRouter = router({ + createNote: readWriteProcedure + .input(z.object({ note_text: z.string() })) + .mutation(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const note = ctx.activeAccountId +- ? await notesService.createNote(ctx.activeAccountId, input.note_text) ++ ? await NotesService.createNote(ctx.activeAccountId, input.note_text) + : null; + return { + note +@@ -42,9 +39,8 @@ export const notesRouter = router({ + deleteNote: adminProcedure + .input(z.object({ note_id: z.number() })) + .mutation(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const note = ctx.activeAccountId +- ? await notesService.deleteNote(input.note_id) ++ ? await NotesService.deleteNote(input.note_id) + : null; + return { + note +@@ -54,9 +50,8 @@ export const notesRouter = router({ + .use(accountHasSpecialFeature) + .input(z.object({ user_prompt: z.string() })) + .query(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const noteText = ctx.activeAccountId +- ? await notesService.generateAINoteFromPrompt( ++ ? await NotesService.generateAINoteFromPrompt( + input.user_prompt, + ctx.activeAccountId + ) +-- +2.32.0 + diff --git a/patches/1_4_2-service-refactor-to-namespaces_authcontextremoved.patch b/patches/1_4_2-service-refactor-to-namespaces_authcontextremoved.patch new file mode 100644 index 0000000..4c919f0 --- /dev/null +++ b/patches/1_4_2-service-refactor-to-namespaces_authcontextremoved.patch @@ -0,0 +1,588 @@ +From 32ac5e68868d66c04cc7918d18e2988b66214519 Mon Sep 17 00:00:00 2001 +From: Michael Dausmann +Date: Sat, 17 Feb 2024 10:29:45 +1100 +Subject: [PATCH] service refactor to namespaces + +--- + lib/services/account.service.ts | 50 +++++++++++-------- + lib/services/auth.service.ts | 12 +++-- + lib/services/notes.service.ts | 26 +++++----- + lib/services/util.service.ts | 8 +-- + server/api/note.ts | 5 +- + server/routes/create-checkout-session.post.ts | 7 ++- + server/routes/webhook.post.ts | 6 +-- + server/trpc/routers/account.router.ts | 32 ++++-------- + server/trpc/routers/notes.router.ts | 17 +++---- + 10 files changed, 80 insertions(+), 90 deletions(-) + +diff --git a/lib/services/account.service.ts b/lib/services/account.service.ts +index f60c15a..e934c8c 100644 +--- a/lib/services/account.service.ts ++++ b/lib/services/account.service.ts +@@ -14,15 +14,17 @@ import { AccountLimitError } from './errors'; + + const config = useRuntimeConfig(); + +-export default class AccountService { +- async getAccountById(account_id: number): Promise { ++export namespace AccountService { ++ export async function getAccountById( ++ account_id: number ++ ): Promise { + return prisma_client.account.findFirstOrThrow({ + where: { id: account_id }, + ...accountWithMembers + }); + } + +- async getAccountByJoinPassword( ++ export async function getAccountByJoinPassword( + join_password: string + ): Promise { + return prisma_client.account.findFirstOrThrow({ +@@ -31,14 +33,16 @@ export default class AccountService { + }); + } + +- async getAccountMembers(account_id: number): Promise { ++ export async function getAccountMembers( ++ account_id: number ++ ): Promise { + return prisma_client.membership.findMany({ + where: { account_id }, + ...membershipWithUser + }); + } + +- async updateAccountStipeCustomerId( ++ export async function updateAccountStipeCustomerId( + account_id: number, + stripe_customer_id: string + ) { +@@ -50,7 +54,7 @@ export default class AccountService { + }); + } + +- async updateStripeSubscriptionDetailsForAccount( ++ export async function updateStripeSubscriptionDetailsForAccount( + stripe_customer_id: string, + stripe_subscription_id: string, + current_period_ends: Date, +@@ -93,7 +97,7 @@ export default class AccountService { + } + } + +- async acceptPendingMembership( ++ export async function acceptPendingMembership( + account_id: number, + membership_id: number + ): Promise { +@@ -118,7 +122,7 @@ export default class AccountService { + }); + } + +- async deleteMembership( ++ export async function deleteMembership( + account_id: number, + membership_id: number + ): Promise { +@@ -140,7 +144,7 @@ export default class AccountService { + }); + } + +- async joinUserToAccount( ++ export async function joinUserToAccount( + user_id: number, + account_id: number, + pending: boolean +@@ -179,7 +183,10 @@ export default class AccountService { + }); + } + +- async changeAccountName(account_id: number, new_name: string) { ++ export async function changeAccountName( ++ account_id: number, ++ new_name: string ++ ) { + return prisma_client.account.update({ + where: { id: account_id }, + data: { +@@ -188,7 +195,7 @@ export default class AccountService { + }); + } + +- async changeAccountPlan(account_id: number, plan_id: number) { ++ export async function changeAccountPlan(account_id: number, plan_id: number) { + const plan = await prisma_client.plan.findFirstOrThrow({ + where: { id: plan_id } + }); +@@ -202,7 +209,7 @@ export default class AccountService { + }); + } + +- async rotateJoinPassword(account_id: number) { ++ export async function rotateJoinPassword(account_id: number) { + const join_password: string = generator.generate({ + length: 10, + numbers: true +@@ -217,7 +224,7 @@ export default class AccountService { + // User must already be an ADMIN for the Account + // Existing OWNER memberships are downgraded to ADMIN + // In future, some sort of Billing/Stripe tie in here e.g. changing email details on the Account, not sure. +- async claimOwnershipOfAccount( ++ export async function claimOwnershipOfAccount( + user_id: number, + account_id: number + ): Promise { +@@ -278,7 +285,7 @@ export default class AccountService { + } + + // Upgrade access of a membership. Cannot use this method to upgrade to or downgrade from OWNER access +- async changeUserAccessWithinAccount( ++ export async function changeUserAccessWithinAccount( + user_id: number, + account_id: number, + access: ACCOUNT_ACCESS +@@ -333,15 +340,14 @@ export default class AccountService { + Note.. for each usage limit, you will need another pair of check/increment methods and of course the count and max limit in the account schema + + How to use in a service method.... +- async someServiceMethod(account_id: number, .....etc) { +- const accountService = new AccountService(); +- const account = await accountService.checkAIGenCount(account_id); ++ export async function someServiceMethod(account_id: number, .....etc) { ++ const account = await AccountService.checkAIGenCount(account_id); + ... User is under the limit so do work +- await accountService.incrementAIGenCount(account); ++ await AccountService.incrementAIGenCount(account); + } + */ + +- async getAccountWithPeriodRollover(account_id: number) { ++ export async function getAccountWithPeriodRollover(account_id: number) { + const account = await prisma_client.account.findFirstOrThrow({ + where: { id: account_id } + }); +@@ -366,8 +372,8 @@ export default class AccountService { + return account; + } + +- async checkAIGenCount(account_id: number) { +- const account = await this.getAccountWithPeriodRollover(account_id); ++ export async function checkAIGenCount(account_id: number) { ++ const account = await getAccountWithPeriodRollover(account_id); + + if (account.ai_gen_count >= account.ai_gen_max_pm) { + throw new AccountLimitError( +@@ -378,7 +384,7 @@ export default class AccountService { + return account; + } + +- async incrementAIGenCount(account: any) { ++ export async function incrementAIGenCount(account: any) { + return await prisma_client.account.update({ + where: { id: account.id }, + data: { +diff --git a/lib/services/auth.service.ts b/lib/services/auth.service.ts +index 350b6d2..da275ef 100644 +--- a/lib/services/auth.service.ts ++++ b/lib/services/auth.service.ts +@@ -6,8 +6,8 @@ import generator from 'generate-password-ts'; + + const config = useRuntimeConfig(); + +-export default class AuthService { +- async getFullUserBySupabaseId( ++export namespace AuthService { ++ export async function getFullUserBySupabaseId( + supabase_uid: string + ): Promise { + return prisma_client.user.findFirst({ +@@ -16,14 +16,16 @@ export default class AuthService { + }); + } + +- async getUserById(user_id: number): Promise { ++ export async function getUserById( ++ user_id: number ++ ): Promise { + return prisma_client.user.findFirstOrThrow({ + where: { id: user_id }, + ...fullDBUser + }); + } + +- async createUser( ++ export async function createUser( + supabase_uid: string, + display_name: string, + email: string +@@ -65,7 +67,7 @@ export default class AuthService { + }); + } + +- async deleteUser(user_id: number): Promise { ++ export async function deleteUser(user_id: number): Promise { + return prisma_client.user.delete({ + where: { id: user_id }, + ...fullDBUser +diff --git a/lib/services/notes.service.ts b/lib/services/notes.service.ts +index c3b19ab..370e38e 100644 +--- a/lib/services/notes.service.ts ++++ b/lib/services/notes.service.ts +@@ -1,22 +1,22 @@ + import prisma_client from '~~/prisma/prisma.client'; + import { openai } from './openai.client'; + import { AccountLimitError } from './errors'; +-import AccountService from './account.service'; ++import { AccountService } from './account.service'; + +-export default class NotesService { +- async getAllNotes() { ++export namespace NotesService { ++ export async function getAllNotes() { + return prisma_client.note.findMany(); + } + +- async getNoteById(id: number) { ++ export async function getNoteById(id: number) { + return prisma_client.note.findUniqueOrThrow({ where: { id } }); + } + +- async getNotesForAccountId(account_id: number) { ++ export async function getNotesForAccountId(account_id: number) { + return prisma_client.note.findMany({ where: { account_id } }); + } + +- async createNote(account_id: number, note_text: string) { ++ export async function createNote(account_id: number, note_text: string) { + const account = await prisma_client.account.findFirstOrThrow({ + where: { id: account_id }, + include: { notes: true } +@@ -31,17 +31,19 @@ export default class NotesService { + return prisma_client.note.create({ data: { account_id, note_text } }); + } + +- async updateNote(id: number, note_text: string) { ++ export async function updateNote(id: number, note_text: string) { + return prisma_client.note.update({ where: { id }, data: { note_text } }); + } + +- async deleteNote(id: number) { ++ export async function deleteNote(id: number) { + return prisma_client.note.delete({ where: { id } }); + } + +- async generateAINoteFromPrompt(userPrompt: string, account_id: number) { +- const accountService = new AccountService(); +- const account = await accountService.checkAIGenCount(account_id); ++ export async function generateAINoteFromPrompt( ++ userPrompt: string, ++ account_id: number ++ ) { ++ const account = await AccountService.checkAIGenCount(account_id); + + const prompt = ` + Write an interesting short note about ${userPrompt}. +@@ -56,7 +58,7 @@ export default class NotesService { + n: 1 + }); + +- await accountService.incrementAIGenCount(account); ++ await AccountService.incrementAIGenCount(account); + + return completion.data.choices[0].text; + } +diff --git a/lib/services/util.service.ts b/lib/services/util.service.ts +index b9bd697..2e3760e 100644 +--- a/lib/services/util.service.ts ++++ b/lib/services/util.service.ts +@@ -1,5 +1,5 @@ +-export class UtilService { +- public static addMonths(date: Date, months: number): Date { ++export namespace UtilService { ++ export function addMonths(date: Date, months: number): Date { + const d = date.getDate(); + date.setMonth(date.getMonth() + +months); + if (date.getDate() != d) { +@@ -8,12 +8,12 @@ export class UtilService { + return date; + } + +- public static getErrorMessage(error: unknown) { ++ export function getErrorMessage(error: unknown) { + if (error instanceof Error) return error.message; + return String(error); + } + +- public static stringifySafely(obj: any) { ++ export function stringifySafely(obj: any) { + let cache: any[] = []; + let str = JSON.stringify(obj, function (key, value) { + if (typeof value === 'object' && value !== null) { +diff --git a/server/api/note.ts b/server/api/note.ts +index 19e0edb..c570dae 100644 +--- a/server/api/note.ts ++++ b/server/api/note.ts +@@ -1,6 +1,6 @@ + import { H3Event, getQuery } from 'h3'; + import { defineProtectedEventHandler } from '../defineProtectedEventHandler'; +-import NotesService from '~/lib/services/notes.service'; ++import { NotesService } from '~/lib/services/notes.service'; + + // Example API Route with query params ... /api/note?note_id=41 + export default defineProtectedEventHandler(async (event: H3Event) => { +@@ -14,8 +14,7 @@ export default defineProtectedEventHandler(async (event: H3Event) => { + } + } + +- const notesService = new NotesService(); +- const note = await notesService.getNoteById(+note_id); ++ const note = await NotesService.getNoteById(+note_id); + + return { + note +diff --git a/server/routes/create-checkout-session.post.ts b/server/routes/create-checkout-session.post.ts +index aafe408..63de03a 100644 +--- a/server/routes/create-checkout-session.post.ts ++++ b/server/routes/create-checkout-session.post.ts +@@ -1,6 +1,6 @@ + import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum'; + import Stripe from 'stripe'; +-import AccountService from '~~/lib/services/account.service'; ++import { AccountService } from '~~/lib/services/account.service'; + import { AccountWithMembers } from '~~/lib/services/service.types'; + + const config = useRuntimeConfig(); +@@ -14,8 +14,7 @@ export default defineEventHandler(async event => { + `session.post.ts recieved price_id:${price_id}, account_id:${account_id}` + ); + +- const accountService = new AccountService(); +- const account: AccountWithMembers = await accountService.getAccountById( ++ const account: AccountWithMembers = await AccountService.getAccountById( + account_id + ); + let customer_id: string; +@@ -32,7 +31,7 @@ export default defineEventHandler(async event => { + email: owner?.user.email + }); + customer_id = customer.id; +- accountService.updateAccountStipeCustomerId(account_id, customer.id); ++ AccountService.updateAccountStipeCustomerId(account_id, customer.id); + } else { + customer_id = account.stripe_customer_id; + } +diff --git a/server/routes/webhook.post.ts b/server/routes/webhook.post.ts +index ca226b8..4f14c44 100644 +--- a/server/routes/webhook.post.ts ++++ b/server/routes/webhook.post.ts +@@ -1,5 +1,5 @@ + import Stripe from 'stripe'; +-import AccountService from '~~/lib/services/account.service'; ++import { AccountService } from '~~/lib/services/account.service'; + + const config = useRuntimeConfig(); + const stripe = new Stripe(config.stripeSecretKey, { apiVersion: '2022-11-15' }); +@@ -56,8 +56,6 @@ export default defineEventHandler(async event => { + }); + } + +- const accountService = new AccountService(); +- + let current_period_ends: Date = new Date( + subscription.current_period_end * 1000 + ); +@@ -68,7 +66,7 @@ export default defineEventHandler(async event => { + console.log( + `updating stripe sub details subscription.current_period_end:${subscription.current_period_end}, subscription.id:${subscription.id}, stripe_product_id:${stripe_product_id}` + ); +- accountService.updateStripeSubscriptionDetailsForAccount( ++ AccountService.updateStripeSubscriptionDetailsForAccount( + subscription.customer.toString(), + subscription.id, + current_period_ends, +diff --git a/server/trpc/routers/account.router.ts b/server/trpc/routers/account.router.ts +index eb03b6b..e8c52db 100644 +--- a/server/trpc/routers/account.router.ts ++++ b/server/trpc/routers/account.router.ts +@@ -8,7 +8,7 @@ import { + } from '../trpc'; + import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum'; + import { z } from 'zod'; +-import AccountService from '~~/lib/services/account.service'; ++import { AccountService } from '~~/lib/services/account.service'; + import { MembershipWithAccount } from '~~/lib/services/service.types'; + + /* +@@ -48,8 +48,7 @@ export const accountRouter = router({ + changeAccountName: adminProcedure + .input(z.object({ new_name: z.string() })) + .mutation(async ({ ctx, input }) => { +- const accountService = new AccountService(); +- const account = await accountService.changeAccountName( ++ const account = await AccountService.changeAccountName( + ctx.activeAccountId!, + input.new_name + ); +@@ -58,8 +57,7 @@ export const accountRouter = router({ + }; + }), + rotateJoinPassword: adminProcedure.mutation(async ({ ctx }) => { +- const accountService = new AccountService(); +- const account = await accountService.rotateJoinPassword( ++ const account = await AccountService.rotateJoinPassword( + ctx.activeAccountId! + ); + return { +@@ -69,8 +67,7 @@ export const accountRouter = router({ + getAccountByJoinPassword: publicProcedure + .input(z.object({ join_password: z.string() })) + .query(async ({ input }) => { +- const accountService = new AccountService(); +- const account = await accountService.getAccountByJoinPassword( ++ const account = await AccountService.getAccountByJoinPassword( + input.join_password + ); + return { +@@ -80,9 +77,8 @@ export const accountRouter = router({ + joinUserToAccountPending: publicProcedure // this uses a passed account id rather than using the active account because user is usually active on their personal or some other account when they attempt to join a new account + .input(z.object({ account_id: z.number(), user_id: z.number() })) + .mutation(async ({ input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.joinUserToAccount( ++ await AccountService.joinUserToAccount( + input.user_id, + input.account_id, + true +@@ -94,9 +90,8 @@ export const accountRouter = router({ + acceptPendingMembership: adminProcedure + .input(z.object({ membership_id: z.number() })) + .query(async ({ ctx, input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.acceptPendingMembership( ++ await AccountService.acceptPendingMembership( + ctx.activeAccountId!, + input.membership_id + ); +@@ -107,9 +102,8 @@ export const accountRouter = router({ + rejectPendingMembership: adminProcedure + .input(z.object({ membership_id: z.number() })) + .query(async ({ ctx, input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.deleteMembership( ++ await AccountService.deleteMembership( + ctx.activeAccountId!, + input.membership_id + ); +@@ -120,9 +114,8 @@ export const accountRouter = router({ + deleteMembership: ownerProcedure + .input(z.object({ membership_id: z.number() })) + .query(async ({ ctx, input }) => { +- const accountService = new AccountService(); + const membership: MembershipWithAccount = +- await accountService.deleteMembership( ++ await AccountService.deleteMembership( + ctx.activeAccountId!, + input.membership_id + ); +@@ -143,8 +136,7 @@ export const accountRouter = router({ + }) + ) + .mutation(async ({ ctx, input }) => { +- const accountService = new AccountService(); +- const membership = await accountService.changeUserAccessWithinAccount( ++ const membership = await AccountService.changeUserAccessWithinAccount( + input.user_id, + ctx.activeAccountId!, + input.access +@@ -154,8 +146,7 @@ export const accountRouter = router({ + }; + }), + claimOwnershipOfAccount: adminProcedure.mutation(async ({ ctx }) => { +- const accountService = new AccountService(); +- const memberships = await accountService.claimOwnershipOfAccount( ++ const memberships = await AccountService.claimOwnershipOfAccount( + ctx.dbUser!.id, + ctx.activeAccountId! + ); +@@ -164,8 +155,7 @@ export const accountRouter = router({ + }; + }), + getAccountMembers: adminProcedure.query(async ({ ctx }) => { +- const accountService = new AccountService(); +- const memberships = await accountService.getAccountMembers( ++ const memberships = await AccountService.getAccountMembers( + ctx.activeAccountId! + ); + return { +diff --git a/server/trpc/routers/notes.router.ts b/server/trpc/routers/notes.router.ts +index 44f7d28..34fa4f4 100644 +--- a/server/trpc/routers/notes.router.ts ++++ b/server/trpc/routers/notes.router.ts +@@ -1,4 +1,4 @@ +-import NotesService from '~~/lib/services/notes.service'; ++import { NotesService } from '~~/lib/services/notes.service'; + import { + accountHasSpecialFeature, + adminProcedure, +@@ -11,9 +11,8 @@ import { z } from 'zod'; + + export const notesRouter = router({ + getForActiveAccount: memberProcedure.query(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const notes = ctx.activeAccountId +- ? await notesService.getNotesForAccountId(ctx.activeAccountId) ++ ? await NotesService.getNotesForAccountId(ctx.activeAccountId) + : []; + return { + notes +@@ -22,8 +21,7 @@ export const notesRouter = router({ + getById: publicProcedure + .input(z.object({ note_id: z.number() })) + .query(async ({ ctx, input }) => { +- const notesService = new NotesService(); +- const note = await notesService.getNoteById(input.note_id); ++ const note = await NotesService.getNoteById(input.note_id); + return { + note + }; +@@ -31,9 +29,8 @@ export const notesRouter = router({ + createNote: readWriteProcedure + .input(z.object({ note_text: z.string() })) + .mutation(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const note = ctx.activeAccountId +- ? await notesService.createNote(ctx.activeAccountId, input.note_text) ++ ? await NotesService.createNote(ctx.activeAccountId, input.note_text) + : null; + return { + note +@@ -42,9 +39,8 @@ export const notesRouter = router({ + deleteNote: adminProcedure + .input(z.object({ note_id: z.number() })) + .mutation(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const note = ctx.activeAccountId +- ? await notesService.deleteNote(input.note_id) ++ ? await NotesService.deleteNote(input.note_id) + : null; + return { + note +@@ -54,9 +50,8 @@ export const notesRouter = router({ + .use(accountHasSpecialFeature) + .input(z.object({ user_prompt: z.string() })) + .query(async ({ ctx, input }) => { +- const notesService = new NotesService(); + const noteText = ctx.activeAccountId +- ? await notesService.generateAINoteFromPrompt( ++ ? await NotesService.generateAINoteFromPrompt( + input.user_prompt, + ctx.activeAccountId + ) +-- +2.32.0 +