Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
1bbdfef
feat: initialize backend and PostgreSQL connection
Oct 13, 2026
b161026
feat: add listings database migration
Oct 13, 2026
2870e43
feat: add listing seed and backend documentation
Oct 13, 2026
686162c
docs: finalize backend setup and fix formatting
Oct 13, 2026
cebc384
Merge pull request #32 from IT2810-H26/31-feat/backend-postgresql
birkjr Oct 13, 2026
8fac1be
docs: document PostgreSQL and backend VM setup
Oct 13, 2026
ddd69d1
config: prepare backend systemd service for VM
Oct 13, 2026
66505c8
config: prepare Apache deployment and local development docs
Oct 13, 2026
5a1b16f
docs: AI usage in this PR
Oct 13, 2026
7bab8d7
Merge pull request #34 from IT2810-H26/33-config/postgresql-vm
tordtst Oct 13, 2026
51bfad1
feat: set up Apollo GraphQL server
Oct 13, 2026
97634ff
feat: add GraphQL queries for listings
Oct 13, 2026
8eb986c
feat: add createListing mutation and validation
Oct 13, 2026
3b773a5
docs: finalize GraphQL API and testing documentation
Oct 13, 2026
1590d1e
fix: lint and package update for prettier
Oct 13, 2026
56e2392
Merge pull request #36 from IT2810-H26/35-feat-implementer-graphql-ap…
tordtst Oct 13, 2026
6cfee6c
docs(deploy): dokumenter verifisert GraphQL-systemd på VM
Oct 13, 2026
5d0733d
Merge pull request #38 from IT2810-H26/37-config/graphql-backend-vm-m…
andretg Oct 14, 2026
7e56204
feat(backend): hent verktøymodeller fra Wikidata
andretg Oct 14, 2026
a36f9ad
refactor(backend): fjern Wikidata-beskrivelser fra verktøymodeller
andretg Oct 14, 2026
9b2870b
feat(backend): legg til opprettelsesdato for annonser
andretg Oct 14, 2026
fef5ab3
feat(backend): lag generator for varierte annonser
andretg Oct 14, 2026
67f125f
feat(backend): legg til seed av stort datasett
andretg Oct 14, 2026
56fd8ba
docs(docs): dokumenter datasett og skalering
andretg Oct 14, 2026
1890403
docs(docs): dokumenter KI-bruk for stort datasett
andretg Oct 14, 2026
4c8f34c
refactor(backend): bruk «seed-verdi» i stedet for «frø»
andretg Oct 14, 2026
5f40123
Merge pull request #40 from IT2810-H26/39-feat-generer-stort-og-varie…
tordtst Oct 14, 2026
8b87e5c
feat(backend): legg til createdAt i Listing-typen
Oct 14, 2026
bc4048b
feat(backend): valider sidestørrelse og posisjon for listings
Oct 14, 2026
8f95eca
feat(backend): legg til paginering i listings-spørringen
Oct 14, 2026
c99b286
feat(backend): legg til indeks for sortering på nyeste
Oct 14, 2026
bfdf6ab
test(backend): test paginering i listings-spørringen
Oct 14, 2026
c1387e9
docs(docs): dokumenter paginering i listings og måling av indeks
Oct 14, 2026
ac0cc5b
Merge pull request #46 from IT2810-H26/42-feat-legg-til-paginering-i-…
sebasti Oct 14, 2026
6d27cd4
build(config): bruk /project2/ som base og proxy API-et lokalt
Oct 14, 2026
6b6b59b
refactor(src): flytt annonsebildene til public
Oct 14, 2026
22f52ed
build(config): sett opp Apollo Client mot GraphQL-backenden
Oct 14, 2026
95a84d9
feat(src): lag komponent for å bla mellom sider
Oct 14, 2026
3c2893d
feat(pages): hent katalogen fra GraphQL med én side om gangen
Oct 14, 2026
2327512
feat(pages): hent detaljvisningen fra GraphQL
Oct 14, 2026
720bebe
docs(docs): dokumenter Apollo Client og kjøring av frontend mot backend
Oct 14, 2026
739e9fc
docs(deploy): beskriv innlasting av stort datasett på VM
andretg Oct 15, 2026
10a0f01
docs(deploy): oppdater VM-status med stort datasett
andretg Oct 15, 2026
8e7e421
Merge pull request #47 from IT2810-H26/43-feat-koble-katalog-og-detal…
andretg Oct 15, 2026
fcb3917
Merge pull request #48 from IT2810-H26/41-config-datasett-i-databasen…
tordtst Oct 15, 2026
aca5d61
feat(src): legg til mutasjon for å opprette annonse
andretg Oct 16, 2026
908b8a0
feat(src): send utleieskjemaet til backenden
andretg Oct 16, 2026
218d6b1
test(src): test innsending av utleieskjema
andretg Oct 16, 2026
52fd488
docs(docs): dokumenter lagring av nye annonser
andretg Oct 16, 2026
014ae59
Merge pull request #49 from IT2810-H26/44-feat-lagre-nye-annonser-med…
tordtst Oct 16, 2026
1e9734a
docs(docs): beskriv innlevering 2 og hvor appen kan prøves
tordtst Oct 16, 2026
e9aec8f
docs(docs): rett utdaterte avsnitt om mockdata og plan
tordtst Oct 16, 2026
14c41bb
docs(docs): legg til spørsmål om oppsett og samspill med backend
tordtst Oct 16, 2026
9dfa1d1
Merge pull request #51 from IT2810-H26/50-docs-oppdater-readme-for-in…
birkjr Oct 16, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ npm-debug.log*

