6 min czytaniaInżynieria

Wdrożenie migracji z TinyURL: REST dla Pro/Bulk, brak ścieżki dla wersji darmowej

Jak zbudowaliśmy importy TinyURL Pro/Bulk jednym kliknięciem dla Elido — dlaczego publiczny TinyURL nie posiada API, terminologia alias-vs-slug oraz limit, który wdrożyliśmy celowo.

Marius Voß
DevRel · edge infra
Diagram potoku: API TinyURL Pro/Bulk po lewej stronie przepływające przez worker importu Elido do tabeli linków, z panelem bocznym wyszczególniającym gwarancje numeryczne (limit 50 tys., budżet 30 min, 100 na stronę, tylko plany Pro/Bulk)

Czwarta ścieżka migracji w naszym wdrożeniu Tier-3 została udostępniona dzisiaj. Wklej token API TinyURL Pro lub Bulk, wybierz docelową domenę Elido, kliknij Start. Od czterech do siedmiu minut później każdy alias TinyURL znajduje się w Twojej domenie Elido z zachowanym aliasem tam, gdzie nie wystąpiła kolizja.

Ten wpis to opracowanie inżynieryjne — co jest specyficzne dla TinyURL, celowy limit, który wdrożyliśmy, oraz dlaczego "migracja z darmowego planu TinyURL" jest czymś, czego nie możemy zbudować.

Problem darmowego planu#

Publiczny TinyURL nie posiada API i nigdy go nie posiadał. Klasyczny tinyurl.com/<slug>, który tworzysz bez konta, to przekierowanie typu fire-and-forget — użytkownik tworzy go za pomocą formularza na stronie głównej, otrzymuje slug, a slug ten nigdy nie pojawia się w żadnym pulpicie nawigacyjnym konta. Nie ma listy per użytkownik, ponieważ nie ma powiązania per użytkownik.

Jest to dobrze znane, ale warto o tym wspomnieć na stronie lądowania /migrate-from/tinyurl, ponieważ zapytanie w wyszukiwarce "migrate from TinyURL" nie rozróżnia wersji Pro od darmowej. Wdrożyliśmy:

  • Jasny komunikat "Tylko Pro/Bulk" w sekcji hero strony lądowania.
  • Wpis w FAQ, który kieruje użytkowników darmowego planu do formularza /docs/guides/bulk-create w celu masowego skracania wklejonej listy miejsc docelowych.
  • Krok walidacji tokena w launcherze, który kończy się szybkim błędem "ten token nie należy do planu Pro lub Bulk" zamiast cichego błędu 401 w trakcie paginacji.

Uzasadnienie: każde inne źródło migracji, które wdrażamy, ma szczęśliwą ścieżkę dla "każdego użytkownika, który go szuka". TinyURL jest wyjątkiem — użytkownicy darmowego planu potrzebują innego modelu mentalnego i powinniśmy określić te oczekiwania, zanim cokolwiek wkleją.

Kształt REST API Pro/Bulk#

TinyURL Pro API jest proste: token bearer, odpowiedzi JSON, 100 aliasów na stronę. Paginacja wykorzystuje parametr query-string page, który jest indeksowany od 1; odpowiedź zawiera data.aliases (tablica linków) i meta.has_more (sygnał kontynuacji).

const tinyurlPageSize = 100

page := 1
for {
    resp, err := w.fetchPage(ctx, opts.Token, page)
    if err != nil { /* mark failed */ return }
    if len(resp.Data.Aliases) == 0 { break }
    for _, alias := range resp.Data.Aliases { /* import */ }
    if !resp.Meta.HasMore { break }
    page++
}

Każdy alias przenosi url (długie miejsce docelowe), alias (niestandardowy slug lub automatycznie wygenerowany krótki kod), description (opcjonalne pole TinyURL, które zachowujemy jako tytuł linku Elido) oraz domain (TinyURL pozwala na markowe domeny w planach Bulk).

Terminologia — alias vs slug#

TinyURL nazywa je "aliasami". My nazywamy je "slugami". To samo — sekwencja znaków po hoście w adresie URL przekierowania. Migracja zachowuje alias 1:1 tam, gdzie docelowa domena Elido nie ma kolizji; jeśli wystąpi kolizja, stosuje się standardową strategię konfliktu suffix/skip/fail.

