From b17c7fad1aac0a3b21bf07e29e14a5cc0a95a451 Mon Sep 17 00:00:00 2001 From: Saikhan Date: Fri, 16 Oct 2026 22:01:19 +0200 Subject: [PATCH] =?UTF-8?q?N=C3=A5=20har=20vi=20en=20samlet=20README=20med?= =?UTF-8?q?=20utfyllende=20informasjon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 421 +++++++++++++++++++++++++++++++++++++---------- server/README.md | 77 --------- 2 files changed, 331 insertions(+), 167 deletions(-) delete mode 100644 server/README.md diff --git a/README.md b/README.md index 2a2705a..813c679 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ -# Country Boys – Prosjekt 2 +# Country Boys -Repository for team IT2810-H26-T20 in IT2810-H26. -Link to project: http://it2810-20.idi.ntnu.no/project2/ +Prosjekt 2 i IT2810 (høsten 2026), gruppe 20. -> En søkbar katalog over byer med filtre, sortering, anmeldelser og lokale bokmerker. +Nettsiden ligger på http://it2810-20.idi.ntnu.no/project2/ + +**Merk:** VM-en kan bare nås fra NTNU-nettet eller via NTNU-VPN. Utenfor det nettet lastes ikke siden, og det er forventet. + +Dataene kommer fra World Cities-datasettet på Kaggle (https://www.kaggle.com/datasets/marcobics/world-cities-database). + +> Country Boys en søkbar katalog over byer i verden. Man kan søke på navn, filtrere på land og innbyggertall, sortere, lese anmeldelser og skrive egne. > Utviklet i emnet IT2810 Webutvikling ved NTNU. @@ -18,85 +23,67 @@ Link to project: http://it2810-20.idi.ntnu.no/project2/ ## Innhold -- [Om prosjektet](#om-prosjektet) -- [Teknologistack](#teknologistack) -- [Funksjonalitet](#funksjonalitet) -- [Kom i gang](#kom-i-gang) -- [Database og API](#database-og-api) +- [Hvordan det henger sammen](#hvordan-det-henger-sammen) +- [Teknologi og valg](#teknologi-og-valg) +- [Hva som fungerer og hva som mangler](#hva-som-fungerer-og-hva-som-mangler) +- [Kjøre frontend lokalt](#kjøre-frontend-lokalt) +- [Backend og database](#backend-og-database) +- [Sette opp på VM-en](#sette-opp-på-vm-en) - [Arbeidsflyt](#arbeidsflyt) -- [Dokumentasjon](#dokumentasjon) --- -## Om prosjektet - -Country Boys lar brukeren utforske bydata fra gruppas GraphQL-backend. Frontend-en henter data via Apollo Server, som håndterer kommunikasjonen med MongoDB på VM-en. Nettleseren kobler seg aldri direkte til databasen. +## Hvordan det henger sammen ```text -React + TypeScript ──(GraphQL over HTTP)──▶ Apollo Server på VM ──▶ MongoDB på VM - ▲ │ - └──────────── JSON-respons ───────────────┘ +Nettleser (React) --> Apache (port 80) --> Apollo Server (:3001) --> MongoDB (:27017) ``` -Under lokal utvikling går frontend-kall til `/api/graphql`. Vite videresender denne ruten til Apollo-serveren på `http://it2810-20.idi.ntnu.no:3001/`. Dette unngår at nettleseren trenger direkte CORS-tilgang til backend mens man utvikler. +Apache serverer de ferdigbygde frontend-filene under `/project2/` og sender kall til `/api/graphql` videre til Apollo Server på samme VM. Backend er den eneste som snakker med databasen. -## Teknologistack +Søk, filtrering, sortering og paginering gjøres i databasen. Klienten henter 12 byer om gangen, men filtre og sortering gjelder hele datasettet og ikke bare det som er lastet inn. -| Lag | Teknologi | Rolle | -|---|---|---| -| **Frontend** | React 19 + TypeScript | Brukergrensesnitt og klienttilstand | -| | Vite 8 | Utviklingsserver, proxy og bygg | -| | Fetch API | Sender GraphQL-operasjoner som HTTP POST; Apollo Client er ikke installert | -| **Backend** | Apollo Server | GraphQL-API på port `3001` | -| **Database** | MongoDB | Lagring på VM, nås av backend | -| **Kvalitet** | TypeScript | Typekontroll ved produksjonsbygg | -| | ESLint | Linting | +Under `pnpm dev` har Vite en proxy som sender `/api/graphql` til backend på VM-en (se `vite.config.ts`). I produksjon er det Apache som gjør den jobben. -> **Viktig prinsipp:** Søk, filtrering, sortering og paginering sendes til GraphQL-backend. Frontend-en henter bare siden med byer den skal vise. +## Teknologi og valg -## Funksjonalitet +- **Frontend** React 19, TypeScript og Vite. GraphQL-kallene gjøres med `fetch` i `src/graphql.ts`. Apollo Client er ikke installert. +- **Backend** Node, TypeScript og Apollo Server 5, i mappen `server/`. +- **Database** MongoDB, installert direkte på VM-en uten Docker, Vi valgte MongoDB fordi vi kjente den fra før og fordi det fantes en oppskrift fra forelesningen som var lett å følge. +- **Kvalitet** ESLint i frontend, Prettier og `tsc` i backend. Vi har ikke tester ennå. -### Søk og navigering -- [x] Søker på bynavn med case-insensitiv delstrengsmatching via `searchCities`. -- [x] Henter 12 byer om gangen og kan laste inn flere med `offset`-paginering. -- [x] Viser totalt antall treff og om det finnes flere resultater. +> **Viktig prinsipp:** Søk, filtrering, sortering og paginering sendes til GraphQL-backend. Frontend-en henter bare siden med byer den skal vise. -### Filtrering og sortering -- [x] Filtrerer på land. -- [x] Filtrerer på befolkningsintervall. -- [x] Sorterer alfabetisk, etter land eller etter befolkning. -- [x] Filter- og søkevalg gjenopprettes ikke etter omlasting av siden. +## Hva som fungerer og hva som mangler -### Anmeldelser og bokmerker -- [x] Viser byens gjennomsnittsvurdering, antall anmeldelser og eksisterende anmeldelser. -- [x] Sender nye anmeldelser til backend med `addReview`-mutasjonen. -- [x] Lagrer bokmerker i nettleserens `localStorage`. -- [x] Bokmerker er ikke knyttet til en bruker og synkroniseres ikke mellom enheter. -- [x] API-et eksponerer ikke innlogging eller brukeradministrasjon. +Fungerer nå: -### Tilgjengelighet og feiltilstander -- [x] Viser lasting, tomt resultat og API-feil med mulighet for å prøve på nytt. -- [x] Søk, knapper og skjemaelementer har tilgjengelige etiketter og tastaturfokus. +- Søk på bynavn med (ikke case-insensitiv, treffer også midt i navnet). Søket sendes når man trykker på søkeknappen eller Enter, så det går ikke ett kall per tastetrykk. +- Filter på land og befolkningsintervall, og sortering på navn, land og befolkning. +- «Show more cities» laster neste 12 byer, og totalt antall treff vises. +- Anmeldelser: gjennomsnitt og eksisterende anmeldelser vises for hver by, og man kan skrive en ny (1–5 og valgfri kommentar). De lagres i MongoDB og er felles for alle brukere. +- Bokmerker for byer, lagret i nettleseren. +- Lasting, tomt resultat og feilmelding med «prøv igjen». Skjemafelt og knapper har etiketter og kan brukes med tastatur. -## Kom i gang +Mangler: -### Forutsetninger -- Node.js 24 eller nyere. -- pnpm. -- Tilgang til NTNU-nettet eller NTNU-VPN for å nå backend-VM-en. +- Innlogging. Anmeldelser bruker det navnet man skriver inn, og det er ikke verifisert. +- Bokmerkene ligger i `localStorage`, så de følger ikke brukeren mellom enheter. +- Søk og filtre huskes ikke når siden lastes på nytt. +- Kart, filter på hovedstad og høyde over havet fra den opprinnelige planen. +- State management-bibliotek (vi bruker `useState`) og tester (Vitest og ende-til-ende). -### Installasjon og utvikling +## Kjøre frontend lokalt -Kjør kommandoene fra prosjektmappen: +Krever Node 24 og pnpm. Du må være på NTNU-nettet eller VPN siden utviklingsserveren bruker backend på VM-en. ```bash +cd t20-project-2 pnpm install pnpm dev ``` -Vite skriver ut lokal URL når serveren starter, vanligvis `http://localhost:5173/`. - -### Scripts +Vite skriver ut adressen, vanligvis http://localhost:5173/project2/. Andre kommandoer: `pnpm build` (typesjekk og produksjonsbygg), `pnpm lint` og `pnpm preview`. | Kommando | Beskrivelse | |---|---| @@ -105,64 +92,318 @@ Vite skriver ut lokal URL når serveren starter, vanligvis `http://localhost:517 | `pnpm lint` | Kjører ESLint | | `pnpm preview` | Forhåndsviser produksjonsbygget lokalt | -Det er ikke konfigurert et eget testscript i `package.json` ennå. +## Backend og database -## Database og API +### Datamodellen -MongoDB kjører på VM-en og skal bare nås av Apollo-backend. Frontend-en bruker GraphQL-endepunktet: +Byene ligger i collection `byer` i databasen `cities`, ett dokument per by: -```text -http://it2810-20.idi.ntnu.no:3001/ +```json +{ "by": "Shanghai", "land": "China", "iso2": "CN", "region": 23, + "breddegrad": 31.22222, "lengdegrad": 121.45806, + "befolkning": 24874500, "tidssone": "Asia/Shanghai", "hovedstad": false } ``` -I utviklingsmodus er Vite-proxyen definert i `vite.config.ts`. Klienten i `src/graphql.ts` bruker `/api/graphql` som standard. En alternativ offentlig GraphQL-adresse kan settes ved bygging med `VITE_GRAPHQL_URL`. +`befolkning`, `breddegrad` og `lengdegrad` må være tall og ikke tekst, ellers blir sortering og filtrering feil. `iso2`, `region` og `hovedstad` ligger i databasen, men API-et bruker dem ikke ennå (det kan bli et hovedstadsfilter senere). Hvis feltene heter noe annet kan man rette `COLLECTION` og `FIELD` i `server/src/db.ts`, som er den eneste filen som kjenner til navnene. + +Anmeldelser ligger i collection `reviews`, som opprettes når den første lagres: + +```json +{ "cityId": ObjectId, "rating": 4, "comment": "…", "author": "Anonym", "createdAt": Date } +``` -### GraphQL-operasjoner som frontend bruker +By-id er MongoDBs `_id`, og sendes som streng i GraphQL. -| Operasjon | Bruk | +### API-et + +| Operasjon | Hva den gjør | |---|---| -| `searchCities` | Søk, land-/befolkningsfilter, sortering og paginering. Backend setter en øvre grense på 100 treff per kall. | -| `countries` | Fyller landfilteret. | -| `addReview` | Lagrer en anmeldelse for en by. | +| `searchCities(search, filter, sort, limit, offset)` | Søk, filtrering (land, tidssone, min/maks befolkning), sortering og paginering. Gir `items`, `totalCount` og `hasMore`. `limit` er maks 100. | +| `city(id)` | Én by med anmeldelser. | +| `countries` | Alle land, brukes i landfilteret | +| `addReview(input)` | Lagrer rating og kommentar. | -Test for eksempel følgende spørring i Apollo Sandbox når du er på NTNU-nettet eller VPN: +Frontend bruker `searchCities`, `countries` og `addReview`. Et eksempel som kan limes inn i Apollo Sandbox på http://it2810-20.idi.ntnu.no:3001/ (krever NTNU-nett eller VPN): ```graphql query { - searchCities(limit: 3, offset: 0) { + searchCities( + search: "trond" + filter: { country: "Norway", minPopulation: 100000 } + sort: { field: POPULATION, direction: DESC } + limit: 20 + offset: 0 + ) { totalCount hasMore - items { - id - name - country - } + items { id name country population averageRating reviewCount } } } ``` -### Sikkerhet og produksjon +### Valg i backend -- MongoDB-adresse, brukernavn og passord skal aldri legges i frontend eller sendes til nettleseren. -- Apollo-endepunktet bruker HTTP i dagens VM-oppsett. Dersom frontend publiseres over HTTPS, må webserveren proxy `/api/graphql` til Apollo over backend-nettverket, eller Apollo må tilgjengeliggjøres over HTTPS. -- Hvis produksjonen kaller Apollo direkte fra et annet domene, må backend konfigurere CORS for frontend-domenet. -- API-et har ikke autentisering nå. Anmeldelser sendes med navnet brukeren skriver inn; navnet er ikke verifisert som en identitet. -- API-endepunktet kan kreve NTNU-nett eller VPN. En offentlig frontend blir ikke tilgjengelig for eksterne brukere før nettverkstilgangen til backend er avklart. +- Sorteringsfeltet velges fra en fast liste, og søketeksten escapes før den brukes i et regulært uttrykk. Klienten kan altså ikke sende inn vilkårlige feltnavn eller MongoDB-operatorer. +- Sorteringen bruker `_id` som siste kriterium, så sidene ikke endrer seg mellom to kall. +- Snitt og antall anmeldelser hentes med én aggregering for byene på den aktuelle siden, ikke ett kall per by. +- Indekser på befolkning, navn, land og anmeldelser lages med `npm run indexes`. +- Begrensning: søket er et regex-treff på delstreng, og det kan ikke bruke indeks fullt ut. For noen titusen byer er det raskt nok. Med mye større data bør man heller søke på starten av navnet i et felt med små bokstaver, eller bruke en tekstindeks. +- API-et har ingen autentisering, og trafikken går over vanlig HTTP. -## Arbeidsflyt +### Kjøre backend lokalt + +Du trenger tilgang til en MongoDB med dataene, for eksempel den på VM-en (fra NTNU-nett eller VPN). + +```bash +cd server +npm install +cp .env.example .env # fyll inn MONGODB_URI +npm run indexes +npm run dev +``` +Serveren starter på http://localhost:3001/. + +## Sette opp på VM-en + +Alt kjører på `it2810-20.idi.ntnu.no`, uten Docker. + +| Del | Adresse | +| --- | --- | +| Frontend (statiske filer fra Apache) | http://it2810-20.idi.ntnu.no/project2/ | +| GraphQL via Apache | http://it2810-20.idi.ntnu.no/api/graphql | +| Backend direkte (Apollo Sandbox) | http://it2810-20.idi.ntnu.no:3001/ | +| MongoDB | port 27017 | + +Under står alt som trengs for å få det til å kjøre fra scratch. Koble først til VM-en: `ssh @it2810-20.idi.ntnu.no`. Passord og andre hemmeligheter står ikke i git. Brukernavn og passord til databasen ligger i `server/.env` på VM-en, som kan leses med `sudo`. + +### 1. MongoDB + +Vi fulgte oppskriften «Sette opp database» fra forelesningen og kjører MongoDB 8.0.34. VM-en hadde v7 fra før, og vi oppgraderte til v8 etter MongoDBs egen installasjonsguide for Ubuntu 24.04 (noble): + +```bash +sudo apt-get install gnupg curl +curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \ + sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor +echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | \ + sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list +sudo apt-get update +sudo apt-get install -y mongodb-org +``` + +Deretter starter og sjekker vi databasen slik: + +```bash +sudo systemctl start mongod +sudo systemctl status mongod +sudo systemctl enable mongod # starter automatisk etter restart +mongod --version # db version v8.0.34 +``` + +For å kunne koble til fra våre egne maskiner under utvikling slo vi på innlogging og åpnet databasen for eksterne klienter. Maskinen er bare tilgjengelig fra NTNU-nettet eller VPN, så risikoen er liten, men i et ekte system ville vi latt databasen kun lytte lokalt. + +1. Lag en admin-bruker i `mongosh`: + + ```js + use admin + db.createUser({ + user: "admin", + pwd: "", + roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ] + }) + ``` + +2. Rediger `/etc/mongod.conf` med sudo, slik at disse to seksjonene ser slik ut (innrykk er viktig, feil her gjør at databasen ikke starter): + + ```yaml + net: + port: 27017 + bindIp: 0.0.0.0 + + security: + authorization: enabled + ``` + +3. Start på nytt og logg inn: + + ```bash + sudo systemctl restart mongod + mongosh --port 27017 --authenticationDatabase "admin" -u "admin" -p + ``` + +Fra en annen maskin legger man til `--host it2810-20.idi.ntnu.no`. Vi bruker også MongoDB Compass til å se på innholdet. + +### 2. Dataene + +Byene fra Kaggle-datasettet ligger i collection `byer` i databasen `cities`, med feltnavnene fra datamodellen over. Slik kan man sjekke innholdet: + +```bash +mongosh "mongodb://admin:@localhost:27017/cities?authSource=admin" \ + --eval 'db.byer.countDocuments(); db.byer.findOne()' +``` + +Kaggle-filen har andre kolonnenavn enn databasen, så den ble omdøpt og ryddet før den ble lagt inn, og `befolkning`, `breddegrad` og `lengdegrad` ble lagret som tall. + +Hvis databasen må bygges opp på nytt, må CSV-filen få kolonner som heter `by`, `land`, `breddegrad`, `lengdegrad`, `befolkning` og `tidssone` (og gjerne `iso2`, `region` og `hovedstad`), og tallkolonnene må importeres som tall og ikke tekst. Et mulig utgangspunkt, når overskriftene er `by,land,breddegrad.double(),lengdegrad.double(),befolkning.int32(),tidssone`, er: + +```bash +mongoimport --uri "mongodb://admin:@localhost:27017/?authSource=admin" \ + --db cities --collection byer --type csv --headerline --columnsHaveTypes --file byer.csv +``` -Gjør endringer i en egen branch og bruk pull request for gjennomgang. +Dette er ikke kommandoen vi brukte, og den er ikke testet. Den viser bare hvilken form dataene må ha. -Før pull request: +### 3. Backend + +Backend krever Node 22, som vi installerte med nvm på VM-en: ```bash -pnpm lint +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash +source ~/.bashrc +nvm install 22 +node -v +``` + +Hent koden og installer: + +```bash +git clone +cd T20-Project-2/server +npm install +cp .env.example .env +``` + +Fyll inn `.env`, som ligger i `server/` på VM-en og ikke i git: + +- `PORT`: standard 3001. +- `MONGODB_URI`: siden innlogging er slått på blir den `mongodb://admin:@localhost:27017/?authSource=admin`. +- `MONGODB_DB`: `cities`. + +Fagstaben kan lese filen med `sudo cat /server/.env`. Passordet står ikke i denne README-en. + +Lag indekser (trygt å kjøre flere ganger) og bygg: + +```bash +npm run indexes # indekser på befolkning, navn og land +npm run build # kompilerer TypeScript til dist/ +``` + +For rask testing under utvikling kan serveren startes med `npm run dev` (`tsx watch`). På VM-en kjører den kompilerte versjonen (`dist/index.js`) som en systemd-tjeneste, slik at den starter av seg selv etter en restart av VM-en og startes på nytt hvis den krasjer. Tjenesten heter `cities-backend`, med en fil som `/etc/systemd/system/cities-backend.service`: + +```ini +[Unit] +Description=Country Boys GraphQL backend +After=network.target mongod.service + +[Service] +Type=Simple +User= +WorkingDirectory=/home//T20-Project-2/server +EnvironmentFile=/server/.env +ExecStart=/home//.nvm/versions/node/v22.x.x/bin/node dist/index.js +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +``` + +Siden Node er installert med nvm må `ExecStart` bruke hele stien til nvm sin Node (`which node` viser den, for eksempel `/home//.nvm/versions/node/v22.x.x/bin/node`). `/usr/bin/node` finnes ikke. Aktiver og start: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now cities-backend +``` + +Se status og logg: + +```bash +sudo systemctl status cities-backend +sudo journalctl -u cities-backend -f +``` + +Test at den svarer. Hvis spørringen gir land, fungerer koblingen mellom backend og MongoDB: + +```bash +curl -s -X POST http://localhost:3001/ -H 'content-type: application/json' \ + -d '{"query":"{ countries }"}' +``` + +Oppdatere etter nye endringer: + +```bash +cd /server +git pull +npm install +npm run build +sudo systemctl restart cities-backend +``` + +### 4. Frontend + +`vite.config.ts` har `base: '/project2/'`. Det må være nøyaktig det, ellers peker de bygde filene feil og siden blir blank. Frontend bygges på vår egen maskin og kopieres til VM-en. + +På egen maskin: + +```bash +cd t20-project-2 pnpm build +scp -r ./dist @it2810-20.idi.ntnu.no:~/project2-dist +``` + +Hvis `scp` spør om vertsnøkkel, sjekk at adressen er riktig før du svarer yes. På Windows kjøres det samme i PowerShell, bare med `.\dist`. + +På VM-en: + +```bash +sudo mkdir -p /var/www/html/project2 +sudo cp -a ~/project2-dist/. /var/www/html/project2/ +sudo find /var/www/html/project2 -type d -exec chmod 755 {} \; +sudo find /var/www/html/project2 -type f -exec chmod 644 {} \; ``` -## Dokumentasjon +`index.html` og `assets/` skal ligge direkte i `/var/www/html/project2/`. + +### 5. Apache + +Åpne `sudo nano /etc/apache2/sites-available/000-default.conf` og legg dette inni ``: + +```apache +Alias /project2/ /var/www/html/project2/ + + + Require all granted + Options -Indexes + AllowOverride None + DirectoryIndex index.html + + +ProxyPass /api/graphql http://127.0.0.1:3001/ +ProxyPassReverse /api/graphql http://127.0.0.1:3001/ +``` + +Aktiver proxy-modulene, test konfigurasjonen og last Apache på nytt. Fortsett bare hvis `configtest` sier «Syntax OK»: + +```bash +sudo a2enmod proxy proxy_http +sudo apachectl configtest +sudo systemctl reload apache2 +``` + +Proxyen gjør at frontend kan bruke `/api/graphql` både lokalt og på VM-en, uten at adressen til backend ligger i koden. + +### 6. Sjekke at det virker + +1. På VM-en: `curl -i -X POST http://127.0.0.1/api/graphql -H 'Content-Type: application/json' -d '{"query":"{ countries }"}'` gir en liste med land. +2. http://it2810-20.idi.ntnu.no/project2/ laster uten feil i konsollen, og byer vises. +3. Søk, landfilter, sortering og «Show more cities» endrer resultatet. +4. Skriv en anmeldelse, last siden på nytt og se at den fortsatt er der. + +Hvis siden ikke laster: `sudo tail -n 40 /var/log/apache2/error.log`. Hvis `searchCities` gir null treff, sjekk collection- og feltnavn i `server/src/db.ts`. Hvis `ECONNREFUSED 27017` vises, kjører ikke MongoDB. + +### Etter endringer + +Frontend: bygg på nytt, kjør `scp` og kopieringskommandoene fra steg 4. Backend: se steg 3. + +## Arbeidsflyt -- Denne README-en beskriver funksjoner, teknologistack og hvordan frontend kommuniserer med API-et. -- GraphQL-operasjonene og klientkallet ligger i `src/App.tsx` og `src/graphql.ts`. -- Lokal GraphQL-proxy er definert i `vite.config.ts`. -- Endringer i backend-schemaet må gjenspeiles i GraphQL-operasjonene og TypeScript-typene i frontend. +`main` er beskyttet. Vi jobber i egne branches og åpner pull requests, og minst én annen på gruppa må godkjenne før den slås sammen. Alle kommentarer må være løst, og nye commits fjerner godkjenningen. Før en pull request kjører vi `pnpm lint` og `pnpm build`. \ No newline at end of file diff --git a/server/README.md b/server/README.md deleted file mode 100644 index 4402125..0000000 --- a/server/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Server (GraphQL-backend) - -Node + TypeScript + Apollo Server 5 mot MongoDB. Lytter på port 3001. - -## Kjøre lokalt - -```bash -cd server -npm install -cp .env.example .env # MONGODB_URI og MONGODB_DB -npm run indexes # oppretter indekser (trygt å kjøre flere ganger) -npm run dev # http://localhost:3001/ -``` - -Produksjon (på VM-en): `npm run build && npm start`. Bruk gjerne systemd eller pm2 -slik at prosessen starter igjen etter restart. - -## Forventet datamodell - -**Collection `cities`**, ett dokument per by. Feltnavnene styres av `FIELD` i -`src/db.ts`, og standardverdiene er: - -```json -{ "name": "Oslo", "country": "Norway", "latitude": 59.91, "longitude": 10.75, - "population": 700000, "timezone": "Europe/Oslo" } -``` - -Hvis feltene i den importerte collectionen heter noe annet, endre `FIELD` og -`COLLECTION` i `src/db.ts`. Ingen andre filer trenger å endres. `population`, -`latitude` og `longitude` bør være tall (ikke tekst), ellers blir sortering og -filtrering feil. - -**Collection `reviews`** opprettes automatisk når første anmeldelse lagres: - -```json -{ "cityId": ObjectId, "rating": 4, "comment": "…", "author": "Anonym", "createdAt": Date } -``` - -By-id er MongoDBs `_id` (ObjectId), eksponert som en streng i GraphQL. - -## API - -| Operasjon | Beskrivelse | -| --- | --- | -| `searchCities(search, filter, sort, limit, offset)` | Case-insensitivt søk på bynavn, filtrering (land, tidssone, befolkning), sortering og paginering. Returnerer `items`, `totalCount`, `hasMore`. `limit` er maks 100. | -| `city(id)` | Detaljer for én by, inkludert anmeldelser. | -| `countries` | Alle land, til filtervalg. | -| `addReview(input)` | Lagrer en rating (1-5) og valgfri kommentar. | - -Eksempel: - -```graphql -query { - searchCities( - search: "trond" - filter: { country: "Norway", minPopulation: 100000 } - sort: { field: POPULATION, direction: DESC } - limit: 20 - offset: 0 - ) { - totalCount - hasMore - items { id name country population averageRating reviewCount } - } -} -``` - -## Designvalg - -- **Filtrering og sortering i databasen**, slik at det gjelder hele resultatsettet. -- **Sortering fra hvitliste**, og søketekst escapes før den brukes i regex. -- **Stabil paginering**: `_id` er tiebreaker i sorteringen. -- **Review-statistikk** hentes med én aggregering for kun byene på gjeldende side. -- **Indekser** på befolkning, navn, land og anmeldelser (`npm run indexes`). -- **Kjent begrensning:** Søket er et regex-treff på delstreng, som ikke kan bruke - indeks fullt ut. For 30 000–50 000 byer er det raskt nok. For en mye større - samling bør man bytte til prefikssøk på et lowercase-felt eller en tekstindeks.