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

Create release 0.1.5 #111

Merged
merged 15 commits into from
Oct 16, 2023
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
31 changes: 20 additions & 11 deletions .github/workflows/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ env:
ARTIFACT_REPOSITORY_NAME: codeparty-prod-images
GKE_CLUSTER: codeparty-g11-prod # Add your cluster name here.
GKE_REGION: asia-southeast1 # Add your cluster zone here.
FIREBASE_SERVICE_ACCOUNT_PROD: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_PROD }}
PRISMA_DATABASE_URL_PROD: ${{ secrets.PRISMA_DATABASE_URL_PROD }}
MONGO_ATLAS_URL_PROD: ${{ secrets.MONGO_ATLAS_URL_PROD }}
FRONTEND_FIREBASE_CONFIG_PROD: ${{ secrets.FRONTEND_FIREBASE_CONFIG_PROD }}
FIREBASE_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_PROD }}
PRISMA_DATABASE_URL: ${{ secrets.PRISMA_DATABASE_URL_PROD }}
MONGO_ATLAS_URL: ${{ secrets.MONGO_ATLAS_URL_PROD }}
FRONTEND_FIREBASE_CONFIG: ${{ secrets.FRONTEND_FIREBASE_CONFIG_PROD }}

jobs:
setup-build-publish-deploy:
name: Setup, Build, Publish, and Deploy
runs-on: ubuntu-latest
environment: production
permissions:
contents: 'read'
id-token: 'write'

steps:
- name: Checkout
Expand Down Expand Up @@ -52,6 +55,10 @@ jobs:
cluster_name: ${{ env.GKE_CLUSTER }}
location: ${{ env.GKE_REGION }}

# Install the dependencies such as prisma
- name: Install dependencies with immutable lockfile
run: yarn install --frozen-lockfile

# Apply prisma migrations to production prisma database
- name: Apply prisma database migrations
run: |-
Expand All @@ -60,24 +67,26 @@ jobs:
# Build the Docker images and push to Google Artifact Repository
- name: Build and push Docker images
run: |-
chmod u+x ./deployment/build-prod-images.sh
./deployment/build-prod-images.sh
chmod u+x ./build-prod-images.sh
./build-prod-images.sh
working-directory: ./deployment

# Set the secrets that are used as env variables in the manifest files
- name: Set kubectl secrets
run: |-
kubectl create secret generic firebase-service-account \
--from-literal=firebase-service-account=$FIREBASE_SERVICE_ACCOUNT_PROD
--from-literal=firebase-service-account=$FIREBASE_SERVICE_ACCOUNT
kubectl create secret generic prisma-database-url \
--from-literal=prisma-database-url=$PRISMA_DATABASE_URL_PROD
--from-literal=prisma-database-url=$PRISMA_DATABASE_URL
kubectl create secret generic mongo-atlas-url \
--from-literal=mongo-atlas-url=$MONGO_ATLAS_URL_PROD
--from-literal=mongo-atlas-url=$MONGO_ATLAS_URL
kubectl create secret generic frontend-firebase-config \
--from-literal=frontend-firebase-config=$FRONTEND_FIREBASE_CONFIG_PROD
--from-literal=frontend-firebase-config=$FRONTEND_FIREBASE_CONFIG

# Deploy the Docker images to the GKE cluster
- name: Deploy production application
run: |-
kubectl apply -f ./deployment/gke-prod-manifests
kubectl apply -f ./gke-prod-manifests
kubectl rollout status deployment
kubectl get services -o wide
working-directory: ./deployment
8 changes: 4 additions & 4 deletions deployment/build-prod-images.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Build root docker image
docker build -t peerprep-base -f ../Dockerfile .
# Build root docker image with context set to be parent directory
docker build -t peerprep-base -f ../Dockerfile ..

# Create array of services
service_array=("admin-service" "collaboration-service" "gateway" "matching-service" "question-service" "user-service" "frontend")

# Build and publish prod images
# Build and publish prod images with context set to be parent directory
for s in ${service_array[@]}; do
docker build \
--tag asia-southeast1-docker.pkg.dev/$PROJECT_ID/$ARTIFACT_REPOSITORY_NAME/${service_array[s]}:latest \
--file prod-dockerfiles/Dockerfile.${service_array[s]}-prod .
--file prod-dockerfiles/Dockerfile.${service_array[s]}-prod ..
docker push asia-southeast1-docker.pkg.dev/$PROJECT_ID/$ARTIFACT_REPOSITORY_NAME/${service_array[s]}:latest
done
148 changes: 148 additions & 0 deletions frontend/providers/MatchmakingProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, {
createContext,
useContext,
useState,
useEffect,
useCallback,
} from "react";
import { io, Socket } from "socket.io-client";
import { Match } from "@prisma/client";
import { AuthContext } from "@/contexts/AuthContext";

const SERVER_URL = "http://localhost:5002";

