Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/src/auth/__test__/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ describe('AuthService', () => {
}).compile();

service = module.get<AuthService>(AuthService);
(service as any).grantService = {
updateGrantsByPOC: vi.fn().mockResolvedValue(undefined),
};
});

// ── register ────────────────────────────────────────────────────────────────
Expand Down
9 changes: 3 additions & 6 deletions backend/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@ import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { GrantModule } from '../grant/grant.module';

@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1h' },
}),
GrantModule,
],
controllers: [AuthController],
providers: [
{
provide: AuthService,
useClass: AuthService,
},
],
providers: [AuthService],
exports: [AuthService],
})
export class AuthModule {}
39 changes: 38 additions & 1 deletion backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { group, table } from "console";
import * as crypto from "crypto";
import { User } from "../../../middle-layer/types/User";
import { UserStatus } from "../../../middle-layer/types/UserStatus";
import { GrantService } from '../grant/grant.service';
import {
HttpException,
HttpStatus,
Expand Down Expand Up @@ -37,7 +38,7 @@ export class AuthService {
.digest("base64");
}

constructor() {
constructor(private readonly grantService: GrantService) {
try {
this.logger.log("Starting AuthService constructor...");
this.logger.log("AWS module:", typeof AWS);
Expand Down Expand Up @@ -882,6 +883,42 @@ async updateProfile(

throw new InternalServerErrorException("Failed to update user data in database");
}
// ── Step 3: Update grants where user is BCAN POC ──────────────────────
try {
await this.grantService.updateGrantsByPOC(
currentEmail,
newEmail,
`${firstName} ${lastName}`,
);
this.logger.log(`Grants updated for new POC info`);
} catch (grantError: any) {
this.logger.error(`Failed to update grants, rolling back profile changes`, grantError);

// Rollback DynamoDB
await this.dynamoDb.update({
TableName: tableName,
Key: { email: currentEmail },
UpdateExpression: "SET firstName = :firstName, lastName = :lastName, email = :email",
ExpressionAttributeValues: {
":firstName": existingUser.firstName,
":lastName": existingUser.lastName,
":email": currentEmail,
},
ReturnValues: "NONE",
}).promise();
this.logger.log(`DynamoDB rolled back to original values`);

// Rollback Cognito if email changed
if (isEmailChanging) {
await this.cognito.updateUserAttributes({
AccessToken: accessToken,
UserAttributes: [{ Name: "email", Value: currentEmail }],
}).promise();
this.logger.log(`Cognito rolled back to ${currentEmail}`);
}

throw new InternalServerErrorException("Failed to update grants. All changes have been rolled back.");
}
} catch (error) {
if (error instanceof HttpException) {
throw error;
Expand Down
1 change: 1 addition & 0 deletions backend/src/grant/grant.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import { NotificationsModule } from '../notifications/notification.module';
imports: [NotificationsModule],
controllers: [GrantController],
providers: [GrantService],
exports: [GrantService],
})
export class GrantModule { }
32 changes: 32 additions & 0 deletions backend/src/grant/grant.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,4 +641,36 @@ export class GrantService {
const diffMs = +new Date(deadline) - +new Date(alertTime);
return Math.round(diffMs / (1000 * 60 * 60 * 24));
}

// Updates the POC of grants after that POC changes their contact info
async updateGrantsByPOC(
currentEmail: string,
newEmail: string,
newName: string,
): Promise<void> {
this.logger.log(`Updating grants where bcan_poc email is ${currentEmail}`);

const tableName = process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE';
if (tableName === 'TABLE_FAILURE') {
throw new InternalServerErrorException('Server configuration error: DynamoDB table name not configured');
}

const data = await this.dynamoDb.scan({ TableName: tableName }).promise();
const grants = (data.Items as Grant[]) || [];

const affectedGrants = grants.filter(
(g) => g.bcan_poc?.POC_email?.toLowerCase() === currentEmail.toLowerCase()
);

this.logger.log(`Found ${affectedGrants.length} grants to update`);

for (const grant of affectedGrants) {
await this.updateGrant({
...grant,
bcan_poc: { POC_name: newName, POC_email: newEmail },
});
}

this.logger.log(`Successfully updated ${affectedGrants.length} grants for new POC info`);
}
}
2 changes: 2 additions & 0 deletions frontend/src/main-page/settings/Settings.tsx

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fetch grants doesn't have to be awaited here since its a side effect. I'll make this change since its small and very minor.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fetch grants doesn't have to be awaited here since its a side effect. I'll make this change since its small and very minor.

so i lied its prob good to just await this cons are much bigger than the pros

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ChangePasswordModal, { ChangePasswordFormValues } from "./ChangePasswordM
import { getAppStore } from "../../external/bcanSatchel/store";
import { setActiveUsers, updateUserProfile } from "../../external/bcanSatchel/actions";
import { User } from "../../../../middle-layer/types/User";
import { fetchGrants } from "../grants/filter-bar/processGrantData";
import { InputField } from "../../sign-up";

const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
Expand Down Expand Up @@ -104,6 +105,7 @@ function Settings() {
]);
updateUserProfile(updatedUser);
setPersonalInfo(editForm);
await fetchGrants();

setIsEditingPersonalInfo(false);
setPersonalInfoError(null);
Expand Down
Loading