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

#02 AS: integrated redis into the app and dockerized the app #3

Merged
merged 4 commits into from
Feb 22, 2024
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
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
./node_modules
Dockerfile
.dockerignore
docker-compose.yml
.env.local
start.txt
.next
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
entrypoint.sh text eol=lf
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM docker:20-dind

RUN apk add --no-cache nodejs npm

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN chmod +x /app/entrypoint.sh

EXPOSE 3000

ENTRYPOINT ["sh", "-c", "dockerd-entrypoint.sh &>/dev/null & /app/entrypoint.sh"]
72 changes: 13 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,11 @@ The Virtual Event Starter Kit simplifies organizing and executing virtual events

![Alt text](https://i.imgur.com/odAVqks.png)

### TODO
***
1. Database seeding script to populate database with sample data.
2. Unit tests for backend and frontend.
3. Live chat functionality.
4. Dockerize the app.

### Features
***
- **Multiple stages:** Customize and manage multiple event stages effortlessly, accommodating various sessions on each stage.<br>
- **Flexible stage configuration:** Configure each stage with either an embedded YouTube stream or a live interactive audio-video experience powered by a third-party service.<br>
- **Sponsor expo:** Showcase sponsors with individual virtual booths, allowing for enhanced visibility and engagement opportunities.<br>
- **Career Fair:** Facilitate networking and job discovery for attendees with dedicated career fair sections.<br>
- **Ticket registration and generation:** Simplify event registration and ticket issuance processes for attendees.<br>
- **Speaker pages and bios:** Highlight event speakers with dedicated pages featuring bios and contributions.<br>
- **Flexible stage configuration:** Configure each stage with an embedded YouTube video.<br>
- **Speaker pages:** Highlight event speakers with dedicated pages.<br>
- **Schedule management:** Streamline event scheduling and coordination with a comprehensive schedule management system.<br>

### Technologies Used
Expand Down Expand Up @@ -61,53 +51,17 @@ The backend API sends the response back to the frontend, which then displays the
Additionally, the backend API may also send responses directly to the user in certain scenarios, such as authentication or error messages.


## Installation

Follow these steps to set up the project locally:

### Prerequisites

- Node.js and npm must be installed on your machine.
- Docker must be installed and running on your machine for the Supabase services.

### Setup

1. **Install dependencies**

Open a terminal in the project directory and run the following command:

```bash
npm install

1. **Create a local environment file**

Create a file named ```.env.local``` in the root of your project directory. This file will
store your local environment variables.

1. **Start Supabase locally**

To start Supabase locally, ensure the Docker daemon is running, then execute the
following command:

```bash
npx supabase start

1. **Configure environment variables**

After starting Supabase, it will provide an **API_URL** and an **ANON_KEY**. You need
to add these values to your ```.env.local``` file as follows:

NEXT_PUBLIC_SUPABASE_URL=<Your Supabase API URL>

NEXT_PUBLIC_SUPABASE_ANON_KEY=<Your Supabase ANON KEY>

1. **Start the development server**

Finally, to run your project locally, execute:
### Installation

```bash
npm run dev
**Prerequisites**
- Docker

This will start the development server. Once running, you can access your project at [http://localhost:3000/](http://localhost:3000/).
**Run locally**
```
git clone https://github.com/devzero-inc/virtual-event-starter-kit.git
cd virtual-event-starter-kit
docker compose up
```

*Note* - You wont see any schedules or speakers detail on the website since it wont be there on your local database.
The app will be running on PORT:3000 -> http://localhost:3000/
Now just go to http://localhost:3000/ and explore the application.
20 changes: 9 additions & 11 deletions app/api/events/route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { NextResponse } from "next/server";
import { getEventsFromSupabase } from "@/utils/controller";
import {
AccessDeniedError,
SupabaseError,
UnhandledError,
NotAuthenticatedError,
} from "@/errors/databaseerror";
import { getEventsFromSupabase } from "@/controller/controller";

export async function GET() {
export async function GET(): Promise<NextResponse> {
try {
const data = await getEventsFromSupabase();
const events = await getEventsFromSupabase();

return NextResponse.json({
message: "retrieved data successfully",
data: data,
status: 200,
events: events,
});
} catch (err) {
if (err instanceof AccessDeniedError) {
Expand All @@ -27,16 +25,16 @@ export async function GET() {
status: 503,
message: err.message,
});
} else if (err instanceof UnhandledError) {
return NextResponse.json({
status: 500,
message: err.message,
});
} else if (err instanceof NotAuthenticatedError) {
return NextResponse.json({
status: 401,
message: err.message,
});
} else {
return NextResponse.json({
status: 500,
message: "Internal server error",
});
}
}
}
160 changes: 160 additions & 0 deletions app/api/routes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { GET as eventsGet } from "@/app/api/events/route";
import { GET as speakersGet } from "@/app/api/speakers/route";

import {
getEventsFromSupabase,
getSpeakersFromSupabase,
} from "@/controller/controller";
import {
SupabaseError,
NotAuthenticatedError,
AccessDeniedError,
UnhandledError,
} from "@/errors/databaseerror";

jest.mock("../../controller/controller", () => ({
getEventsFromSupabase: jest.fn(),
getSpeakersFromSupabase: jest.fn(),
}));

describe("GET api/events", () => {
it("should return status 200 if events are fetched successfully", async () => {
(getEventsFromSupabase as jest.Mock).mockResolvedValueOnce([
{ id: 1, title: "Test Events" },
]);

const response = await eventsGet();
const jsonResponse = await response.json();

expect(jsonResponse).toEqual({
status: 200,
events: [{ id: 1, title: "Test Events" }],
});
});

it("should return status 503 if table is not found", async () => {
const errMessage = "SUPABASE_ERR";
(getEventsFromSupabase as jest.Mock).mockRejectedValueOnce(
new SupabaseError(errMessage)
);
const response = await eventsGet();
const jsonResponse = await response.json();

expect(jsonResponse).toEqual({
status: 503,
message: errMessage,
});
});

it("should return status 403 if the user is denied access to the server", async () => {
const errMessage = "ACCESS_DENIED";

(getEventsFromSupabase as jest.Mock).mockRejectedValueOnce(
new AccessDeniedError(errMessage)
);
const response = await eventsGet();
const jsonResponse = await response.json();

expect(jsonResponse).toEqual({
status: 403,
message: errMessage,
});
});

it("should return status 500 if it is an unhandled error", async () => {
const errMessage = "UNHANDLED_ERR";

(getEventsFromSupabase as jest.Mock).mockRejectedValueOnce(
new UnhandledError(errMessage)
);
const response = await eventsGet();
const jsonResponse = await response.json();

expect(jsonResponse).toEqual({
status: 500,
message: 'Internal server error',
});
});

it("should return status 401 if the user is unauthenticated", async () => {
const errMessage = "Not Authenticated Error";
(getEventsFromSupabase as jest.Mock).mockRejectedValueOnce(
new NotAuthenticatedError(errMessage)
);
const response = await eventsGet();
const jsonResponse = await response.json();

expect(jsonResponse).toEqual({
status: 401,
message: errMessage,
});
});
});

describe("GET api/speakers", () => {
it("should return status 200 if speakers are fetched successfully", async () => {
(getSpeakersFromSupabase as jest.Mock).mockResolvedValueOnce([
{ id: 1, title: "Test Speakers" },
]);
const response = await speakersGet();
const jsonResponse = await response!.json();
expect(jsonResponse).toEqual({
status: 200,
speakers: [{ id: 1, title: "Test Speakers" }],
});
});

it("should return status 503 if table is not found", async () => {
const errMessage = "SUPABASE_ERR";
(getSpeakersFromSupabase as jest.Mock).mockRejectedValueOnce(
new SupabaseError(errMessage)
);
const response = await speakersGet();
const jsonResponse = await response!.json();
expect(jsonResponse).toEqual({
status: 503,
message: errMessage,
});
});

it("should return status 403 if user is denied access to the server", async () => {
const errMessage = "ACCESS_DENIED";
(getSpeakersFromSupabase as jest.Mock).mockRejectedValueOnce(
new AccessDeniedError(errMessage)
);
const response = await speakersGet();
const jsonResponse = await response!.json();

expect(jsonResponse).toEqual({
status: 403,
message: errMessage,
});
});

it("should return status 500 if it is an unhandled error", async () => {
const errMessage = "UNHANDLED_ERR";
(getSpeakersFromSupabase as jest.Mock).mockRejectedValueOnce(
new UnhandledError(errMessage)
);
const response = await speakersGet();
const jsonResponse = await response!.json();

expect(jsonResponse).toEqual({
status: 500,
message: 'Internal server error',
});
});

it("should return status 401 if the user is unauthenticated", async () => {
const errMessage = "AUTH_DENIED";
(getSpeakersFromSupabase as jest.Mock).mockRejectedValueOnce(
new NotAuthenticatedError(errMessage)
);
const response = await speakersGet();
const jsonResponse = await response!.json();
expect(jsonResponse).toEqual({
status: 401,
message: errMessage,
});
});
});
19 changes: 9 additions & 10 deletions app/api/speakers/route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { NextResponse } from "next/server";
import { getSpeakersFromSupabase } from "@/utils/controller";
import {
AccessDeniedError,
SupabaseError,
UnhandledError,
NotAuthenticatedError,
} from "@/errors/databaseerror";
import { getSpeakersFromSupabase } from "@/controller/controller";
import { NextResponse } from "next/server";

export async function GET() {
try {
const data = await getSpeakersFromSupabase();
const speakers = await getSpeakersFromSupabase();

return NextResponse.json({
message: "retrieved data successfully",
data: data,
status: 200,
speakers: speakers,
});
} catch (err) {
if (err instanceof AccessDeniedError) {
Expand All @@ -27,16 +26,16 @@ export async function GET() {
status: 503,
message: err.message,
});
} else if (err instanceof UnhandledError) {
return NextResponse.json({
status: 500,
message: err.message,
});
} else if (err instanceof NotAuthenticatedError) {
return NextResponse.json({
status: 401,
message: err.message,
});
} else {
return NextResponse.json({
status: 500,
message: "Internal server error",
});
}
}
}
Loading
Loading