Een dating-app op basis van brieven, niet van gezichten!
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
-- Liefdesbrieven: seed-data
-- Geboortedata: vandaag - n jaar - (40 + 3n) dagen.
-- Locaties: Utrecht (Daan, Marit, Nina), Amsterdam (Noor, Sam), Nijmegen (Hanna),
-- Groningen (Vera), Rotterdam (Jasper), Wijchen (Bram). Straal is standaard leeg (geen afstandsfilter).
-- Hanna is een trans vrouw: alleen zichtbaar voor wie 'ook trans personen' aan heeft (Daan, Noor, Sam; Jasper niet).
-- Relatiestatus: iedereen vrijgezel behalve Sam (open relatie); Noor en Sam willen ook
-- mensen in een relatie zien (seek_relatie), de rest ziet alleen vrijgezellen (standaard).
--
-- Inloggen kan als iedereen: e-mail <naam>@voorbeeld.nl, wachtwoord "liefdesbrieven".
-- Alle seed-gebruikers hebben al toestemming gegeven (consent_at), zodat de toestemmingsvraag
-- niet bij elke seed-login opduikt; wil je hem zien: update users set consent_at = null where name = 'Daan';
-- De hash hieronder is scrypt met vaste salt; opnieuw maken met:
-- node -e "import('./server/auth.js').then(a => a.hashPassword('liefdesbrieven', Buffer.from('liefdesbrieven-seed!')).then(console.log))"
-- Foto's zitten niet in de seed: die maak je in de app zelf (schrijf een brief met een foto),
-- zodat de hele weg — verkleinen, versturen, opslaan, terugvragen — echt getest wordt.
-- Alle tabellen in één truncate: meldingen, waarschuwingen en maatregelen verwijzen naar
-- letters en users, dus ze moeten mee — anders weigert Postgres de hele opdracht.
truncate departures, purchases, measures, warnings, reports, blocks, favorites, photos, letters, sessions, password_resets, users restart identity;
-- signup_slots gaat niet mee in de truncate: dat is één vaste rij (de teller), geen data. Hij wordt
-- onderaan dit bestand gelijkgezet aan het aantal seed-gebruikers.
with input (name, gender, trans, relationship, years, days_ago_active, bio, seek_genders, seek_relatie, seek_trans, age_min, age_max, lat, lon, radius_km) as (
values
('Daan', 'm', false, 'vrijgezel', 33, 0,
'Ik lees traag en wandel snel, of andersom, dat wisselt. Zoek iemand die brieven durft te schrijven die nergens heen hoeven. Ik bak op zondag, meestal mislukt het op een interessante manier.',
'{v}', false, true, 28, 38, 52.0907, 5.1214, null),
('Marit', 'v', false, 'vrijgezel', 31, 0,
'Boulderen op dinsdag, Dostojevski op zondag — De idioot ligt al maanden half open naast mijn bed. Ik schrijf lange berichten en verwacht ze terug. Favoriete plek: de leeszaal van de UB als het regent.',
'{m}', false, false, 29, 40, 52.0860, 5.1200, null),
('Noor', 'v', false, 'vrijgezel', 28, 2,
'Zuurdesem is mijn huisdier. Verder: volkstuin, augurken inmaken, en fietsen naar plekken waar je eigenlijk niet heen fietst. Ik geloof dat je iemand pas kent na drie brieven.',
'{m,x}', true, true, 27, 36, 52.3676, 4.9041, null),
('Hanna', 'v', true, 'vrijgezel', 35, 5,
'Ik loop GR-paden in stukken van een week en werk tussendoor in een archief. Oud papier, oude routes, nieuwe mensen. Wandelen praat makkelijker dan tegenover elkaar zitten.',
'{m}', false, true, 30, 42, 51.8426, 5.8546, null),
-- Vera: Daan past niet in háár filter → onzichtbaar voor hem
('Vera', 'v', false, 'vrijgezel', 40, 1,
'Opera in de zaal, motor op de dijk. Ik zoek iemand met levenservaring en een pen die niet aarzelt.',
'{m}', false, false, 42, 55, 53.2194, 6.5665, null),
-- Jasper: zelfde gender als Daan → geen wederzijdse match
('Jasper', 'm', false, 'vrijgezel', 34, 3,
'Zeilen, kaarten tekenen, en heel af en toe iets vangen. Brieven schrijf ik het liefst vanaf het water.',
'{v}', false, false, 28, 38, 51.9244, 4.4777, null),
-- Sam: in een (open) relatie → alleen zichtbaar voor wie 'ook mensen in een relatie' aanvinkt (Noor)
('Sam', 'x', false, 'relatie', 30, 1,
'Poëzie en lange fietstochten. Ik schrijf ''s nachts, dus verwacht mijn brieven in de ochtend.',
'{x,v}', true, true, 25, 35, 52.3702, 4.8952, null)
)
insert into users (email, password_hash, consent_at, name, gender, trans, relationship, birthdate, bio, last_active, seek_genders, seek_relatie, seek_trans, seek_min_birth, seek_max_birth, lat, lon, seek_radius_km)
select
lower(name) || '@voorbeeld.nl',
'scrypt$16384$8$1$bGllZmRlc2JyaWV2ZW4tc2VlZCE$2ODMSaaLZbo7LBHzKxahvLBrOndQ9OTDkCOMfCOiIY8',
now(),
name,
gender,
trans,
relationship,
(current_date - make_interval(years => years) - make_interval(days => 40 + 3 * years))::date,
bio,
now() - make_interval(days => days_ago_active),
seek_genders::char(1)[],
seek_relatie,
seek_trans,
oldest_birthdate(age_max),
youngest_birthdate(age_min),
lat, lon, radius_km::int -- kolom is overal null → cast nodig
from input;
-- Een lange, al gelezen briefwisseling tussen Daan en Marit (30 brieven, om en om,
-- 40 tot 3 dagen geleden) — zodat de paginering (THREAD_PAGE) en het bijladen van
-- oudere brieven in de correspondentie iets te doen hebben. Daarna volgt Marits nieuwste,
-- ongelezen brief.
insert into letters (from_id, to_id, sent_at, read_at, body)
select
case when n % 2 = 1 then d.id else m.id end,
case when n % 2 = 1 then m.id else d.id end,
now() - make_interval(days => 41 - n, hours => (n * 7) % 24),
now() - make_interval(days => 40 - n),
(array[
'Ik schrijf je vanuit de leeszaal, waar het regent tegen de hoge ramen en iedereen doet alsof dat niet zo is. Je vorige brief ligt hier naast me; ik heb hem twee keer gelezen, één keer snel en één keer zoals het hoort. Over het brood: het is dit weekend niet mislukt, wat me eerlijk gezegd een beetje teleurstelde. Vertel me wat je zondag hebt gedaan, en of De idioot inmiddels een bladzijde verder is.',
'Boulderen was vanavond een ramp van het soort dat je later mooi kunt navertellen. Ik viel drie keer op dezelfde greep en de vierde keer bleef ik hangen, meer uit koppigheid dan uit techniek. Onderweg naar huis dacht ik aan wat je schreef over brieven die nergens heen hoeven. Deze hoeft ook nergens heen. Maar ik wil wel weten wat je leest als je traag leest, want daar heb je nog steeds geen antwoord op gegeven.',
'Vandaag een lange wandeling langs de Kromme Rijn, met een boek in mijn jaszak dat ik niet heb opengeslagen. Dat vind ik het fijne aan wandelen met een boek: het idee is genoeg. Ik moest denken aan jouw leeszaal en aan hoe verschillend we dezelfde dingen doen. Schrijf me over de plank der goede voornemens; welke boeken staan er en welke ga je echt nog lezen, en welke horen daar alleen te staan?',
'Je vraag over de plank der goede voornemens heeft me een avond gekost. Ik heb hem opgeruimd, wat betekent dat ik alles eraf heb gehaald, gestapeld, en er precies zo weer op heb gezet. Er staan negen boeken. Twee ga ik lezen, drie hoop ik te lezen, en de rest zijn eerlijk gezegd cadeaus die ik niet durf weg te doen. Wat staat er bij jou naast het bed, behalve datgene waar je al maanden in vastzit?',
'Het brood van dit weekend is gelukt en dat maakt me achterdochtig. Ik heb niets anders gedaan dan anders. Misschien is het weer, misschien is het dat ik onder het kneden aan je brief zat te denken en daardoor minder haast had. Ik zeg dit niet om aardig te doen, ik zeg het omdat het waarschijnlijk zo is. Wanneer regent het weer in de leeszaal, en mag ik dan een keer aan de andere kant van de tafel zitten?'
])[1 + n % 5]
from generate_series(1, 30) as n, users d, users m
where d.name = 'Daan' and m.name = 'Marit';
-- Marits nieuwste brief aan Daan, twee dagen geleden, nog ongelezen
insert into letters (from_id, to_id, sent_at, body)
select m.id, d.id, now() - interval '2 days',
E'Je schreef dat je bakken op een interessante manier mislukt — dat wil ik graag preciezer weten. Interessant als in: het werd per ongeluk iets anders dat óók lekker was? Of interessant als in: de brandweer kent je inmiddels bij naam?\n\nJe zou naar mijn boekenkast gevraagd hebben, dus: de onderste plank is de plank der goede voornemens. Dostojevski staat er niet tussen, die ligt naast mijn bed, half open, al maanden op dezelfde pagina. Ik zeg tegen mezelf dat dat spaarzaamheid is.\n\nSchrijf me over het brood. En over wat je leest als je traag leest.'
from users m, users d
where m.name = 'Marit' and d.name = 'Daan';
-- Een oudere, al gelezen brief van Noor aan Daan; Daan heeft Noor als favoriet gemarkeerd,
-- dus zij staat in zijn postvak bóven Marit, ondanks de nieuwere brief van Marit.
insert into letters (from_id, to_id, sent_at, read_at, body)
select n.id, d.id, now() - interval '5 days', now() - interval '4 days',
E'Je zei traag lezen en snel wandelen, of andersom. Ik doe het omgekeerde met brood: snel beginnen, dan dagen wachten. Mijn zuurdesem heet Gerrit en is ouder dan de meeste vriendschappen die ik heb; hij krijgt elke ochtend eten voordat ik zelf iets neem.\n\nWat lees je als het langzaam gaat? Ik zit vast in een dik boek over de geschiedenis van de aardappel en ik weet niet of dat interessant is of dat ik gewoon koppig ben.\n\nDe volkstuin stond er vorige week prachtig bij, na de regen. Ik stuur je binnenkort een foto.'
from users n, users d
where n.name = 'Noor' and d.name = 'Daan';
insert into favorites (user_id, other_id)
select d.id, n.id from users d, users n where d.name = 'Daan' and n.name = 'Noor';
-- ---------------------------------------------------------------------------------------------
-- Twee accounts die er ook op een échte installatie horen te staan: de bouwer, en het account
-- waarmee de beoordelaar van de app-store naar binnen gaat. Alles in deze app zit achter de login,
-- dus zonder zo'n account ziet een reviewer niets — en zonder post ziet hij wel een app maar geen
-- correspondentie, en kan hij melden en blokkeren niet uitproberen. Dát is waar bij een datingapp
-- naar gekeken wordt.
--
-- Ze staan hier met een adres op @voorbeeld.nl en het gewone seed-wachtwoord. De échte adressen en
-- wachtwoorden komen uit de omgeving (SEED_BRAM_* en SEED_REVIEW_*, zie .env.example) en worden er
-- door db/setup.js overheen gezet. Dat is met opzet: deze repository is openbaar, en een account op
-- een draaiende installatie hoort niet met een bekend wachtwoord in git te staan.
--
-- De filters passen wederzijds — 33 zoekt 25–44, 28 zoekt 22–37 — en ze passen ook op een deel van
-- de mensen hierboven, zodat Zoeken voor allebei niet leeg is.
insert into users (email, password_hash, consent_at, name, gender, trans, relationship, birthdate, bio,
last_active, seek_genders, seek_relatie, seek_trans, seek_min_birth, seek_max_birth, lat, lon)
values
('bram@voorbeeld.nl', 'scrypt$16384$8$1$bGllZmRlc2JyaWV2ZW4tc2VlZCE$2ODMSaaLZbo7LBHzKxahvLBrOndQ9OTDkCOMfCOiIY8',
now(), 'Bram', 'm', false, 'vrijgezel',
-- Een echte datum en geen "vandaag min 33 jaar": dit account is van een echt iemand, en dan
-- hoort zijn leeftijd gewoon mee te lopen met de kalender.
date '1992-09-13',
'Ontwikkelaar. Ik maak het liefst dingen helemaal zelf af — een boekhoudprogramma voor eenmanszaken, mijn eigen git-server, en een stapel projecten waar niemand om gevraagd heeft. Verder probeer ik gitaar te leren spelen, met wisselend succes. Schrijf me over iets waar je te lang over hebt nagedacht; dat leest het prettigst.',
-- Wijchen, op de precisie die de app zelf hanteert (LOCATION_DECIMALS = 2, ~1 km): dit is een
-- echt account van een echt iemand, en dan hoort hier geen adres maar een plaats te staan.
now(), '{v}', true, true, oldest_birthdate(44), youngest_birthdate(25), 51.81, 5.72),
('review@voorbeeld.nl', 'scrypt$16384$8$1$bGllZmRlc2JyaWV2ZW4tc2VlZCE$2ODMSaaLZbo7LBHzKxahvLBrOndQ9OTDkCOMfCOiIY8',
now(), 'Nina', 'v', false, 'vrijgezel',
(current_date - make_interval(years => 28) - interval '124 days')::date,
'Ik werk in een boekwinkel en lees het meeste staand, tussen twee klanten door. Schrijf me over iets waar je te lang over hebt nagedacht — dat leest het prettigst.',
now(), '{m}', false, false, oldest_birthdate(37), youngest_birthdate(22), 52.0860, 5.1200);
-- Post over en weer tussen die twee: vier gelezen brieven, om en om beginnend bij Bram, en daarna
-- één ongelezen van hem. Zo staat er meteen een correspondentie klaar — te lezen, te beantwoorden,
-- en te gebruiken om melden en blokkeren uit te proberen zonder eerst zelf te moeten schrijven.
-- Dat de ongelezene naar Nina gaat is met opzet: zij is het account waarmee een beoordelaar
-- binnenkomt, en die hoort post te vinden en niet een leeg postvak.
insert into letters (from_id, to_id, sent_at, read_at, body)
select
case when n % 2 = 1 then b.id else r.id end,
case when n % 2 = 1 then r.id else b.id end,
now() - make_interval(days => 10 - 2 * n, hours => (n * 5) % 24),
now() - make_interval(days => 9 - 2 * n),
(array[
E'Je schreef dat je het meeste staand leest. Dat verklaart iets, geloof ik: staand lees je anders dan zittend, ongeduldiger, maar ook eerlijker — wat niet boeit leg je meteen weg.\n\nIk heb deze week geprobeerd traag te lezen en merkte dat ik het verleerd ben. Halverwege een bladzijde betrap ik mezelf erop dat ik alleen nog kijk hoe ver het nog is. Hoe doe jij dat, tussen twee klanten door?',
E'Tussen twee klanten door heb je geen keus: je leest een alinea en die moet het dan maar doen. Het rare is dat ik me juist die alinea''s het best herinner.\n\nGisteren kwam er iemand een boek terugbrengen omdat het te verdrietig was. Ik heb het teruggenomen en het daarna zelf gelezen. Hij had gelijk. Toch ben ik blij dat ik het gelezen heb — dat is denk ik het hele punt.\n\nWaar denk jij te lang over na?',
E'Over dit soort brieven, om te beginnen. Ik ben er drie keer opnieuw aan begonnen en telkens werd het te netjes — alsof ik solliciteerde naar een gesprek in plaats van er al in te zitten. Dus dan maar zo, met de aanloop erin.\n\nWat ik eigenlijk wilde zeggen: ik vond het mooi dat je dat boek eerst zelf bent gaan lezen voordat je er iets van vond. De meeste mensen doen dat andersom, en ik ook, vaker dan me lief is.\n\nEn verder denk ik te lang na over werk waarvan niemand merkt dat het er is zolang het goed gaat. Dat is een raar soort vak. Jij hebt tenminste een winkel waar mensen binnenlopen.',
E'Werk waarvan niemand merkt dat het er is zolang het goed gaat — dat is precies wat mijn vader deed, veertig jaar lang, bij de waterleiding. Hij zei altijd dat hij pas op het journaal kwam als hij zijn werk niet goed had gedaan. Ik heb daar als kind nooit iets van begrepen en nu ineens wel.\n\nJe aanloop mocht er trouwens in blijven staan. Ik lees liever een brief die drie keer opnieuw begonnen is dan een die in één keer goed ging; in dat eerste zit iemand die het nog niet wist toen hij begon.\n\nVertel eens wat je gebouwd hebt waar je zelf nog weleens aan denkt.'
])[n]
from generate_series(1, 4) as n, users b, users r
where b.name = 'Bram' and r.name = 'Nina';
insert into letters (from_id, to_id, sent_at, body)
select b.id, r.id, now() - interval '1 day',
E'Ik liep vanochtend langs jouw boekwinkel, of langs een boekwinkel die ik voor de jouwe hield, en heb er niet naar binnen gekeken. Dat leek me eerlijker: eerst schrijven, dan pas kijken.\n\nJe vroeg waar ik te lang over nadenk. Behalve over brieven: over de vraag of iets traag mag zijn zonder dat je het hoeft te verdedigen. Ik denk het wel, maar ik betrap me erop dat ik het toch elke keer uitleg.\n\nSchrijf eens over die alinea''s die je onthoudt. Ik ben benieuwd of het er ook maar één is die de schrijver belangrijk had bedoeld.'
from users b, users r
where b.name = 'Bram' and r.name = 'Nina';
-- De gratis bak gelijkzetten aan wat de seed heeft aangemaakt: deze accounts hebben allemaal een
-- gratis plek gekregen, dus de teller hoort dat te weten. Zo gedraagt een ontwikkelopstelling zich
-- als productie (en is de bak leeg te krijgen door FREE_ACCOUNTS tijdelijk laag te zetten).
update signup_slots set granted = (select count(*) from users);