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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,7 @@ short, it was used for:
Where AI contributed directly to a piece of code, there is a comment saying
so, for example in [frontend/app/api/types/index.ts](frontend/app/api/types/index.ts) and
[frontend/app/root.tsx](frontend/app/root.tsx).

## Deployment

For more details on deploying the solution on a VM, see the [deployment documentation](/docs/deployment.md).
167 changes: 167 additions & 0 deletions docs/deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Deployment

## Install dependencies

Start by installing our dependencies:

- Install [node 24](https://nodejs.org/en/download)
- Install [pnpm](https://pnpm.io/installation)
- Install [PostgreSQL 18](https://www.postgresql.org/download/)
- Install [pm2](https://pm2.keymetrics.io/)

The binaries provided by PostgreSQL may not be added to path automatically.
If the commands `pg_ctl` and `initdb` fail, do the following:

```sh
cd ~
touch .profile
nano .profile
# And add the following line: PATH=$PATH:/usr/lib/postgresql/18/bin/

# Apply the changes
source .profile
```

## Clone the project and set it up

Clone the project:

```sh
git clone https://git.ntnu.no/IT2810-H26/T37-Project-2.git
cd T37-Project-2
```

### Environment variables

This step needs to be done only whenever environment variables are updated (`.env`).

Set the correct environment values for the frontend:

```sh
cd frontend
cp .env.example .env
nano .env # and change the VITE_API_URL
```

And do the same for the backend:

```sh
cd backend
cp .env.example .env
nano .env # and change variables if needed
```

### Build the frontend

Build the frontend, remove the old version and move the new content:

```sh
cd frontend
pnpm build
sudo rm -rf /var/www/html/project2
sudo cp -r build/client/ /var/www/html/project2
```

> [!CAUTION]
> We only move the `client/` folder from the build!
> We do not use React Router's server-side functionality, because our frontend does not use server-side rendering (SSR).
> See [react-router.config.ts](./frontend/react-router.config.ts)
The move must be executed as sudo because the `/var` folders are owned by `root`.

### Building the backend

Build the backend like so:

```sh
cd backend
pnpm build
```

### Prepare the database

We need the seed book data file.
For more details, please check the [backend README.md](./backend/README.md).

Let's download the dataset, unzip and place it in the correct folder:

```sh
curl -L -o comprehensive-literary-greats-dataset.zip https://www.kaggle.com/api/v1/datasets/download/thedevastator/comprehensive-literary-greats-dataset
unzip comprehensive-literary-greats-dataset.zip
# After unzipping, a file named "books_1.Best_Books_Ever.csv" will be placed
# in the current working directory.

mv books_1.Best_Books_Ever.csv ./backend/src/db/seed/book-data.csv
rm comprehensive-literary-greats-dataset.zip
```

Set up the database as usual:

```sh
cd backend
pnpm db:setup
```

> [!NOTE]
> If you receive permission errors, add your user to the `postgres` group by running the following command:
> `sudo usermod -aG postgres $USER`
## Running the project

The frontend is already handling by the Apache web server by being placed in `/var/www/html/project2`.

In order to run the backend, we use `pm2`.

The first time project is run:

```sh
cd backend
pm2 start --name "backend" --interpreter="/bin/bash" pnpm -- start
```

Afterwards, after changing the backend and building it, it's enough to restart it:

```sh
# This command can be done in any directory on the machine!
pm2 restart backend
```

## Our VM setup

This is what is set up on our VM (`it2810-37.idi.ntnu.no`, Ubuntu 26.04).
Apache was already installed and running.

### Installed packages

```sh
# Node 24 from NodeSource
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -
sudo apt install -y nodejs

# pnpm, installed globally
sudo npm install -g pnpm@12

# PostgreSQL 18
sudo apt install -y postgresql
```

### Apache config for client-side routes

React Router handles routes like `/project2/book/5` in the browser. Without
this config, opening such a URL directly or reloading the page gives a 404,
because Apache looks for a file with that path. `FallbackResource` makes
Apache serve `index.html` instead, so the app can load and show the right page.
It only applies to the `project2` folder.

```sh
sudo tee /etc/apache2/conf-available/project2.conf > /dev/null <<'CONF'
<Directory /var/www/html/project2>
FallbackResource /project2/index.html
</Directory>
CONF
sudo a2enconf project2
sudo apache2ctl configtest # should print "Syntax OK"
sudo systemctl reload apache2
```

This was only done once, and should only need to be run once.
2 changes: 2 additions & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Must be changed to http://it2810-37.idi.ntnu.no:3001/ for production
VITE_API_URL = http://localhost:3001
15 changes: 10 additions & 5 deletions frontend/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import { TooltipProvider } from "~/components/ui/Tooltip";

// Created outside the component tree so the cache survives re-renders.
const apolloClient = new ApolloClient({
link: new HttpLink({ uri: "http://localhost:3001/" }),
link: new HttpLink({
uri: (import.meta.env.VITE_API_URL as string) || "http://localhost:3001/",
}),
cache: new InMemoryCache(),
});

Expand All @@ -36,21 +38,24 @@ export function Layout({ children }: { children: React.ReactNode }) {
<link
rel="apple-touch-icon"
sizes="180x180"
href="/favicon/apple-touch-icon.png"
href={`${import.meta.env.BASE_URL}favicon/apple-touch-icon.png`}
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon/favicon-32x32.png"
href={`${import.meta.env.BASE_URL}favicon/favicon-32x32.png`}
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon/favicon-16x16.png"
href={`${import.meta.env.BASE_URL}favicon/favicon-16x16.png`}
/>
<link
rel="manifest"
href={`${import.meta.env.BASE_URL}favicon/site.webmanifest`}
/>
<link rel="manifest" href="/favicon/site.webmanifest" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
Expand Down
8 changes: 4 additions & 4 deletions frontend/public/favicon/site.webmanifest
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "",
"short_name": "",
"name": "Readr",
"short_name": "Readr",
"icons": [
{
"src": "/android-chrome-192x192.png",
"src": "android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"src": "android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
Expand Down