prettier fixes #16

This commit is contained in:
Michael Dausmann
2023-10-24 21:18:03 +11:00
parent dc9d64ebf5
commit a7f8c37f99
56 changed files with 1706 additions and 935 deletions

View File

@@ -1,13 +1,13 @@
import { inferAsyncReturnType } from '@trpc/server'
import { inferAsyncReturnType } from '@trpc/server';
import { H3Event } from 'h3';
export async function createContext(event: H3Event){
export async function createContext(event: H3Event) {
return {
user: event.context.user, // the Supabase User
dbUser: event.context.dbUser, // the corresponding Database User
user: event.context.user, // the Supabase User
dbUser: event.context.dbUser, // the corresponding Database User
activeAccountId: event.context.activeAccountId, // the account ID that is active for the user
event, // required to enable setCookie in accountRouter
}
};
event // required to enable setCookie in accountRouter
};
}
export type Context = inferAsyncReturnType<typeof createContext>
export type Context = inferAsyncReturnType<typeof createContext>;

View File

@@ -1,5 +1,11 @@
import { TRPCError } from '@trpc/server';
import { router, adminProcedure, publicProcedure, protectedProcedure, ownerProcedure } from '../trpc'
import {
router,
adminProcedure,
publicProcedure,
protectedProcedure,
ownerProcedure
} from '../trpc';
import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum';
import { z } from 'zod';
import AccountService from '~~/lib/services/account.service';
@@ -9,113 +15,161 @@ import { MembershipWithAccount } from '~~/lib/services/service.types';
Note on proliferation of Bang syntax... adminProcedure throws if either the ctx.dbUser or the ctx.activeAccountId is not available but the compiler can't figure that out so bang quiesces the null warning
*/
export const accountRouter = router({
getDBUser: publicProcedure
.query(({ ctx }) => {
return {
dbUser: ctx.dbUser,
}
}),
getActiveAccountId: publicProcedure
.query(({ ctx }) => {
return {
activeAccountId: ctx.activeAccountId,
}
}),
getDBUser: publicProcedure.query(({ ctx }) => {
return {
dbUser: ctx.dbUser
};
}),
getActiveAccountId: publicProcedure.query(({ ctx }) => {
return {
activeAccountId: ctx.activeAccountId
};
}),
changeActiveAccount: protectedProcedure
.input(z.object({ account_id: z.number() }))
.mutation(async ({ ctx, input }) => {
const activeMembership = ctx.dbUser?.memberships.find(membership => membership.account_id == input.account_id);
if(activeMembership?.pending){
throw new TRPCError({ code: 'BAD_REQUEST', message:`membership ${activeMembership?.id} is not active so cannot be switched to` });
const activeMembership = ctx.dbUser?.memberships.find(
membership => membership.account_id == input.account_id
);
if (activeMembership?.pending) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: `membership ${activeMembership?.id} is not active so cannot be switched to`
});
}
ctx.activeAccountId = input.account_id;
setCookie(ctx.event, 'preferred-active-account-id', input.account_id.toString(), {expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10)});
setCookie(
ctx.event,
'preferred-active-account-id',
input.account_id.toString(),
{ expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10) }
);
}),
changeAccountName: adminProcedure
.input(z.object({ new_name: z.string() }))
.mutation(async ({ ctx, input }) => {
const accountService = new AccountService();
const account = await accountService.changeAccountName(ctx.activeAccountId!, input.new_name);
const account = await accountService.changeAccountName(
ctx.activeAccountId!,
input.new_name
);
return {
account,
}
}),
rotateJoinPassword: adminProcedure
.mutation(async ({ ctx }) => {
const accountService = new AccountService();
const account = await accountService.rotateJoinPassword(ctx.activeAccountId!);
return {
account,
}
account
};
}),
rotateJoinPassword: adminProcedure.mutation(async ({ ctx }) => {
const accountService = new AccountService();
const account = await accountService.rotateJoinPassword(
ctx.activeAccountId!
);
return {
account
};
}),
getAccountByJoinPassword: publicProcedure
.input(z.object({ join_password: z.string() }))
.query(async ({ input }) => {
const accountService = new AccountService();
const account = await accountService.getAccountByJoinPassword(input.join_password);
const account = await accountService.getAccountByJoinPassword(
input.join_password
);
return {
account,
}
account
};
}),
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(input.user_id, input.account_id, true);
const membership: MembershipWithAccount =
await accountService.joinUserToAccount(
input.user_id,
input.account_id,
true
);
return {
membership,
}
membership
};
}),
acceptPendingMembership: adminProcedure
.input(z.object({ membership_id: z.number() }))
.query(async ({ ctx, input }) => {
const accountService = new AccountService();
const membership: MembershipWithAccount = await accountService.acceptPendingMembership(ctx.activeAccountId!, input.membership_id);
const membership: MembershipWithAccount =
await accountService.acceptPendingMembership(
ctx.activeAccountId!,
input.membership_id
);
return {
membership,
}
membership
};
}),
rejectPendingMembership: adminProcedure
.input(z.object({ membership_id: z.number() }))
.query(async ({ ctx, input }) => {
const accountService = new AccountService();
const membership: MembershipWithAccount = await accountService.deleteMembership(ctx.activeAccountId!, input.membership_id);
const membership: MembershipWithAccount =
await accountService.deleteMembership(
ctx.activeAccountId!,
input.membership_id
);
return {
membership,
}
membership
};
}),
deleteMembership: ownerProcedure
.input(z.object({ membership_id: z.number() }))
.query(async ({ ctx, input }) => {
const accountService = new AccountService();
const membership: MembershipWithAccount = await accountService.deleteMembership(ctx.activeAccountId!, input.membership_id);
const membership: MembershipWithAccount =
await accountService.deleteMembership(
ctx.activeAccountId!,
input.membership_id
);
return {
membership,
}
membership
};
}),
changeUserAccessWithinAccount: adminProcedure
.input(z.object({ user_id: z.number(), access: z.enum([ACCOUNT_ACCESS.ADMIN, ACCOUNT_ACCESS.OWNER, ACCOUNT_ACCESS.READ_ONLY, ACCOUNT_ACCESS.READ_WRITE]) }))
.input(
z.object({
user_id: z.number(),
access: z.enum([
ACCOUNT_ACCESS.ADMIN,
ACCOUNT_ACCESS.OWNER,
ACCOUNT_ACCESS.READ_ONLY,
ACCOUNT_ACCESS.READ_WRITE
])
})
)
.mutation(async ({ ctx, input }) => {
const accountService = new AccountService();
const membership = await accountService.changeUserAccessWithinAccount(input.user_id, ctx.activeAccountId!, input.access);
const membership = await accountService.changeUserAccessWithinAccount(
input.user_id,
ctx.activeAccountId!,
input.access
);
return {
membership,
}
membership
};
}),
claimOwnershipOfAccount: adminProcedure
.mutation(async ({ ctx }) => {
const accountService = new AccountService();
const memberships = await accountService.claimOwnershipOfAccount(ctx.dbUser!.id, ctx.activeAccountId!);
return {
memberships,
}
}),
getAccountMembers: adminProcedure
.query(async ({ ctx }) => {
const accountService = new AccountService();
const memberships = await accountService.getAccountMembers(ctx.activeAccountId!);
return {
memberships,
}
}),
})
claimOwnershipOfAccount: adminProcedure.mutation(async ({ ctx }) => {
const accountService = new AccountService();
const memberships = await accountService.claimOwnershipOfAccount(
ctx.dbUser!.id,
ctx.activeAccountId!
);
return {
memberships
};
}),
getAccountMembers: adminProcedure.query(async ({ ctx }) => {
const accountService = new AccountService();
const memberships = await accountService.getAccountMembers(
ctx.activeAccountId!
);
return {
memberships
};
})
});

