For arbeidsgivere

Bart van den Berg

Systemutvikler

Jeg er fullstack-utvikler i Bergen med frontend-utdanning fra Noroff og praktisk erfaring med å utvikle, drifte og videreutvikle en webapplikasjon som er i daglig bruk.

Hos Total Entreprenør AS har jeg bygget TotalTiming fra bunnen av: React-klient, Express-API, MySQL-database, tilgangsstyring og drift på egen server. Nå ser jeg etter en fast stilling som systemutvikler, der jeg kan jobbe i team og lære av erfarne utviklere. Resten av dette nettstedet hører til enkeltpersonforetaket mitt, men det er en fast stilling jeg er ute etter.

Portrett av Bart van den Berg utendørs, med fjell i bakgrunnen

Teknisk case

TotalTiming

Utgangspunkt og bruk

Før TotalTiming førte vi timene på papir. Mange glemte å notere timene sine og måtte spørre sjefen om han husket når de hadde jobbet, og i en travel hverdag var det lett at også han glemte å skrive det ned. Ved månedsslutt sendte hver ansatt timelisten sin, og sjefen måtte kontrollere alle for hånd. Det var dette jeg bygget TotalTiming for å løse: nå føres timene fortløpende på mobil eller PC, godkjennes direkte i systemet, og regnskap henter de godkjente timene rett fra appen i stedet for fra innsendte timelister.

TotalTiming er et arbeidsstyringssystem for byggebransjen. Ansatte fører timer og fravær, tar ledige vakter og laster opp bilder, filer og notater til prosjektene. Prosjektledere godkjenner eller avviser timer, regnskap gjennomgår timer og rapporter, og administratorer styrer brukere, prosjekter og vakter.

Systemet er utviklet i jobben min hos Total Entreprenør AS og brukes daglig av 10 aktive brukere, både ansatte og administrasjon. Til sammen føres det over 400 timer i systemet hver måned, og daglig leder har flere ganger sagt at systemet er til stor hjelp i hverdagen.

Vaktsystemet «Ledige timer» ble lansert i oktober 2026. Neste steg er e-postvarsler, slik at ansatte får beskjed om ledige vakter uten at noen må følge opp manuelt.

Se skjermbilder av TotalTiming →

Kildekoden er privat fordi den tilhører Total Entreprenør AS. Jeg går gjerne gjennom løsningen i et intervju.

Stack

Klient
React 19TypeScriptViteTailwind CSSTanStack QueryReact RouterZod
API
Node.js 20Express 5mysql2ZodJSON Web TokensbcryptMultersharp
Data og drift
MySQL 8.4DockerVPSCloudflare Tunnel

Arkitektur

  1. NettleserMobil og PC
  2. CloudflareHTTPS og tunnel (cloudflared)

VPS · Docker

  1. React-klientStatisk Vite-bygg
  2. Express APINode.js-container
  3. MySQL 8.4Container + private filer

Daglige sikkerhetskopier på serveren og på en egen maskin via SSH.

Arkitektur: Nettleseren går via Cloudflare, som sender trafikken gjennom en tunnel (cloudflared) til en VPS uten åpne webporter. Der kjører React-klienten, Express-API-et og MySQL-databasen i Docker. Klienten lastes ned som statiske filer og snakker deretter med API-et over HTTPS. Opplastede filer ligger i et privat Docker-volum.

Sikkerhet og personvern

  • Passord lagres som bcrypt-hasher. Tilgangstokens varer i 15 minutter, og fornyelsestokenet ligger i en httpOnly-cookie som bare sendes til /api/auth.
  • Hver forespørsel sjekker brukeren mot databasen. Deaktiverte brukere og utloggede økter blir derfor avvist med en gang, ikke først når tokenet utløper.
  • Rollene ansatt, regnskap og administrator sjekkes på serveren. Tilgang til prosjekter avgjøres av prosjektmedlemskap direkte i SQL-spørringene, så klienten kan aldri se mer enn brukeren har rett til.
  • All input valideres med Zod, og alle databasekall bruker parametriserte spørringer.
  • Opplastede filer lagres utenfor offentlig mappe med tilfeldige filnavn og kan bare hentes gjennom en innlogget, tilgangskontrollert rute. Filtyper og størrelser er begrenset.
  • Bilder konverteres til WebP med sharp. Da forsvinner også metadata som GPS-posisjon fra mobilkameraet.
  • Slettede prosjektnotater beholdes i 90 dager for gjenoppretting og revisjon, og skal deretter slettes permanent.
  • Klienten sendes med sikkerhetsheadere, blant annet HSTS og en streng Content-Security-Policy, og API-et tillater bare kjente domener via CORS.

Testing

  • Backend-tester skrevet med Node.js sin innebygde testløper. Databasen stubbes, og testoppsettet nekter å koble til en ekte database.
  • Testene dekker blant annet øktsikkerhet og tilgangsstyring, prosjektmedlemskap, interne prosjekter, notater, filer, vakter, oppmøtepoeng og aktivitetslogg.
  • Klienten har enhetstester for tidsberegning rundt vakter, i tillegg til TypeScript-bygg og ESLint.
  • Tester, lint og bygg kjører automatisk med GitHub Actions på hver pull request og hver endring på main.