# Lokale miljøvariabler
.env
.env.test
backend/.env.test
.env.*.local
.env.local

Expand Down
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ node_modules
dist
coverage
package-lock.json

# Skrevet av npm run import:tools. Formatet bestemmes av skriptet, slik at en
# ny import ikke gir formateringsfeil.
backend/src/seed/data
367 changes: 333 additions & 34 deletions README.md

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# PostgreSQL-tilkobling. Kopier til .env og tilpass verdien.
DATABASE_URL=postgresql://bruker:passord@localhost:5432/verktoyutlan

# GraphQL-server (npm run dev / npm run start)
HOST=127.0.0.1
PORT=3001

# Valgfritt: rotsti når frontend er deployet (standard /project2).
# PUBLIC_APP_BASE=/project2
10 changes: 10 additions & 0 deletions backend/.env.test.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Kopier til .env.test (committes ikke). Kun testdatabase — ikke dev eller prod.
# Databasenavnet må slutte på _test (sjekkes av vitest.config.ts).
#
# Én gang per maskin:
# createdb verktoyutlan_test
# set -a && source .env.test && set +a && export DATABASE_URL="$TEST_DATABASE_URL"
# npm run migrate && npm run seed
#
# Kjøre tester: cd backend && npm test
TEST_DATABASE_URL=postgresql://bruker@localhost:5432/verktoyutlan_test
4 changes: 4 additions & 0 deletions backend/.node-pg-migraterc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"migrations-dir": "migrations",
"migration-file-language": "ts"
}
161 changes: 161 additions & 0 deletions backend/deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Deploy: systemd og port 3001 (prosjekt 2)

