Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance optimizations #39

Merged
merged 6 commits into from
Jan 25, 2025
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
6 changes: 3 additions & 3 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand All @@ -37,7 +37,7 @@ jobs:
- name: Set up Node for testing
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand All @@ -37,7 +37,7 @@ jobs:
- name: Set up Node for testing
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand Down Expand Up @@ -109,7 +109,7 @@ jobs:
- name: Set up Node for testing
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand Down Expand Up @@ -152,7 +152,7 @@ jobs:
- name: Set up Node for testing
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
- uses: actions/checkout@v4
env:
HUSKY: "0"
Expand Down
24 changes: 22 additions & 2 deletions cloudformation/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,34 @@ Resources:
Type: AWS::Serverless::Function
DependsOn:
- AppLogGroups
Metadata:
BuildMethod: esbuild
BuildProperties:
Format: esm
Minify: true
OutExtension:
- .js=.mjs
Target: "es2022"
Sourcemap: false
EntryPoints:
- api/lambda.js
External:
- aws-sdk
Banner:
- js=import path from 'path';
import { fileURLToPath } from 'url';
import { createRequire as topLevelCreateRequire } from 'module';
const require = topLevelCreateRequire(import.meta.url);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Properties:
Architectures: [arm64]
CodeUri: ../dist
AutoPublishAlias: live
Runtime: nodejs20.x
Runtime: nodejs22.x
Description: !Sub "${ApplicationFriendlyName} API Lambda"
FunctionName: !Sub ${ApplicationPrefix}-lambda
Handler: api/lambda.handler
Handler: lambda.handler
MemorySize: 512
Role: !GetAtt AppSecurityRoles.Outputs.MainFunctionRoleArn
Timeout: 60
Expand Down
32 changes: 4 additions & 28 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"scripts": {
"build": "yarn workspaces run build && yarn lockfile-manage",
"dev": "concurrently --names 'api,ui' 'yarn workspace infra-core-api run dev' 'yarn workspace infra-core-ui run dev'",
"lockfile-manage": "synp --with-workspace --source-file yarn.lock && cp package-lock.json dist/ && cp package.json dist/ && rm package-lock.json",
"lockfile-manage": "synp --with-workspace --source-file yarn.lock && cp package-lock.json dist/ && cp src/api/package.json dist/ && rm package-lock.json",
"prettier": "yarn workspaces run prettier && prettier --check tests/**/*.ts",
"prettier:write": "yarn workspaces run prettier:write && prettier --write tests/**/*.ts",
"lint": "yarn workspaces run lint",
Expand All @@ -25,35 +25,11 @@
"test:e2e": "playwright test",
"test:e2e-ui": "playwright test --ui"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.624.0",
"@aws-sdk/client-secrets-manager": "^3.624.0",
"@aws-sdk/util-dynamodb": "^3.624.0",
"@azure/msal-node": "^2.16.1",
"@fastify/auth": "^5.0.1",
"@fastify/aws-lambda": "^5.0.0",
"@fastify/caching": "^9.0.1",
"@fastify/cors": "^10.0.1",
"@touch4it/ical-timezones": "^1.9.0",
"discord.js": "^14.15.3",
"dotenv": "^16.4.5",
"fastify": "^5.1.0",
"fastify-plugin": "^4.5.1",
"ical-generator": "^7.2.0",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"node-cache": "^5.1.2",
"pluralize": "^8.0.0",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.23.2",
"zod-validation-error": "^3.3.1"
},
"dependencies": {},
"devDependencies": {
"@eslint/compat": "^1.1.1",
"@playwright/test": "^1.49.1",
"@tsconfig/node20": "^20.1.4",
"@tsconfig/node22": "^22.0.0",
"@types/node": "^22.1.0",
"@types/pluralize": "^0.0.33",
"@types/react": "^18.3.3",
Expand Down Expand Up @@ -82,7 +58,7 @@
"husky": "^9.1.4",
"identity-obj-proxy": "^3.0.0",
"jsdom": "^24.1.1",
"node-ical": "^0.18.0",
"node-ical": "^0.20.1",
"postcss": "^8.4.41",
"postcss-preset-mantine": "^1.17.0",
"postcss-simple-vars": "^7.0.1",
Expand Down
6 changes: 2 additions & 4 deletions src/api/functions/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ import {
import { genericConfig } from "../../common/config.js";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";

const dynamoClient = new DynamoDBClient({
region: genericConfig.AwsRegion,
});

export async function getItemFromCache(
dynamoClient: DynamoDBClient,
key: string,
): Promise<null | Record<string, string | number>> {
const currentTime = Math.floor(Date.now() / 1000);
Expand All @@ -37,6 +34,7 @@ export async function getItemFromCache(
}

export async function insertItemIntoCache(
dynamoClient: DynamoDBClient,
key: string,
value: Record<string, string | number>,
expireAt: Date,
Expand Down
4 changes: 3 additions & 1 deletion src/api/functions/discord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { FastifyBaseLogger } from "fastify";
import { DiscordEventError } from "../../common/errors/index.js";
import { getSecretValue } from "../plugins/auth.js";
import { genericConfig } from "../../common/config.js";
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";

// https://stackoverflow.com/a/3809435/5684541
// https://calendar-buff.acmuiuc.pages.dev/calendar?id=dd7af73a-3df6-4e12-b228-0d2dac34fda7&date=2024-08-30
Expand All @@ -24,12 +25,13 @@ export type IUpdateDiscord = EventPostRequest & { id: string };

const urlRegex = /https:\/\/[a-z0-9\.-]+\/calendar\?id=([a-f0-9-]+)/;
export const updateDiscord = async (
smClient: SecretsManagerClient,
event: IUpdateDiscord,
isDelete: boolean = false,
logger: FastifyBaseLogger,
): Promise<null | GuildScheduledEventCreateOptions> => {
const secretApiConfig =
(await getSecretValue(genericConfig.ConfigSecretName)) || {};
(await getSecretValue(smClient, genericConfig.ConfigSecretName)) || {};
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
let payload: GuildScheduledEventCreateOptions | null = null;

Expand Down
13 changes: 11 additions & 2 deletions src/api/functions/entraId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
officersGroupTestingId,
} from "../../common/config.js";
import {
BaseError,

Check warning on line 9 in src/api/functions/entraId.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

'BaseError' is defined but never used. Allowed unused vars must match /^_/u
EntraGroupError,
EntraInvitationError,
InternalServerError,
Expand All @@ -18,18 +18,23 @@
EntraGroupActions,
EntraInvitationResponse,
} from "../../common/types/iam.js";
import { FastifyInstance } from "fastify";

function validateGroupId(groupId: string): boolean {
const groupIdPattern = /^[a-zA-Z0-9-]+$/; // Adjust the pattern as needed
return groupIdPattern.test(groupId);
}

export async function getEntraIdToken(
fastify: FastifyInstance,
clientId: string,
scopes: string[] = ["https://graph.microsoft.com/.default"],
) {
const secretApiConfig =
(await getSecretValue(genericConfig.ConfigSecretName)) || {};
(await getSecretValue(
fastify.secretsManagerClient,
genericConfig.ConfigSecretName,
)) || {};
if (
!secretApiConfig.entra_id_private_key ||
!secretApiConfig.entra_id_thumbprint
Expand All @@ -42,7 +47,10 @@
secretApiConfig.entra_id_private_key as string,
"base64",
).toString("utf8");
const cachedToken = await getItemFromCache("entra_id_access_token");
const cachedToken = await getItemFromCache(
fastify.dynamoClient,
"entra_id_access_token",
);
if (cachedToken) {
return cachedToken["token"] as string;
}
Expand Down Expand Up @@ -70,6 +78,7 @@
date.setTime(date.getTime() - 30000);
if (result?.accessToken) {
await insertItemIntoCache(
fastify.dynamoClient,
"entra_id_access_token",
{ token: result?.accessToken },
date,
Expand Down
14 changes: 13 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ import iamRoutes from "./routes/iam.js";
import ticketsPlugin from "./routes/tickets.js";
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
import NodeCache from "node-cache";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";

dotenv.config();

const now = () => Date.now();

async function init() {
const dynamoClient = new DynamoDBClient({
region: genericConfig.AwsRegion,
});

const secretsManagerClient = new SecretsManagerClient({
region: genericConfig.AwsRegion,
});

const app: FastifyInstance = fastify({
logger: {
level: process.env.LOG_LEVEL || "info",
Expand Down Expand Up @@ -70,6 +80,8 @@ async function init() {
app.environmentConfig =
environmentConfig[app.runEnvironment as RunEnvironment];
app.nodeCache = new NodeCache({ checkperiod: 30 });
app.dynamoClient = dynamoClient;
app.secretsManagerClient = secretsManagerClient;
app.addHook("onRequest", (req, _, done) => {
req.startTime = now();
const hostname = req.hostname;
Expand Down Expand Up @@ -108,7 +120,7 @@ async function init() {
await app.register(cors, {
origin: app.environmentConfig.ValidCorsOrigins,
});

app.log.info("Initialized new Fastify instance...");
return app;
}

Expand Down
30 changes: 28 additions & 2 deletions src/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,33 @@
"prettier:write": "prettier --write *.ts **/*.ts"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.624.0",
"@aws-sdk/client-secrets-manager": "^3.624.0",
"@aws-sdk/client-sts": "^3.726.0",
"node-cache": "^5.1.2"
"@aws-sdk/util-dynamodb": "^3.624.0",
"@azure/msal-node": "^2.16.1",
"@fastify/auth": "^5.0.1",
"@fastify/aws-lambda": "^5.0.0",
"@fastify/caching": "^9.0.1",
"@fastify/cors": "^10.0.1",
"@touch4it/ical-timezones": "^1.9.0",
"discord.js": "^14.15.3",
"dotenv": "^16.4.5",
"esbuild": "^0.24.2",
"fastify": "^5.1.0",
"fastify-plugin": "^4.5.1",
"ical-generator": "^7.2.0",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"node-cache": "^5.1.2",
"pluralize": "^8.0.0",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.23.2",
"zod-validation-error": "^3.3.1"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.0"
}
}
}
17 changes: 7 additions & 10 deletions src/api/plugins/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
} from "../../common/errors/index.js";
import { genericConfig, SecretConfig } from "../../common/config.js";
import { getGroupRoles, getUserRoles } from "../functions/authorization.js";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

Check warning on line 18 in src/api/plugins/auth.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

'DynamoDBClient' is defined but never used. Allowed unused vars must match /^_/u

function intersection<T>(setA: Set<T>, setB: Set<T>): Set<T> {
const _intersection = new Set<T>();
Expand Down Expand Up @@ -53,15 +53,9 @@
ver: string;
roles?: string[];
};
const smClient = new SecretsManagerClient({
region: genericConfig.AwsRegion,
});

const dynamoClient = new DynamoDBClient({
region: genericConfig.AwsRegion,
});

export const getSecretValue = async (
smClient: SecretsManagerClient,
secretId: string,
): Promise<Record<string, string | number | boolean> | null | SecretConfig> => {
const data = await smClient.send(
Expand Down Expand Up @@ -118,7 +112,10 @@
signingKey =
process.env.JwtSigningKey ||
((
(await getSecretValue(genericConfig.ConfigSecretName)) || {
(await getSecretValue(
fastify.secretsManagerClient,
genericConfig.ConfigSecretName,
)) || {
jwt_key: "",
}
).jwt_key as string) ||
Expand Down Expand Up @@ -168,7 +165,7 @@
if (verifiedTokenData.groups) {
const groupRoles = await Promise.allSettled(
verifiedTokenData.groups.map((x) =>
getGroupRoles(dynamoClient, fastify, x),
getGroupRoles(fastify.dynamoClient, fastify, x),
),
);
for (const result of groupRoles) {
Expand Down Expand Up @@ -201,7 +198,7 @@
if (request.username) {
try {
const userAuth = await getUserRoles(
dynamoClient,
fastify.dynamoClient,
fastify,
request.username,
);
Expand Down
Loading
Loading