From 03304c354bae4dae4507471e368faec0073d067f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?=
Date: Sat, 23 May 2026 10:18:46 +0200
Subject: [PATCH 1/6] Add Komoot import with public (bio verification) and
authenticated modes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Two-mode import: public mode verifies Komoot account ownership by checking
that the user's trails.cool profile URL appears in their Komoot bio — no
credentials stored. Authenticated mode uses email + password (AES-256-GCM
encrypted) to import private tours as well.
Includes unit tests for crypto/komoot client and E2E tests for the full
connect + import flow.
Co-Authored-By: Claude Sonnet 4.6
---
.../credential-adapters/noop.ts | 11 +
.../lib/connected-services/providers/index.ts | 4 +-
.../providers/komoot/importer.test.ts | 128 +++++++++
.../providers/komoot/importer.ts | 91 ++++++
.../providers/komoot/manifest.ts | 20 ++
.../app/lib/connected-services/registry.ts | 3 +
.../app/lib/connected-services/types.ts | 2 +-
apps/journal/app/lib/crypto.server.test.ts | 49 ++++
apps/journal/app/lib/crypto.server.ts | 41 +++
apps/journal/app/lib/komoot.server.test.ts | 184 ++++++++++++
apps/journal/app/lib/komoot.server.ts | 188 +++++++++++++
apps/journal/app/routes.ts | 4 +
.../app/routes/api.sync.komoot.connect.ts | 48 ++++
.../app/routes/api.sync.komoot.verify.ts | 41 +++
apps/journal/app/routes/legal.privacy.tsx | 13 +-
.../routes/settings.connections.komoot.tsx | 204 ++++++++++++++
.../app/routes/settings.connections.tsx | 3 +-
.../journal/app/routes/sync.import.komoot.tsx | 260 +++++++++++++++++
e2e/komoot-import.test.ts | 262 ++++++++++++++++++
packages/i18n/src/locales/de.ts | 26 ++
packages/i18n/src/locales/en.ts | 26 ++
playwright.config.ts | 8 +
22 files changed, 1612 insertions(+), 4 deletions(-)
create mode 100644 apps/journal/app/lib/connected-services/credential-adapters/noop.ts
create mode 100644 apps/journal/app/lib/connected-services/providers/komoot/importer.test.ts
create mode 100644 apps/journal/app/lib/connected-services/providers/komoot/importer.ts
create mode 100644 apps/journal/app/lib/connected-services/providers/komoot/manifest.ts
create mode 100644 apps/journal/app/lib/crypto.server.test.ts
create mode 100644 apps/journal/app/lib/crypto.server.ts
create mode 100644 apps/journal/app/lib/komoot.server.test.ts
create mode 100644 apps/journal/app/lib/komoot.server.ts
create mode 100644 apps/journal/app/routes/api.sync.komoot.connect.ts
create mode 100644 apps/journal/app/routes/api.sync.komoot.verify.ts
create mode 100644 apps/journal/app/routes/settings.connections.komoot.tsx
create mode 100644 apps/journal/app/routes/sync.import.komoot.tsx
create mode 100644 e2e/komoot-import.test.ts
diff --git a/apps/journal/app/lib/connected-services/credential-adapters/noop.ts b/apps/journal/app/lib/connected-services/credential-adapters/noop.ts
new file mode 100644
index 0000000..f68bd26
--- /dev/null
+++ b/apps/journal/app/lib/connected-services/credential-adapters/noop.ts
@@ -0,0 +1,11 @@
+// No-op CredentialAdapter for providers whose credentials never expire and
+// cannot be refreshed (e.g. Komoot basic-auth, public-mode connections).
+
+import type { CredentialAdapter, Credentials } from "../types.ts";
+
+export const noopCredentialAdapter: CredentialAdapter
+ {t("komoot.publicInstructions")} +
++ {verifyError === "not_verified" + ? t("komoot.verificationError") + : verifyError === "invalid_url" + ? t("komoot.invalidUrl") + : t("komoot.verificationError")} +
+ )} + {isVerifying && ( +{t("komoot.verifyPending")}
+ )} ++ {connectError === "invalid_credentials" + ? t("komoot.authError") + : t("komoot.authError")} +
+ )} +{workout.name}
+{t("sync.noWorkouts")}
+ ) : ( +