feat(deepLinks): implement comprehensive deep linking system

- Add type-safe deep link parameter validation using Zod
- Implement consistent error handling across all deep link routes
- Add support for query parameters in deep links
- Create comprehensive deep linking documentation
- Add logging for deep link operations

Security:
- Validate all deep link parameters before processing
- Sanitize and type-check query parameters
- Add error boundaries around deep link handling
- Implement route-specific parameter validation

Testing:
- Add parameter validation tests
- Add error handling tests
- Test query parameter support
This commit is contained in:
Matthew Raymer
2025-02-26 09:35:04 +00:00
parent 3dbb836ae5
commit 64a04ec9a5
6 changed files with 199 additions and 85 deletions

View File

@@ -49,6 +49,8 @@ import { App } from "./lib/capacitor/app";
import router from "./router";
import { handleApiError } from "./services/api";
import { AxiosError } from "axios";
import { DeepLinkHandler } from "./services/deepLinks";
import { logConsoleAndDb } from "./db";
console.log("[Capacitor] Starting initialization");
console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
@@ -62,6 +64,8 @@ window.addEventListener("unhandledrejection", (event) => {
}
});
const deepLinkHandler = new DeepLinkHandler(router);
/**
* Handles deep link routing for the application
* Processes URLs in the format timesafari://<route>/<param>
@@ -80,56 +84,11 @@ window.addEventListener("unhandledrejection", (event) => {
*/
const handleDeepLink = async (data: { url: string }) => {
try {
console.log("[Capacitor Deep Link] START Handler");
console.log("[Capacitor Deep Link] Received URL:", data.url);
await router.isReady();
const parts = data.url.split("://");
if (parts.length !== 2) {
throw new Error("Invalid URL format");
}
const path = parts[1];
console.log("[Capacitor Deep Link] Parsed path:", path);
// Define supported parameterized routes and their regex patterns
const paramRoutes = {
"claim-add-raw": /^claim-add-raw\/(.+)$/,
"claim-cert": /^claim-cert\/(.+)$/,
claim: /^claim\/(.+)$/,
"confirm-gift": /^confirm-gift\/(.+)$/,
"contact-edit": /^contact-edit\/(.+)$/,
"contact-import": /^contact-import\/(.+)$/,
did: /^did\/(.+)$/,
"invite-one-accept": /^invite-one-accept\/(.+)$/,
"offer-details": /^offer-details\/(.+)$/,
project: /^project\/(.+)$/,
"user-profile": /^user-profile\/(.+)$/,
};
// Match route pattern and extract parameter
for (const [routeName, pattern] of Object.entries(paramRoutes)) {
const match = path.match(pattern);
if (match) {
console.log(
`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`,
);
await router.replace({
name: routeName,
params: { id: match[1] },
});
return;
}
}
// Default fallback for non-parameterized routes
await router.replace("/" + path);
await deepLinkHandler.handleDeepLink(data.url);
} catch (error) {
console.error("[Capacitor Deep Link] Error:", error);
if (error instanceof Error) {
handleApiError({ message: error.message } as AxiosError, "deep-link");
}
logConsoleAndDb("[DeepLink] Error handling deep link: " + error, true);
handleApiError(error, "deep-link");
}
};