La cuarta fuente de migración en nuestro despliegue de Nivel 3 se lanzó hoy. Pega un token de la API de TinyURL Pro o Bulk, elige un dominio de Elido de destino y haz clic en Iniciar. De cuatro a siete minutos después, cada alias de TinyURL estará en tu dominio de Elido con el alias preservado cuando no haya colisión.
Esta publicación es el informe de ingeniería: qué es específico de TinyURL, el límite deliberado que implementamos y por qué la "migración de nivel gratuito de TinyURL" no es algo que podamos construir.
El problema del nivel gratuito#
La versión pública de TinyURL no tiene API y nunca la ha tenido. El tinyurl.com/<slug> clásico que creas sin una cuenta es una redirección de "dispara y olvida": el usuario la crea a través del formulario de la página de inicio, recibe un slug y el slug nunca vuelve a aparecer en ningún panel de control de cuenta. No hay listado por usuario porque no hay vinculación por usuario.
Esto es bien conocido, pero vale la pena destacarlo en la página de destino /migrate-from/tinyurl porque la consulta de búsqueda "migrate from TinyURL" no distingue entre Pro y gratuito. Implementamos:
- Una llamada clara de "Solo Pro/Bulk" en el hero de la página de destino.
- Una entrada de preguntas frecuentes que dirige a los usuarios del nivel gratuito al formulario /docs/guides/bulk-create para el acortamiento masivo de listas de destinos pegadas.
- Un paso de validación de token en el lanzador que falla rápidamente con "este token no está en un plan Pro o Bulk" en lugar de dejar que la ejecución falle silenciosamente con un 401 a mitad de la paginación.
El razonamiento: todas las demás fuentes de migración que lanzamos tienen un camino feliz para "cada usuario que la busca". TinyURL es la excepción: los usuarios del nivel gratuito necesitan un modelo mental diferente y deberíamos establecer esa expectativa antes de que peguen algo.
Forma de la API REST Pro/Bulk#
La API de TinyURL Pro es sencilla: token bearer, respuestas JSON, 100 aliases por página. La paginación utiliza un parámetro de cadena de consulta page que tiene índice 1; la respuesta incluye data.aliases (la matriz de enlaces) y meta.has_more (la señal de continuar).
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++
}
Cada alias lleva url (el destino largo), alias (el slug personalizado o código corto autogenerado), description (un campo opcional de TinyURL que preservamos como título del enlace de Elido) y un domain (TinyURL permite dominios de marca en planes Bulk).
Terminología: alias vs slug#
TinyURL los llama "aliases". Nosotros los llamamos "slugs". Es lo mismo: la secuencia de caracteres después del host en la URL de redirección. La migración preserva el alias 1:1 donde el dominio de Elido de destino no tiene colisión; si hay una colisión, se aplica la estrategia estándar de conflicto de sufijo/omitir/fallar.
Consideramos renombrar "slug" a "alias" en el lanzador para que coincida con la terminología del proveedor de origen y lo rechazamos por razones de consistencia. Todas las demás superficies de Elido (lista de enlaces, API, SDK, panel de control) usan "slug". Importar asimetría de terminología en un lanzador haría que la experiencia posterior a la importación fuera confusa.
El lanzador incluye una etiqueta de una línea que dice "TinyURL llama a estos aliases" encima del radio de estrategia de conflicto, para que los usuarios que buscan "alias" en la página de la receta encuentren el control correcto sin tener que leer cada palabra.
Dominios de marca y la transferencia de DNS#
Los planes Bulk de TinyURL admiten dominios de marca: tu propio nombre de host enrutado a través de la infraestructura de TinyURL. Cuando migras a Elido, el slug se importa limpiamente y el lado del DNS es un cambio de CNAME.
El caso interesante es "Tengo un dominio de marca en TinyURL Bulk y quiero mantener el mismo nombre de host después de la migración". Lo manejamos de la misma manera que la migración de Short.io:
- La migración se completa. Los enlaces importados residen en
s.elido.me/<alias>por defecto (o en tu dominio personalizado de Elido existente). - Agregas el dominio de marca de TinyURL como un dominio personalizado de Elido a través de /docs/guides/custom-domains.
- Apuntas el CNAME a Elido. El TLS bajo demanda de Caddy emite un certificado en la primera solicitud;
domain-manageres la fuente de verdad de la lista de permitidos, por lo que los nombres de host no autorizados son rechazados. - La superficie de TinyURL deja de resolver para ese nombre de host; la de Elido toma el control.
Puedes mantener ambas superficies activas en paralelo hasta que finalice tu suscripción a TinyURL, luego el cambio es simplemente dejar que el nombre de host de TinyURL expire. Sin urgencia, sin riesgo el día del cambio.
Lo que no migramos#
Historial de clics. Las analíticas de TinyURL Pro/Bulk son endpoints de informes separados que no están estructurados para la exportación. El plan Bulk expone los recuentos de clics por enlace en el panel de control pero no los muestra a través de la API amigable para la migración; los nuevos clics aterrizan en las analíticas de Elido desde el cambio.
Estilos de QR y plantillas UTM del nivel Bulk. La misma historia que cualquier otra fuente de migración: el slug se importa, la capa de presentación circundante se reconstruye en Elido. Etiquetado como imported:tinyurl para seguimiento masivo a través de campañas.
Enlaces de TinyURL de nivel gratuito. Como se discutió anteriormente, la versión pública de TinyURL no tiene API. La mitigación es el formulario de creación masiva, no un trabajo de migración.
Manejo de tokens#
Las mismas semánticas de un solo uso que Bitly, Rebrandly y Short.io:
bgCtx := context.WithoutCancel(r.Context())
go h.tinyurl.Run(bgCtx, job.ID, imports.TinyURLJobOptions{
Token: req.Token,
})
source_token_id permanece NULL. El token vive en la memoria del proceso api-core durante la ejecución del worker y se descarta al finalizar. Sin persistencia, sin fila en service_tokens, sin cifrado de sobre ADR-0036: eso es para integraciones de Nivel 2 donde el usuario desea llamadas recurrentes al proveedor.
El paso de validación de token al inicio del trabajo golpea el endpoint /account/domains de TinyURL: una llamada económica, devuelve una lista de dominios que el token puede ver. Si devuelve 401, fallamos rápido con "el token es inválido o no está en un plan Pro o Bulk" en lugar de dejar que el usuario espere dos minutos para un 401 a mitad de la paginación y un mensaje de error menos útil.
Resolución de conflictos#
Idéntico a cualquier otro proveedor de migración: el sufijo recorre myalias-2, myalias-3, … en caso de colisión; omitir deja el enlace de Elido existente intacto y registra la fila de origen; fallar aborta en el primer conflicto.
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
}
La búsqueda es una lectura indexada por fila. Pagamos una lectura adicional pero obtenemos recorridos de sufijos deterministas y mensajes de error más amigables que buscar violaciones de unicidad.
El contrato del worker#
MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute, progressEvery=50, errorLogCap=1_000. Compartido en los cinco proveedores de migración. Estas constantes son el contrato que asume la interfaz de usuario de sondeo del panel de control.
Una cuenta de TinyURL Pro de 2,000 aliases golpea la API 20 veces y termina en 3–5 minutos. Una cuenta Bulk de 20,000 aliases toma 200 viajes de ida y vuelta y termina en 15–20 minutos. Por encima de 50,000 aliases, el worker falla rotundamente con una instrucción para enviar un correo a [email protected] para una migración fragmentada; la ruta de migración fragmentada es solo para concierge en la v1.
Reanudabilidad y el problema de despliegue#
El mismo compromiso que las tres primeras migraciones. El worker está en proceso; un despliegue a mitad de la importación mata la goroutine. El cron de limpieza de trabajos atascados cambia cualquier fila running sin progreso en 30 minutos a failed. La re-ejecución es idempotente bajo sufijo y omitir.
Para cuentas de más de 10,000 aliases, la reanudabilidad valdría la pena: registraríamos el cursor de page de TinyURL en import_jobs.source_filter y reanudaríamos desde la última página completada. Los otros cuatro proveedores de migración se beneficiarán del mismo cambio una vez que lo lancemos; el diseño es compartido.
Respaldo CSV#
Para los usuarios en planes Bulk con un CSV exportado que ya no tienen un token de API activo, ejecutamos trabajos CSV únicos desde la bandeja de entrada: envía un correo a [email protected]. No lanzamos un formulario de carga de CSV de autoservicio porque la ruta REST cubre el caso común y la ruta CSV necesita un ajuste de esquema por cuenta que se hace mejor a mano que con un parser genérico frágil.
¿Qué sigue?#
Un proveedor más por aterrizar:
- Dub.co —
GET /api/links?projectSlug=…&limit=100. Las carpetas se aplanan en etiquetas. La API más limpia de las cinco.
Después de Dub, el despliegue de Nivel 3 está terminado. Cinco aterrizajes de migración, cinco publicaciones de blog de ingeniería, un scaffolding de worker compartido, una UI de sondeo de panel de control compartida.
Si has estado esperando porque la migración de TinyURL no estaba documentada, ahora lo está. Pruébala: token Pro/Bulk hasta el último enlace importado en menos de siete minutos para cuentas típicas.