Wpisujesz, czego szukasz. Model czyta bazę 131 osób i zwraca tych, z którymi warto pogadać, z uzasadnieniem i pytaniem na start.
index.html strona — trzy zakładki: Matcher, Agenda, Swipe agendy
agenda.js agenda campu (godziny, ścieżki, prowadzący) — wczytywana w przeglądarce
api/match.js funkcja serwerowa: jedyne miejsce, które zna klucz API
api/data.js GENEROWANY katalog dla modelu — nie edytuj ręcznie
profiles.js 56 uczestników (intro z grupy WhatsApp)
speakers.js 75 prelegentów, mentorów i jury (agenda + biogramy PARP)
build-data.js przerabia profiles.js + speakers.js na api/data.js
vercel.json
aistudio.google.com → Get API key → Create API key in new project. Bez karty, darmowy tier.
Funkcja obsługuje też Claude jako zapas — patrz „Gdy padnie limit” niżej.
Najprościej przez GitHub: załóż repo, wrzuć te pliki, potem na vercel.com →
Add New → Project → wybierz repo → Deploy.
Framework Preset: Other. Build Command i Output Directory zostaw puste.
Alternatywa bez GitHuba:
npm i -g vercel
vercel # w katalogu projektu, odpowiadasz na kilka pytańTo jest ten krok, którego nie wolno pominąć ani obejść. W panelu Vercela: Project → Settings → Environment Variables → dodaj:
| Nazwa | Wartość |
|---|---|
GEMINI_API_KEY |
Twój klucz z AI Studio |
GEMINI_MODEL |
(opcjonalnie) np. gemini-2.5-flash |
Klucza nie wolno wkleić do index.html ani do żadnego pliku w repo. Wszystko, co trafia do
przeglądarki, jest jawne — każdy odwiedzający może podejrzeć źródło strony, wyciągnąć klucz
i obciążyć nim Twoje konto. Klucz ma istnieć wyłącznie w zmiennych środowiskowych Vercela,
gdzie czyta go funkcja api/match.js.
Po dodaniu zmiennej zrób Redeploy — bez tego stara wersja jej nie zobaczy.
GEMINI_MODEL możesz zostawić puste. Jeśli domyślna nazwa modelu nie zadziała, funkcja sama
zapyta API o listę dostępnych i wybierze wariant Flash. Listę zobaczysz też sam:
curl https://generativelanguage.googleapis.com/v1beta/models -H "x-goog-api-key: TWOJ_KLUCZ"Gemini ma dzienny limit. Jeśli wyczerpie się w środku eventu, strona zacznie zwracać błąd. Ratunek zajmuje 30 sekund i nie wymaga zmiany kodu:
- Weź klucz z
console.anthropic.com→ Settings → API Keys - Vercel → Settings → Environment Variables → usuń
GEMINI_API_KEY, dodajANTHROPIC_API_KEY - Redeploy
Funkcja sama wykryje, który klucz jest ustawiony. Claude jest płatny, ale katalog idzie przez prompt caching, więc przy modelu Haiku i skali tego wydarzenia to pojedyncze złotówki. Jak ustawisz oba klucze naraz, wygrywa Gemini.
Wejdź na adres z Vercela, wpisz „szukam inwestora na pierwszą rundę”, poczekaj kilka sekund. Powinny wyjść nazwiska z uzasadnieniami.
Jeśli wyskoczy błąd — Vercel → Deployments → wybierz deploy → Runtime Logs. Tam jest powód.
- Dodaj rekord w
profiles.js(uczestnicy) albospeakers.js(prelegenci) node build-data.js- Commit i push — Vercel przebuduje sam
Krok 2 jest obowiązkowy. Bez niego model nie zobaczy nowej osoby, mimo że jest w pliku.
Katalog ma około 27 tys. tokenów i leci przy każdym zapytaniu. Na darmowym tierze Gemini kosztuje to zero — liczy się tylko dzienny limit zapytań.
W api/match.js siedzą dwa bezpieczniki:
MAX_Q = 500— dłuższych zapytań funkcja nie przyjmieRPM_PER_IP = 12— maksymalnie 12 zapytań na minutę z jednego adresu
To zgrubna ochrona, licząca w pamięci procesu. Wystarczy przeciw przypadkowemu zapętleniu i komuś, kto się nudzi. Nie wystarczy przeciw komuś, kto naprawdę chce Ci wypalić limit — jeśli link wyjdzie poza grupę, obserwuj zużycie w konsoli Anthropic i w razie czego wygeneruj nowy klucz.
- Podmień
TWOJ@MAIL.PLw stopceindex.htmlna swój adres noindexjest już w<head>— strona nie wejdzie do Google- Napisz do organizatora zanim link pójdzie na grupę
- Baza zawiera tylko to, co ludzie sami wrzucili na grupę, plus publiczną agendę PARP.
Numery telefonów widoczne na zrzutach nie weszły. Natalia z HerRise jest oznaczona
hidden:true, bo jej opis pochodził z LinkedIna, a nie z jej intro — zdejmij tę flagę dopiero, jeśli sama napisze coś na grupie.
Zakładka Agenda pokazuje cały program obu dni z agenda.js. Przycisk + przy punkcie
zapisuje go do „Mojej agendy” — lista trzyma się w localStorage przeglądarki, nic nie idzie
na serwer. Przy każdym punkcie jest link Dodaj do Google Calendar (gotowy formularz
wydarzenia), a nad listą przycisk Pobierz .ics eksportujący wszystkie zapisane punkty
jednym plikiem — importuje się do Google Calendar, Apple Calendar i Outlooka.
Przycisk Wyślij do Google Calendar loguje użytkownika przez OAuth i wrzuca wszystkie zapisane punkty do jego kalendarza. Bez backendu — cały przepływ dzieje się w przeglądarce (Google Identity Services, token client). Client secret nie jest tu w ogóle używany.
Zakres uprawnień to calendar.app.created: strona tworzy własny, osobny kalendarz
„PARP Future Camp 2026” i pisze tylko po nim. Do głównego kalendarza użytkownika nie ma
dostępu ani do odczytu, ani do zapisu. To najwęższy zakres, jaki wystarcza do tego zadania —
i dlatego nie wymaga przechodzenia przez weryfikację aplikacji, którą Google nakłada na
zakresy dające dostęp do całego kalendarza.
Żeby to włączyło się na Twoim wdrożeniu, potrzebny jest własny OAuth Client ID:
- Nowy projekt:
console.cloud.google.com/projectcreate - Włącz Google Calendar API:
console.cloud.google.com/apis/library/calendar-json.googleapis.com - Ekran zgody:
console.cloud.google.com/auth/overview→ Get started → nazwa aplikacji, mail wsparcia, Audience External, dane kontaktowe - Klient:
console.cloud.google.com/auth/clients→ Create client → typ Web application - W Authorized JavaScript origins dodaj adres strony (np.
https://twoj-projekt.vercel.app, a do testówhttp://localhost:5199) - Wklej Client ID do stałej
GOOGLE_CLIENT_IDwindex.html
Starsze poradniki każą szukać APIs & Services → Credentials. Tej ścieżki już nie ma — zarządzanie klientami OAuth przeniosło się do sekcji Google Auth Platform → Clients. Zanim skonfigurujesz ekran zgody (krok 3), lista klientów bywa pusta i nie da się nic dodać.
Client ID jest jawny z definicji — leci do przeglądarki przy każdym logowaniu OAuth, więc
trzymanie go w index.html jest w porządku. To nie jest sekret. (Klucz do modelu z sekcji
wyżej sekretem jest i musi zostać w zmiennych środowiskowych.)
Jeśli GOOGLE_CLIENT_ID zostanie puste, przycisk pokaże panel z instrukcją i polem na
wklejenie ID — wtedy siedzi ono tylko w localStorage tej jednej przeglądarki. Wygodne
do sprawdzenia, czy działa, zanim wpiszesz je na stałe.
Ponowna wysyłka nie duplikuje wydarzeń: strona pamięta w localStorage, co już wysłała,
i aktualizuje istniejące wpisy zamiast tworzyć nowe.
Zakładka Swipe agendy pokazuje wyłącznie punkty, które nachodzą na siebie w czasie: dwie alternatywy naraz, wybierasz jedną przeciągnięciem karty albo kliknięciem. Wybrany punkt ląduje w Mojej agendzie, a wszystko, co z nim koliduje, znika z kolejki pytań.
Godziny końca krótkich wystąpień są wyliczone z początku następnego punktu na tej samej ścieżce — to przybliżenie, nie oficjalny program.
Na karcie wyniku ta sekcja pokazuje tekst źródłowy bez żadnych przeróbek:
dla uczestnika — dokładnie jego intro z grupy WhatsApp (pole raw w profiles.js),
dla prelegenta — dokładnie biogram z jego strony w PARP (pole bio w speakers.js).
Przerobiony, skrócony opis (does) idzie tylko do modelu i do nagłówka karty.