Rozważaliśmy zmianę nazwy "slug" na "alias" w launcherze, aby dopasować się do terminologii dostawcy źródłowego, i odrzuciliśmy to ze względów spójności. Każda inna powierzchnia Elido — lista linków, API, SDK, dashboard — używa "slug". Wprowadzenie asymetrii terminologicznej do jednego launchera sprawiłoby, że doświadczenie po imporcie byłoby mylące.

Launcher dodaje jednoliniową etykietę mówiącą "TinyURL nazywa je aliasami" nad przyciskiem wyboru strategii konfliktu, więc użytkownicy szukający na stronie przepisu hasła "alias" znajdują odpowiednią kontrolkę bez czytania każdego słowa.

Markowe domeny i przekazanie DNS#

Plany TinyURL Bulk obsługują markowe domeny — własna nazwa hosta routowana przez infrastrukturę TinyURL. Po migracji do Elido, slug importuje się czysto, a strona DNS to jedna zmiana CNAME.

Ciekawym przypadkiem jest "Mam markową domenę w TinyURL Bulk i chcę zachować tę samą nazwę hosta po migracji". Obsługujemy to w ten sam sposób, co migrację z Short.io:

  • Migracja kończy się. Importowane linki znajdują się domyślnie na s.elido.me/<alias> (lub Twojej istniejącej domenie niestandardowej Elido).
  • Dodajesz markową domenę TinyURL jako domenę niestandardową Elido przez /docs/guides/custom-domains.
  • Kierujesz CNAME na Elido. on-demand TLS Caddy wystawia certyfikat przy pierwszym żądaniu; domain-manager jest źródłem prawdy dla listy dozwolonych (allow-list), więc nieautoryzowane nazwy hostów są odrzucane.
  • Powierzchnia TinyURL przestaje rozwiązywać tę nazwę hosta; Elido przejmuje kontrolę.

Możesz utrzymać obie powierzchnie równolegle, dopóki nie zakończy się Twoja subskrypcja TinyURL, a wtedy przejście polega tylko na pozwoleniu nazwie hosta TinyURL wygasnąć. Brak pośpiechu, brak ryzyka w dniu przejścia.

Czego nie migrujemy#

Historia kliknięć. Analityka TinyURL Pro/Bulk to oddzielne punkty końcowe raportów, które nie są ustrukturyzowane do eksportu. Plan Bulk ujawnia liczbę kliknięć dla każdego linku w dashboardzie, ale nie udostępnia ich przez API przyjazne dla migracji; nowe kliknięcia trafiają do analityki Elido od momentu przejścia.

Stylizacja QR i szablony UTM w planie Bulk. Ta sama historia co w przypadku każdego innego źródła migracji — slug importuje się, otaczająca warstwa prezentacji jest odbudowywana w Elido. Otagowane imported:tinyurl do masowej obsługi przez kampanie.

Linki darmowego planu TinyURL. Jak wspomniano powyżej, publiczny TinyURL nie posiada API. Mitygacją jest formularz bulk-create, a nie zadanie migracyjne.

Obsługa tokena#

Ta sama semantyka one-shot co Bitly, Rebrandly i Short.io:

bgCtx := context.WithoutCancel(r.Context())
go h.tinyurl.Run(bgCtx, job.ID, imports.TinyURLJobOptions{
    Token: req.Token,
})

source_token_id pozostaje NULL. Token żyje w pamięci procesu api-core przez czas działania workera i jest usuwany po zakończeniu. Brak trwałości, brak wiersza service_tokens, brak szyfrowania kopertowego ADR-0036 — to jest przeznaczone dla integracji Tier-2, gdzie użytkownik chce powtarzalnych wywołań dostawcy.

Krok walidacji tokena na początku zadania uderza w endpoint /account/domains TinyURL — tanie wywołanie, zwraca listę domen, które token może widzieć. Jeśli wystąpi błąd 401, kończymy szybko błędem "token jest nieprawidłowy lub nie należy do planu Pro/Bulk" zamiast kazać użytkownikowi czekać dwie minuty na błąd 401 w trakcie paginacji i mniej pomocny komunikat o błędzie.