interface MatchmakingContextValue {
socket: Socket | null;
match: Match | null;
message: string;
error: string;
joinQueue: (difficulties: string[], programmingLang: string) => void;
sendMessage: (message: string) => void;
leaveMatch: () => void;
cancelLooking: () => void;
}

export const MatchmakingContext = createContext<
MatchmakingContextValue | undefined
>(undefined);

interface MatchmakingProviderProps {
children: React.ReactNode;
}

export const MatchmakingProvider: React.FC<MatchmakingProviderProps> = ({
children,
}) => {
const [socket, setSocket] = useState<Socket | null>(null);
const [match, setMatch] = useState<Match | null>(null);
const [message, setMessage] = useState<string>("");
const [error, setError] = useState<string>("");

const { user: currentUser, authIsReady } = useContext(AuthContext);

const generateRandomNumber = () => {
// Return a random number either 0 or 1 as a string
return Math.floor(Math.random() * 2).toString();
};

// Initialize socket connection
useEffect(() => {
if (currentUser) {
const newSocket = io(SERVER_URL, {
autoConnect: false,
// query: { username: currentUser?.email },
query: { username: generateRandomNumber() },
});
setSocket(newSocket);
newSocket.connect();

console.log("Socket connected");

return () => {
newSocket.close();
};
}
}, [currentUser]);

useEffect(() => {
if (!socket) return;

socket.on("connect", () => {
console.log("Connected to server");
});

socket.on("matchFound", (match: Match) => {
console.log("Match found:", match);
setMatch(match);
});

socket.on("receiveMessage", (message: string) => {
console.log("Message received:", message);
setMessage(message);
});

socket.on("error", (error: string) => {
console.error("An error occurred:", error);
setError(error);
});

socket.on("disconnect", () => {
console.log("Disconnected from server");
});

return () => {
socket.off("connect");
socket.off("matchFound");
socket.off("receiveMessage");
socket.off("error");
socket.off("disconnect");
};
}, [socket]);

const joinQueue = useCallback(
(difficulties: string[], programmingLang: string) => {
if (!socket) return;

socket.emit("lookingForMatch", difficulties, programmingLang);
},
[socket]
);

const sendMessage = useCallback(
(message: string) => {
if (!socket) return;

socket.emit("sendMessage", message);
},
[socket]
);

const leaveMatch = useCallback(() => {
if (!socket) return;

socket.emit("leaveMatch");
}, [socket]);

const cancelLooking = useCallback(() => {
if (!socket) return;

socket.emit("cancelLooking");
}, [socket]);

const value = {
socket,
match,
message,
error,
joinQueue,
sendMessage,
leaveMatch,
cancelLooking,
};

return (
<MatchmakingContext.Provider value={value}>
{children}
</MatchmakingContext.Provider>
);
};
12 changes: 12 additions & 0 deletions frontend/src/components/interviews/InterviewsLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";
import { MatchmakingProvider } from "../../../providers/MatchmakingProvider";

interface InterviewsLayoutProps {
children: React.ReactNode;
}

const InterviewsLayout: React.FC<InterviewsLayoutProps> = ({ children }) => {
return <MatchmakingProvider>{children}</MatchmakingProvider>;
};

export default InterviewsLayout;
10 changes: 10 additions & 0 deletions frontend/src/hooks/useMatchmaking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from "react";
import { MatchmakingContext } from "../../providers/MatchmakingProvider";

export function useMatchmaking() {
const context = useContext(MatchmakingContext);
if (!context) {
throw new Error("useMatchmaking must be used within a MatchmakingProvider");
}
return context;
}
27 changes: 15 additions & 12 deletions frontend/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import '@/styles/globals.scss'
import type { AppProps } from 'next/app'
import Layout from '../components/common/layout'
import { Noto_Sans } from 'next/font/google'
import "@/styles/globals.scss";
import type { AppProps } from "next/app";
import Layout from "../components/common/layout";
import { Noto_Sans } from "next/font/google";
import AuthContextProvider from "@/contexts/AuthContext";
import AuthChecker from '@/components/common/auth-checker';
import { MatchmakingProvider } from "../../providers/MatchmakingProvider";
import AuthChecker from "@/components/common/auth-checker";

const notoSans = Noto_Sans({
weight: ['400', '500', '600', '700', '800', '900'],
preload: false
})
weight: ["400", "500", "600", "700", "800", "900"],
preload: false,
});

export default function App({ Component, pageProps }: AppProps) {
return (
Expand All @@ -21,12 +22,14 @@ export default function App({ Component, pageProps }: AppProps) {
<main>
<AuthContextProvider>
<AuthChecker>
<Layout>
<Component {...pageProps} />
</Layout>
<MatchmakingProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</MatchmakingProvider>
</AuthChecker>
</AuthContextProvider>
</main>
</>
)
);
}
37 changes: 0 additions & 37 deletions frontend/src/pages/interviews/[id]/find-match.tsx

This file was deleted.

52 changes: 0 additions & 52 deletions frontend/src/pages/interviews/[id]/match-found.tsx

This file was deleted.

Loading