View File

@@ -1,12 +1,12 @@
import { router } from "~/server/trpc/trpc";
import { notesRouter } from "./notes.router";
import { authRouter } from "./auth.router";
import { accountRouter } from "./account.router";
import { router } from '~/server/trpc/trpc';
import { notesRouter } from './notes.router';
import { authRouter } from './auth.router';
import { accountRouter } from './account.router';
export const appRouter = router({
notes: notesRouter,
auth: authRouter,
account: accountRouter,
account: accountRouter
});
// export only the type definition of the API

View File

@@ -1,10 +1,9 @@
import { publicProcedure, router } from '../trpc'
import { publicProcedure, router } from '../trpc';
export const authRouter = router({
getDBUser: publicProcedure
.query(({ ctx }) => {
return {
dbUser: ctx.dbUser,
}
}),
})
getDBUser: publicProcedure.query(({ ctx }) => {
return {
dbUser: ctx.dbUser
};
})
});

View File

@@ -1,50 +1,68 @@
import NotesService from '~~/lib/services/notes.service';
import { accountHasSpecialFeature, adminProcedure, memberProcedure, publicProcedure, readWriteProcedure, router } from '../trpc';
import {
accountHasSpecialFeature,
adminProcedure,
memberProcedure,
publicProcedure,
readWriteProcedure,
router
} from '../trpc';
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):[];
return {
notes,
}
}),
getForActiveAccount: memberProcedure.query(async ({ ctx, input }) => {
const notesService = new NotesService();
const notes = ctx.activeAccountId
? await notesService.getNotesForAccountId(ctx.activeAccountId)
: [];
return {
notes
};
}),
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,
}
note
};
}),
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):null;
const note = ctx.activeAccountId
? await notesService.createNote(ctx.activeAccountId, input.note_text)
: null;
return {
note,
}
note
};
}),
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):null;
const note = ctx.activeAccountId
? await notesService.deleteNote(input.note_id)
: null;
return {
note,
}
note
};
}),
generateAINoteFromPrompt: readWriteProcedure.use(accountHasSpecialFeature)
generateAINoteFromPrompt: readWriteProcedure
.use(accountHasSpecialFeature)
.input(z.object({ user_prompt: z.string() }))
.query(async ({ ctx, input }) => {
const notesService = new NotesService();
const noteText = (ctx.activeAccountId)?await notesService.generateAINoteFromPrompt(input.user_prompt, ctx.activeAccountId):null;
const noteText = ctx.activeAccountId
? await notesService.generateAINoteFromPrompt(
input.user_prompt,
ctx.activeAccountId
)
: null;
return {
noteText
}
}),
})
};
})
});