Rozwiązywanie konfliktów#

Identycznie jak u każdego innego dostawcy migracji — sufiks typu myalias-2, myalias-3, … przy kolizji; skip pozostawia istniejący link Elido w spokoju i loguje wiersz źródłowy; fail przerywa przy pierwszym konflikcie.

func (w *TinyURLWorker) resolveSlug(ctx context.Context, domainID int64, desired, strategy string) (string, error) {
    if _, err := w.links.GetByDomainSlug(ctx, domainID, desired); err != nil {
        if errors.Is(err, pgx.ErrNoRows) { return desired, nil }
        return "", fmt.Errorf("slug lookup: %w", err)
    }
    // suffix/skip/fail branching identical to bitly.go
}

Wyszukiwanie to jeden odczyt indeksowany na wiersz. Płacimy za dodatkowy odczyt, ale otrzymujemy deterministyczne sufiksy i bardziej przyjazne komunikaty o błędach niż szukanie naruszeń unikalności.

Kontrakt workera#

MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute, progressEvery=50, errorLogCap=1_000. Wspólne dla wszystkich pięciu dostawców migracji. Te stałe to kontrakt, który zakłada UI odpytywania dashboardu.

Konto TinyURL Pro z 2000 aliasami uderza w API 20 razy i kończy w 3–5 minut. Konto Bulk z 20 000 aliasami wymaga 200 rund tripów i kończy w 15–20 minut. Powyżej 50 000 aliasów worker kończy się twardym błędem z instrukcją wysłania maila na [email protected] w celu migracji podzielonej na kawałki (chunked); ścieżka migracji podzielonej jest w wersji v1 dostępna tylko z obsługą concierge.

Możliwość wznowienia i problem z deployem#

Ten sam kompromis co w przypadku trzech pierwszych migracji. Worker jest w procesie; deploy w trakcie importu zabija gorutynę. Cron typu stuck-sweep zmienia każdy wiersz running bez postępu w ciągu 30 minut na failed. Ponowne uruchomienie jest idempotentne w ramach suffix i skip.

Dla kont powyżej 10 000 aliasów możliwość wznowienia byłaby opłacalna — zapisywalibyśmy kursor page TinyURL w import_jobs.source_filter i wznawialibyśmy od ostatniej ukończonej strony. Czterej inni dostawcy migracji skorzystają na tej samej zmianie, gdy ją wdrożymy; projekt jest wspólny.

Rezerwowy CSV#

Dla użytkowników planów Bulk z wyeksportowanym plikiem CSV, którzy nie mają już aktywnego tokena API, uruchamiamy jednorazowe zadania CSV z skrzynki odbiorczej — napisz na [email protected]. Nie wdrożyliśmy samoobsługowego formularza przesyłania CSV, ponieważ ścieżka REST pokrywa typowy przypadek, a ścieżka CSV wymaga dopasowania schematu dla każdego konta, co lepiej zrobić ręcznie niż za pomocą kruchego ogólnego parsera.

Co dalej#

Jeszcze jeden dostawca do wdrożenia:

  • Dub.coGET /api/links?projectSlug=…&limit=100. Foldery są spłaszczane do tagów. Najczystsze API z całej piątki.

Po Dub, wdrożenie Tier-3 jest zakończone. Pięć stron lądowania migracji, pięć wpisów na blogu inżynieryjnym, jeden wspólny szkielet workera, jeden wspólny dashboard UI odpytywania.

Jeśli wstrzymywałeś się, ponieważ migracja z TinyURL była nieudokumentowana, teraz jest udokumentowana. Wypróbuj — od tokena Pro/Bulk do ostatniego zaimportowanego linku w mniej niż siedem minut dla typowych kont.

Powiązane na blogu#

Wypróbuj Elido

Skracarka URL hostowana w UE: własne domeny, głęboka analityka i otwarte API. Darmowy plan — bez karty kredytowej.

Tagi
tinyurl migration
url shortener
go worker
data migration
engineering
tier 3 integrations

Czytaj dalej