Drift og sikkerhetskopier

  • Først driftet jeg TotalTiming selv fra en maskin hjemme, med Nginx foran. Maskinen var ikke energieffektiv, så det ble billigere å flytte løsningen til en VPS.
  • Klient, API og MySQL kjører i Docker på én VPS. Jeg valgte det fremfor tre separate tjenester for å holde hostingkostnadene nede, fordi Docker er enkelt å jobbe med, og fordi jeg ville lære det. Ulempen er at alt står på én server, og derfor tas det også kopier utenfor serveren.
  • Trafikken går gjennom Cloudflare Tunnel (cloudflared). Serveren åpner selv en utgående forbindelse til Cloudflare, så appen trenger ingen åpne porter mot internett, og Cloudflare håndterer HTTPS.
  • Databaseendringer leveres som versjonerte SQL-migreringer med dokumentert fremgangsmåte, verifisering og rollback.
  • Databasen sikkerhetskopieres hver dag både på serveren og til en egen maskin, med mysqldump over SSH. Kopiene på den egne maskinen slettes automatisk etter 30 dager.
  • [PLACEHOLDER: hvor ofte gjenoppretting fra backup testes]

Tre problemer jeg måtte løse

  1. Utlogging som faktisk stenger tilgangen

    Problemet
    Rene JWT-er er raske, men kan ikke trekkes tilbake før de utløper. En ansatt som slutter, eller en mistet telefon, skulle ikke beholde tilgang.
    Slik løste jeg det
    Hver bruker har et sessionVersion-felt som bakes inn i tokenene. Utlogging øker versjonen, og alle tokens med gammel versjon blir ugyldige på alle enheter.
    Hva det koster
    Det koster ett enkelt databaseoppslag per forespørsel, men gir umiddelbar tilbaketrekking uten å måtte drifte en egen øktlagring.
  2. Samme person, ulik rolle på ulike prosjekter

    Problemet
    I byggebransjen kan samme person lede ett prosjekt og jobbe som vanlig ansatt på et annet. En global prosjektleder-rolle ville gitt for mye eller for lite tilgang.
    Slik løste jeg det
    Prosjektledelse og medlemskap lagres per prosjekt. Én felles funksjon lager SQL-betingelsen som avgjør hvilke prosjekter en bruker får se, og den brukes på tvers av API-et.
    Hva det koster
    Spørringene blir litt mer komplekse, men tilgangsregelen ligger ett sted og er dekket av tester.
  3. Poeng som ikke kan telles dobbelt

    Problemet
    Oppmøte på vakter gir poeng, og registreringer kan bli korrigert i ettertid. Doble klikk eller nye forsøk etter nettverksfeil skulle ikke gi doble poeng.
    Slik løste jeg det
    Hver endring i oppmøte skrives som en ny rad i en poenglogg som bare tillater nye rader. Unike nøkler på bookingversjon og operasjons-ID gjør gjentatte forespørsler trygge, og databasetriggere hindrer sletting av historikk.
    Hva det koster
    Det krever transaksjoner og mer omhyggelig API-kode, men poengsummen kan alltid regnes ut og forklares fra historikken.

Erfaring og utdanning

Arbeid

  1. Apr 2025 – nå

    Utvikler · Total Entreprenør AS

    Utvikler og drifter TotalTiming, og har laget og vedlikeholder bedriftens nettside.

  2. Jun 2019 – sep 2023

    Nattevakt og resepsjonist · Citybox Bergen

    Gjestehenvendelser, bookinger og daglig drift, og opplæring av nye ansatte.

  3. Mar 2016 – nov 2018

    Kundeservice · Sellbytel, Barcelona

    Kundeservice for internasjonale merkevarer som Salomon, Arc'teryx og Atomic.

  4. 2013 – 2016

    Personlig trener og instruktør

  5. Des 2009 – mai 2012

    Soldat · 11 Luchtmobiele Brigade, Den nederlandske hæren

Utdanning

  1. Aug 2026 – jun 2028

    Cybersikkerhet, høyere fagskole (2 år) · Gokstad Akademiet

    Pågår. Informasjonssikkerhet, etisk hacking, penetrasjonstesting og cyberforsvar.

  2. Sep 2026

    Networking Basics (22 timer) · Cisco Networking Academy via Gokstad Akademiet

    Grunnleggende nettverk og nettverksprotokoller.

  3. Sep 2026

    Linux Essentials · Cisco Networking Academy

    Grunnleggende Linux og kommandolinjen.

  4. Mar 2023 – mar 2025

    Frontend-utvikling (2 år) · Noroff School of Technology and Digital Media

    React, TypeScript, REST API-er og universell utforming. Scrum Master i et teamprosjekt.

Ferdigheter

Språk
  • TypeScript
  • JavaScript
  • SQL
  • HTML
  • CSS
Backend
  • Node.js
  • Express
  • REST API-er
  • MySQL
  • JWT-autentisering
  • Zod
Frontend
  • React
  • Next.js
  • Tailwind CSS
  • TanStack Query
  • React Router
  • Universell utforming (WCAG)
Drift og verktøy
  • Docker
  • Nginx
  • Linux (Ubuntu)
  • Fail2Ban
  • Git og GitHub
  • Netlify
  • Figma
Testing og CI
  • Node.js test runner
  • ESLint
  • TypeScript
  • GitHub Actions
Metodikk
  • Scrum (Scrum Master i studieprosjekt)
  • Versjonerte databasemigreringer
  • SEO

Litt om meg

Jeg er oppvokst i Nederland og har bodd og jobbet i Barcelona før jeg slo meg ned i Arna. Bakgrunnen fra militæret, kundeservice og byggebransjen har lært meg å jobbe strukturert og å forstå hva brukerne faktisk trenger.

På fritiden trener jeg, går på tur og er mye sammen med barna mine. Jeg liker å utvikle nye idéer og leke med ny teknologi, for eksempel å sette opp ti AI-agenter samtidig bare for å se hva som skjer. Jeg løser også oppgaver på Hack The Box, som henger godt sammen med cybersikkerhetsstudiet. Ellers slapper jeg av med serier og spill.

Språk: Nederlandsk (morsmål), engelsk (flytende) og norsk (B1/B2, brukes daglig på jobb).

Send meg en e-post
CV – systemutvikler | Bartholomeus Berg