View File

@@ -7,7 +7,7 @@
* @see https://trpc.io/docs/v10/router
* @see https://trpc.io/docs/v10/procedures
*/
import { initTRPC, TRPCError } from '@trpc/server'
import { initTRPC, TRPCError } from '@trpc/server';
import { Context } from './context';
import { ACCOUNT_ACCESS } from '~~/prisma/account-access-enum';
import superjson from 'superjson';
@@ -15,7 +15,7 @@ import { AccountLimitError } from '~~/lib/services/errors';
const t = initTRPC.context<Context>().create({
transformer: superjson,
errorFormatter: (opts)=> {
errorFormatter: opts => {
const { shape, error } = opts;
if (!(error.cause instanceof AccountLimitError)) {
return shape;
@@ -26,10 +26,10 @@ const t = initTRPC.context<Context>().create({
...shape.data,
httpStatus: 401,
code: 'UNAUTHORIZED'
},
}
};
}
})
});
/**
* auth middlewares
@@ -40,58 +40,97 @@ const isAuthed = t.middleware(({ next, ctx }) => {
}
return next({
ctx: {
user: ctx.user,
},
user: ctx.user
}
});
});
const isMemberWithAccessesForActiveAccountId = (access: ACCOUNT_ACCESS[]) =>
t.middleware(({ next, ctx }) => {
if (!ctx.dbUser || !ctx.activeAccountId) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'no user or active account information was found'
});
}
const activeMembership = ctx.dbUser.memberships.find(
membership => membership.account_id == ctx.activeAccountId
);
const isMemberWithAccessesForActiveAccountId = (access: ACCOUNT_ACCESS[]) => t.middleware(({ next, ctx }) => {
if (!ctx.dbUser || !ctx.activeAccountId) {
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'no user or active account information was found' });
}
const activeMembership = ctx.dbUser.memberships.find(membership => membership.account_id == ctx.activeAccountId);
console.log(
`isMemberWithAccessesForActiveAccountId(${access}) activeMembership?.access:${activeMembership?.access}`
);
console.log(`isMemberWithAccessesForActiveAccountId(${access}) activeMembership?.access:${activeMembership?.access}`);
if (!activeMembership) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: `user is not a member of the active account`
});
}
if(!activeMembership) {
throw new TRPCError({ code: 'UNAUTHORIZED', message:`user is not a member of the active account` });
}
if (activeMembership.pending) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: `membership ${activeMembership?.id} is pending approval`
});
}
if(activeMembership.pending) {
throw new TRPCError({ code: 'UNAUTHORIZED', message:`membership ${activeMembership?.id} is pending approval` });
}
if (access.length > 0 && !access.includes(activeMembership.access)) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: `activeMembership ${activeMembership?.id} has insufficient access (${activeMembership?.access})`
});
}
if(access.length > 0 && !access.includes(activeMembership.access)) {
throw new TRPCError({ code: 'UNAUTHORIZED', message:`activeMembership ${activeMembership?.id} has insufficient access (${activeMembership?.access})` });
}
return next({ ctx });
});
return next({ ctx });
});
export const isAccountWithFeature = (feature: string) => t.middleware(({ next, ctx }) => {
if (!ctx.dbUser || !ctx.activeAccountId) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
const activeMembership = ctx.dbUser.memberships.find(membership => membership.account_id == ctx.activeAccountId);
export const isAccountWithFeature = (feature: string) =>
t.middleware(({ next, ctx }) => {
if (!ctx.dbUser || !ctx.activeAccountId) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
const activeMembership = ctx.dbUser.memberships.find(
membership => membership.account_id == ctx.activeAccountId
);
console.log(`isAccountWithFeature(${feature}) activeMembership?.account.features:${activeMembership?.account.features}`);
if(!activeMembership?.account.features.includes(feature)){
throw new TRPCError({ code: 'UNAUTHORIZED', message: `Account does not have the ${feature} feature` });
}
return next({ ctx });
});
console.log(
`isAccountWithFeature(${feature}) activeMembership?.account.features:${activeMembership?.account.features}`
);
if (!activeMembership?.account.features.includes(feature)) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: `Account does not have the ${feature} feature`
});
}
return next({ ctx });
});
/**
* Procedures
**/
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
export const memberProcedure = protectedProcedure.use(isMemberWithAccessesForActiveAccountId([]));
export const readWriteProcedure = protectedProcedure.use(isMemberWithAccessesForActiveAccountId([ACCOUNT_ACCESS.READ_WRITE, ACCOUNT_ACCESS.ADMIN, ACCOUNT_ACCESS.OWNER]));
export const adminProcedure = protectedProcedure.use(isMemberWithAccessesForActiveAccountId([ACCOUNT_ACCESS.ADMIN, ACCOUNT_ACCESS.OWNER]));
export const ownerProcedure = protectedProcedure.use(isMemberWithAccessesForActiveAccountId([ACCOUNT_ACCESS.OWNER]));
export const memberProcedure = protectedProcedure.use(
isMemberWithAccessesForActiveAccountId([])
);
export const readWriteProcedure = protectedProcedure.use(
isMemberWithAccessesForActiveAccountId([
ACCOUNT_ACCESS.READ_WRITE,
ACCOUNT_ACCESS.ADMIN,
ACCOUNT_ACCESS.OWNER
])
);
export const adminProcedure = protectedProcedure.use(
isMemberWithAccessesForActiveAccountId([
ACCOUNT_ACCESS.ADMIN,
ACCOUNT_ACCESS.OWNER
])
);
export const ownerProcedure = protectedProcedure.use(
isMemberWithAccessesForActiveAccountId([ACCOUNT_ACCESS.OWNER])
);
export const accountHasSpecialFeature = isAccountWithFeature('SPECIAL_FEATURE');
export const router = t.router;