massive state refactor, mostly fixes #3
This commit is contained in:
@@ -8,6 +8,7 @@ import AuthService from '~~/lib/services/auth.service';
|
||||
export async function createContext(event: H3Event){
|
||||
let user: User | null = null;
|
||||
let dbUser: FullDBUser | null = null;
|
||||
let activeAccountId: number | null = null;
|
||||
|
||||
if (!user) {
|
||||
user = await serverSupabaseUser(event);
|
||||
@@ -20,11 +21,24 @@ export async function createContext(event: H3Event){
|
||||
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`);
|
||||
}
|
||||
|
||||
if(dbUser){
|
||||
const preferredAccountId = getCookie(event, 'preferred-active-account-id')
|
||||
if(preferredAccountId && dbUser?.memberships.find(m => m.account_id === +preferredAccountId && !m.pending)){
|
||||
activeAccountId = +preferredAccountId
|
||||
} else {
|
||||
const defaultActive = dbUser.memberships[0].account_id.toString();
|
||||
setCookie(event, 'preferred-active-account-id', defaultActive, {expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10)});
|
||||
activeAccountId = +defaultActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
dbUser,
|
||||
user, // the Supabase User
|
||||
dbUser, // the corresponding Database User
|
||||
activeAccountId, // the account ID that is active for the user
|
||||
event, // required to enable setCookie in accountRouter
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,93 +1,96 @@
|
||||
import { router, adminProcedure, publicProcedure } from '../trpc'
|
||||
import { router, adminProcedure, publicProcedure, protectedProcedure } 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';
|
||||
|
||||
/*
|
||||
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: protectedProcedure
|
||||
.query(({ ctx }) => {
|
||||
return {
|
||||
dbUser: ctx.dbUser,
|
||||
}
|
||||
}),
|
||||
getActiveAccountId: protectedProcedure
|
||||
.query(({ ctx }) => {
|
||||
return {
|
||||
activeAccountId: ctx.activeAccountId,
|
||||
}
|
||||
}),
|
||||
changeActiveAccount: adminProcedure
|
||||
.input(z.object({ account_id: z.number() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
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)});
|
||||
}),
|
||||
changeAccountName: adminProcedure
|
||||
.input(z.object({ account_id: z.number(), new_name: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
.input(z.object({ new_name: z.string() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const accountService = new AccountService();
|
||||
const account = await accountService.changeAccountName(input.account_id, input.new_name);
|
||||
|
||||
const account = await accountService.changeAccountName(ctx.activeAccountId!, input.new_name);
|
||||
return {
|
||||
account,
|
||||
}
|
||||
}),
|
||||
rotateJoinPassword: adminProcedure
|
||||
.input(z.object({ account_id: z.number() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
.mutation(async ({ ctx }) => {
|
||||
const accountService = new AccountService();
|
||||
const account = await accountService.rotateJoinPassword(input.account_id);
|
||||
|
||||
const account = await accountService.rotateJoinPassword(ctx.activeAccountId!);
|
||||
return {
|
||||
account,
|
||||
}
|
||||
}),
|
||||
getAccountByJoinPassword: publicProcedure
|
||||
.input(z.object({ join_password: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
.query(async ({ input }) => {
|
||||
const accountService = new AccountService();
|
||||
const account = await accountService.getAccountByJoinPassword(input.join_password);
|
||||
|
||||
return {
|
||||
account,
|
||||
}
|
||||
}),
|
||||
joinUserToAccount: adminProcedure
|
||||
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() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
.mutation(async ({ input }) => {
|
||||
const accountService = new AccountService();
|
||||
const membership: MembershipWithAccount| null = (ctx.dbUser?.id)?await accountService.joinUserToAccount(input.user_id, input.account_id, false):null;
|
||||
return {
|
||||
membership,
|
||||
}
|
||||
}),
|
||||
joinUserToAccountPending: publicProcedure
|
||||
.input(z.object({ account_id: z.number(), user_id: z.number() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const accountService = new AccountService();
|
||||
const membership: MembershipWithAccount| null = (ctx.dbUser?.id)?await accountService.joinUserToAccount(input.user_id, input.account_id, true):null;
|
||||
const membership: MembershipWithAccount = await accountService.joinUserToAccount(input.user_id, input.account_id, true);
|
||||
return {
|
||||
membership,
|
||||
}
|
||||
}),
|
||||
acceptPendingMembership: adminProcedure
|
||||
.input(z.object({ account_id: z.number(), membership_id: z.number() }))
|
||||
.input(z.object({ membership_id: z.number() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const accountService = new AccountService();
|
||||
const membership: MembershipWithAccount| null = (ctx.dbUser?.id)?await accountService.acceptPendingMembership(input.account_id, input.membership_id):null;
|
||||
const membership: MembershipWithAccount = await accountService.acceptPendingMembership(ctx.activeAccountId!, input.membership_id);
|
||||
return {
|
||||
membership,
|
||||
}
|
||||
}),
|
||||
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 }) => {
|
||||
.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, input.account_id, input.access);
|
||||
|
||||
const membership = await accountService.changeUserAccessWithinAccount(input.user_id, ctx.activeAccountId!, input.access);
|
||||
return {
|
||||
membership,
|
||||
}
|
||||
}),
|
||||
claimOwnershipOfAccount: adminProcedure
|
||||
.input(z.object({ account_id: z.number() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
claimOwnershipOfAccount: adminProcedure
|
||||
.mutation(async ({ ctx }) => {
|
||||
const accountService = new AccountService();
|
||||
const membership = await accountService.claimOwnershipOfAccount(ctx.dbUser!.id, input.account_id); // adminProcedure errors if ctx.dbUser is null so bang is ok here
|
||||
|
||||
const membership = await accountService.claimOwnershipOfAccount(ctx.dbUser!.id, ctx.activeAccountId!);
|
||||
return {
|
||||
membership,
|
||||
}
|
||||
}),
|
||||
getAccountMembers: adminProcedure
|
||||
.input(z.object({ account_id: z.number() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
getAccountMembers: adminProcedure
|
||||
.query(async ({ ctx }) => {
|
||||
const accountService = new AccountService();
|
||||
const memberships = await accountService.getAccountMembers(input.account_id);
|
||||
|
||||
const memberships = await accountService.getAccountMembers(ctx.activeAccountId!);
|
||||
return {
|
||||
memberships,
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ import { z } from 'zod';
|
||||
|
||||
export const notesRouter = router({
|
||||
getForCurrentUser: protectedProcedure
|
||||
.input(z.object({ account_id: z.number() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const notesService = new NotesService();
|
||||
const notes = await notesService.getNotesForAccountId(input.account_id);
|
||||
const notes = (ctx.activeAccountId)?await notesService.getNotesForAccountId(ctx.activeAccountId):[];
|
||||
return {
|
||||
notes,
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
import { initTRPC, TRPCError } from '@trpc/server'
|
||||
import { Context } from './context';
|
||||
import { z } from 'zod';
|
||||
import { ACCOUNT_ACCESS } from '@prisma/client';
|
||||
import superjson from 'superjson';
|
||||
|
||||
@@ -32,14 +31,11 @@ const isAuthed = t.middleware(({ next, ctx }) => {
|
||||
});
|
||||
|
||||
const isAdminForInputAccountId = t.middleware(({ next, rawInput, ctx }) => {
|
||||
if (!ctx.dbUser) {
|
||||
if (!ctx.dbUser || !ctx.activeAccountId) {
|
||||
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
||||
}
|
||||
const result = z.object({ account_id: z.number() }).safeParse(rawInput);
|
||||
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);
|
||||
if(!test_membership || (test_membership?.access !== ACCOUNT_ACCESS.ADMIN && test_membership?.access !== ACCOUNT_ACCESS.OWNER)) {
|
||||
const activeMembership = ctx.dbUser.memberships.find(membership => membership.account_id == ctx.activeAccountId);
|
||||
if(!activeMembership || (activeMembership?.access !== ACCOUNT_ACCESS.ADMIN && activeMembership?.access !== ACCOUNT_ACCESS.OWNER)) {
|
||||
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user