Mal for å kjøre GraphQL-backenden som egen systemd-tjeneste på
`it2810-36.idi.ntnu.no`. Steg-for-steg på VM: [docs/vm-backend.md §7](../../docs/vm-backend.md#7-graphql-med-systemd-port-3001).

**Verifisert på VM (oktober 2026):** tjenesten kjører som `User=birkjr`,
`Group=eall_s`, med Node
`/home/birkjr/.nvm/versions/node/v24.21.0/bin/node`,
`WorkingDirectory=/home/birkjr/T36-Project-2/backend`, miljø fra
`/etc/t36-project2/backend.env`. GraphQL svarer på `127.0.0.1:3001/graphql`
(`health`, `listings`, `createListing`); `systemctl restart` og `enabled` er
testet. VM-reboot er ikke testet.

## Avhengighet før enable

Før `systemctl enable --now` må repoet ha minst:

- `backend/src/server.ts` som **leser `HOST` og `PORT` fra miljø** og binder til
**`127.0.0.1:3001`** (verdier fra `/etc/t36-project2/backend.env` — ikke
hardkode `0.0.0.0`).
- `npm ci` kjørt i `backend/`, slik at `node_modules/tsx/dist/cli.mjs` finnes.
- `npm run start` (eller `dev`) verifisert mot VM-database etter deploy av kode.
- Valgfritt senere: `npm run build` + `ExecStart` mot `dist/server.js` i stedet
for tsx.

## Filer

| Fil | Formål |
| ------------------------------ | -------------------------------------------------------------------- |
| `t36-project2-backend.service` | systemd-enhet (`__DEPLOY_USER__`, `__BACKEND_DIR__`, `__NODE_BIN__`) |
| `backend.env.example` | Mal for `/etc/t36-project2/backend.env` |

Migrasjon/seed kan fortsatt bruke `~/T36-Project-2/backend/.env`. Tjenesten leser
**kun** `/etc/t36-project2/backend.env` (utenfor git).

## Plassholdere

| Plassholder | Betydning |
| ----------------- | ---------------------------------------------------------------------------------- |
| `__DEPLOY_USER__` | NTNU-bruker på VM (ikke root) |
| `__BACKEND_DIR__` | Absolutt sti, f.eks. `/home/<NTNU_BRUKERNAVN>/T36-Project-2/backend` |
| `__NODE_BIN__` | Full sti til Node 24, f.eks. `/home/<bruker>/.nvm/versions/node/v24.21.0/bin/node` |

Etter `sed` fra malen: på IT2810-VM kan **`Group=`** settes til primærgruppen
(f.eks. `eall_s`) i stedet for brukernavn, hvis `systemd` krever det. Rediger
`/etc/systemd/system/t36-project2-backend.service` og kjør `daemon-reload`.

## Forberedelse på VM (etter GraphQL er merget)

Erstatt `<NTNU_BRUKERNAVN>` med faktisk bruker (samme som eier repoet).

```bash
export DEPLOY_USER="<NTNU_BRUKERNAVN>"
export BACKEND_DIR="/home/${DEPLOY_USER}/T36-Project-2/backend"

cd "${BACKEND_DIR}/.."
git pull
cd "${BACKEND_DIR}"
nvm use 24
npm ci
npm run typecheck
test -f node_modules/tsx/dist/cli.mjs
# npm run build — hvis dere bytter ExecStart til dist/server.js
```

Noter Node-sti:

```bash
export NODE_BIN="$(which node)"
echo "${NODE_BIN}"
```

## Miljøfil utenfor git

Tjenesten kjører som **`DEPLOY_USER`** ( ikke root ). Filen skal være lesbar for
prosessen, men ikke for «alle» på VM:

```bash
export DEPLOY_USER="<NTNU_BRUKERNAVN>"

sudo mkdir -p /etc/t36-project2
sudo cp "/home/${DEPLOY_USER}/T36-Project-2/backend/deploy/backend.env.example" \
/etc/t36-project2/backend.env
sudo nano /etc/t36-project2/backend.env # DATABASE_URL, HOST, PORT
sudo chmod 640 /etc/t36-project2/backend.env
sudo chown "root:${DEPLOY_USER}" /etc/t36-project2/backend.env
```

**Krav i filen:** `HOST=127.0.0.1`, `PORT=3001`. GraphQL-implementasjonen må respektere
disse.

## Installere tjenesten (manuelt, én gang)

Kjør på VM etter `nvm use 24` og `npm ci` i backend:

```bash
export DEPLOY_USER="<NTNU_BRUKERNAVN>"
export BACKEND_DIR="/home/${DEPLOY_USER}/T36-Project-2/backend"
export NODE_BIN="$(which node)"

sed -e "s|__DEPLOY_USER__|${DEPLOY_USER}|g" \
-e "s|__BACKEND_DIR__|${BACKEND_DIR}|g" \
-e "s|__NODE_BIN__|${NODE_BIN}|g" \
"${BACKEND_DIR}/deploy/t36-project2-backend.service" \
| sudo tee /etc/systemd/system/t36-project2-backend.service > /dev/null

sudo systemctl daemon-reload
```

Test manuelt før `enable` (miljø fra `/etc/t36-project2/backend.env`):

```bash
set -a && source /etc/t36-project2/backend.env && set +a
"${NODE_BIN}" "${BACKEND_DIR}/node_modules/tsx/dist/cli.mjs" "${BACKEND_DIR}/src/server.ts"
```

Deretter:

```bash
sudo systemctl enable t36-project2-backend
sudo systemctl start t36-project2-backend
```

## Kontroll

```bash
sudo systemctl status t36-project2-backend --no-pager
sudo systemctl is-enabled t36-project2-backend
journalctl -u t36-project2-backend -n 50 --no-pager
ss -tlnp | grep 3001 || true

curl -sS -X POST http://127.0.0.1:3001/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ health listings(limit: 1) { totalCount } }"}'
```

Stopp / restart:

```bash
sudo systemctl stop t36-project2-backend
sudo systemctl restart t36-project2-backend
```

Ved OS-omstart: autostart når enheten er **enabled** (`Restart=on-failure` ved krasj).

## Prosjekt 1

- Enheten heter `t36-project2-backend` og bruker port **3001** — ikke endre
prosjekt 1s tjenester eller Apache-vhosts.

## Oppdatere etter pull

```bash
export BACKEND_DIR="/home/<NTNU_BRUKERNAVN>/T36-Project-2/backend"
cd "${BACKEND_DIR}" && npm ci
sudo systemctl restart t36-project2-backend
```

Ved bytt til produksjonsbuild, oppdater `ExecStart` i
`/etc/systemd/system/t36-project2-backend.service` (f.eks. `node dist/server.js`),
deretter `daemon-reload` og `restart`.
21 changes: 21 additions & 0 deletions backend/deploy/backend.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Eksempel for systemd-tjenesten på VM.
# Kopier til /etc/t36-project2/backend.env (aldri commit med passord).
#
# sudo mkdir -p /etc/t36-project2
# sudo cp backend/deploy/backend.env.example /etc/t36-project2/backend.env
# sudo chmod 640 /etc/t36-project2/backend.env
# sudo chown root:__DEPLOY_USER__ /etc/t36-project2/backend.env
#
# Tjenesten kjører som __DEPLOY_USER__ (ikke root). Med 640 og gruppe =
# deploy-brukeren kan prosessen lese filen; andre brukere på VM kan ikke.

DATABASE_URL=postgresql://t36_project2:PASSORD@localhost:5432/t36_project2

# GraphQL-server (src/server.ts): les HOST og PORT, lytt kun på 127.0.0.1 — ikke
# 0.0.0.0. Apache proxyer mot http://127.0.0.1:3001/graphql (se deploy/apache/).
HOST=127.0.0.1
PORT=3001
NODE_ENV=production

# Valgfritt: offentlig rot for seed-bilder
# PUBLIC_APP_BASE=/project2
33 changes: 33 additions & 0 deletions backend/deploy/t36-project2-backend.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Mal for gruppens Ubuntu-VM. Ikke enable før npm ci, backend.env og GraphQL er
# verifisert lokalt (src/server.ts leser HOST/PORT, default 127.0.0.1:3001).
#
# Installasjon: se backend/deploy/README.md (erstatt __DEPLOY_USER__, __BACKEND_DIR__,
# __NODE_BIN__).

[Unit]
Description=T36 Project 2 backend (GraphQL, port 3001)
Documentation=file://__BACKEND_DIR__/deploy/README.md
After=network-online.target postgresql.service
Wants=network-online.target
# Prosjekt 2 egen enhet — ikke delt med prosjekt 1

[Service]
Type=simple
User=__DEPLOY_USER__
Group=__DEPLOY_USER__
WorkingDirectory=__BACKEND_DIR__

# Hemmeligheter utenfor git (DATABASE_URL, HOST, PORT, osv.)
EnvironmentFile=/etc/t36-project2/backend.env

# Node 24 (nvm) + tsx fra backend/node_modules etter «npm ci» — ikke global tsx
ExecStart=__NODE_BIN__ __BACKEND_DIR__/node_modules/tsx/dist/cli.mjs __BACKEND_DIR__/src/server.ts

Restart=on-failure
RestartSec=5
TimeoutStopSec=30

NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
48 changes: 48 additions & 0 deletions backend/migrations/1791895214550_create-listings-table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { ColumnDefinitions, MigrationBuilder } from 'node-pg-migrate';

export const shorthands: ColumnDefinitions | undefined = undefined;

const LISTING_CATEGORIES = [
'powerTools',
'handTools',
'garden',
'painting',
'measuring',
'cleaning',
] as const;

const LISTING_CONDITIONS = ['likeNew', 'good', 'worn'] as const;

function sqlInList(values: readonly string[]): string {
return values.map((value) => `'${value}'`).join(', ');
}

export async function up(pgm: MigrationBuilder): Promise<void> {
pgm.createTable('listings', {
id: { type: 'text', primaryKey: true },
title: { type: 'text', notNull: true },
category: { type: 'text', notNull: true },
description: { type: 'text', notNull: true },
area: { type: 'text', notNull: true },
daily_price: { type: 'integer', notNull: true },
condition: { type: 'text', notNull: true },
accessories: { type: 'text[]', notNull: true, default: '{}' },
image_url: { type: 'text', notNull: false },
});

pgm.addConstraint('listings', 'listings_category_check', {
check: `category IN (${sqlInList(LISTING_CATEGORIES)})`,
});

pgm.addConstraint('listings', 'listings_condition_check', {
check: `condition IN (${sqlInList(LISTING_CONDITIONS)})`,
});

pgm.addConstraint('listings', 'listings_daily_price_check', {
check: 'daily_price >= 0',
});
}

export async function down(pgm: MigrationBuilder): Promise<void> {
pgm.dropTable('listings');
}
24 changes: 24 additions & 0 deletions backend/migrations/1791978982612_add-listings-created-at.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { ColumnDefinitions, MigrationBuilder } from 'node-pg-migrate';

export const shorthands: ColumnDefinitions | undefined = undefined;

/**
* Legger til når annonsen ble opprettet. Trengs for å sortere på «nyeste» og
* for å spre genererte annonser utover tid.
*
* Eksisterende rader får tidspunktet migrasjonen kjøres. Nye rader får
* tidspunktet de settes inn, med mindre seed-skriptet oppgir et eget.
*/
export async function up(pgm: MigrationBuilder): Promise<void> {
pgm.addColumn('listings', {
created_at: {
type: 'timestamptz',
notNull: true,
default: pgm.func('current_timestamp'),
},
});
}

export async function down(pgm: MigrationBuilder): Promise<void> {
pgm.dropColumn('listings', 'created_at');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { ColumnDefinitions, MigrationBuilder } from 'node-pg-migrate';

export const shorthands: ColumnDefinitions | undefined = undefined;

/**
* Indeks for standardsorteringen i listings: nyeste først, med id som andre
* nøkkel. Indeksen har samme rekkefølge som ORDER BY created_at DESC, id DESC,
* så PostgreSQL kan lese den første siden rett fra indeksen i stedet for å
* sortere hele tabellen for hver forespørsel.
*/
export async function up(pgm: MigrationBuilder): Promise<void> {
pgm.createIndex(
'listings',
[
{ name: 'created_at', sort: 'DESC' },
{ name: 'id', sort: 'DESC' },
],
{ name: 'listings_created_at_id_idx' },
);
}

export async function down(pgm: MigrationBuilder): Promise<void> {
pgm.dropIndex('listings', [], { name: 'listings_created_at_id_idx' });
}
Loading