Files
cal.diy/companion/services/webAuth.ts
T
Dhairyashil Shinde 2a84d46fe9 feat(companion): add Biome for linting and formatting (#26214)
* feat(companion): add ESLint with Prettier integration

- Add ESLint with eslint-config-expo/flat for SDK 55
- Integrate Prettier via eslint-plugin-prettier
- Configure environment-specific settings for Node.js config files and browser extension
- Add lint script to package.json
- Fix Prettier formatting issues in AvailabilityTab.tsx and types/index.ts

* fix(companion): use bun lockfile instead of npm

- Remove package-lock.json that was accidentally committed
- Update bun.lock with ESLint dependencies

* fix(companion): fix all ESLint errors (16 total)

- Fix unescaped entities in JSX (react/no-unescaped-entities):
  - app/(tabs)/(event-types)/index.tsx: escape quotes in delete confirmation
  - components/LoginScreen.tsx: escape apostrophe in "Don't have an account"
  - components/NetworkStatusBanner.tsx: escape apostrophes in offline message
  - components/booking-modals/BookingModals.tsx: escape apostrophe in rejection message
  - components/event-type-detail/tabs/AdvancedTab.tsx: escape quotes and apostrophes
  - components/screens/AvailabilityListScreen.tsx: escape quotes in delete confirmation

- Fix react-hooks/rules-of-hooks in RescheduleScreen.tsx:
  - Move useMemo calls before conditional early return to ensure hooks
    are called in the same order on every render

* fix(companion): fix all ESLint warnings (68 total)

- Fix import/no-named-as-default warnings by using named imports
- Fix @typescript-eslint/no-unused-vars warnings by removing unused imports/variables
- Fix react-hooks/exhaustive-deps warnings with eslint-disable comments
- Convert unused error variables in catch blocks to bare catch
- Remove unused imports (useRouter, Alert, useSafeAreaInsets, etc.)
- All ESLint checks now pass with 0 errors and 0 warnings

* feat(companion): add pre-commit lint check with --max-warnings=0

- Add companion lint check to root lint-staged.config.mjs
- Runs 'bun run lint -- --fix --max-warnings=0' for companion files
- Follows same pattern as apps/packages lint check
- Supports SKIP_WARNINGS=1 env var to bypass warning check if needed
- Commit will fail if any ESLint errors or warnings are present

* fix(companion): restore state variables and imports removed during ESLint fixes

- Restore locationAddress, locationLink, locationPhone state variables in event-type-detail.tsx (still used in fetchEventTypeData)
- Restore Alert import in BookingListScreen.tsx (still used for report booking)
- Add eslint-disable comments for the restored state variables since they're only used by setters

* test

* test

* fix eslint pre commit check

* fix: In React Native, HTML entities like ' are rendered literally as text, not decoded like in web browsers

* refactor(companion): remove ESLint and Prettier config in preparation for Biome

* feat(companion): add Biome for linting and formatting

- Install @biomejs/biome with exact version pinning (2.3.10)
- Add biome.json with formatting settings matching previous Prettier config
- Add lint, format, check, and check:ci scripts to package.json
- Update lint-staged to use Biome instead of Prettier
- Remove Prettier and prettier-plugin-tailwindcss dependencies
- Delete .prettierrc.js configuration file

* style(companion): apply Biome formatting and fix lint-staged config

- Update lint-staged to use 'biome format' instead of 'biome check'
  (pre-commit should only format, not lint)
- Apply Biome formatting to all companion files

* feat(companion): configure Biome for strict linting with pre-commit enforcement

- Apply Biome unsafe fixes (unused imports, optional chaining, etc.)
- Configure biome.json rules to disable overly strict rules for existing code:
  - noExplicitAny, noArrayIndexKey, useIterableCallbackReturn (suspicious)
  - useExhaustiveDependencies, noUnusedFunctionParameters (correctness)
  - noStaticOnlyClass (complexity)
  - noNonNullAssertion (style)
  - noStaticElementInteractions (a11y)
- Update lint-staged to use 'biome check --write --error-on-warnings'
  (pre-commit now runs both formatting AND linting, fails on warnings)

* fix(companion): enable noNonNullAssertion rule and fix all violations

- Enable noNonNullAssertion rule in biome.json (set to 'error')
- Fix 7 violations across the codebase:
  - hooks/useBookings.ts: Add runtime check before API call
  - hooks/useEventTypes.ts: Add runtime check before API call
  - hooks/useSchedules.ts: Add runtime check before API call
  - extension/entrypoints/content.ts: Add null checks for cache and getAttribute

* fix(companion): enable noStaticOnlyClass and noStaticElementInteractions rules

- Enable noStaticOnlyClass rule in biome.json
- Enable noStaticElementInteractions rule in biome.json
- Convert CalComAPIService from static class to object literal pattern
- Convert WebAuthService from static class to object literal pattern
- Add role="tooltip" to Tooltip.web.tsx for accessibility

* fix(companion): resolve TypeScript type errors in useEffect dependencies and remove unused state property

* fix(companion): enable noUnusedFunctionParameters rule and fix 9 violations

* fix(companion): enable noUnusedVariables rule and fix 4 violations

* fix(companion): enable useIterableCallbackReturn rule and fix 11 violations

* fix(companion): enable useExhaustiveDependencies rule and fix 7 violations

* fix(companion): enable noArrayIndexKey rule and fix 10 violations

* fix(companion): enable noExplicitAny rule and fix all violations

- Enable noExplicitAny rule in biome.json
- Replace any types with proper specific types across all files:
  - calcom.ts: Add proper type definitions for API responses and inputs
  - event-types.types.ts: Add BookingLimitsCount, BookingLimitsDuration, ConfirmationPolicy types
  - buildPartialUpdatePayload.ts: Fix type mismatches for booking limits functions
  - booking-actions.ts: Add proper types for booking action handlers
  - BookingDetailScreen.tsx, MarkNoShowScreen.tsx: Add types for attendee data
  - AvailabilityListItem.ios.tsx, BookingListItem.ios.tsx: Import SFSymbols7_0 type
  - extension/content.ts: Fix slot types and __cleanup invocation issues
- Remove unused imports and interfaces

* address cubics comments

* add useHookAtTopLevel rule
2025-12-27 08:19:53 -03:00

203 lines
5.2 KiB
TypeScript

import { Platform } from "react-native";
import type { UserProfile } from "./types/users.types";
export interface WebSessionInfo {
isLoggedIn: boolean;
accessToken?: string;
refreshToken?: string;
userInfo?: UserProfile;
}
// Check if running on web
function isWeb(): boolean {
return Platform.OS === "web";
}
// Get cookies from the browser
function getCookies(): { [key: string]: string } {
if (!isWeb()) return {};
const cookies: { [key: string]: string } = {};
if (typeof document !== "undefined") {
document.cookie.split(";").forEach((cookie) => {
const [name, value] = cookie.trim().split("=");
if (name && value) {
cookies[name] = decodeURIComponent(value);
}
});
}
return cookies;
}
// Check for Cal.com session cookies
async function checkCalComSession(): Promise<WebSessionInfo> {
if (!isWeb()) {
return { isLoggedIn: false };
}
try {
// Check for common Cal.com session cookies
const cookies = getCookies();
// Look for Cal.com authentication cookies
// Common patterns: next-auth.session-token, __Secure-next-auth.session-token, etc.
const sessionCookieNames = [
"next-auth.session-token",
"__Secure-next-auth.session-token",
"__Host-next-auth.session-token",
"next-auth.csrf-token",
"cal-session",
"session-token",
];
const foundSessionCookie = sessionCookieNames.find((name) => cookies[name]);
if (foundSessionCookie) {
// Try to validate the session with Cal.com API
const sessionInfo = await validateWebSession();
return sessionInfo;
}
return { isLoggedIn: false };
} catch (_error) {
return { isLoggedIn: false };
}
}
// Validate web session by calling Cal.com API with cookies
async function validateWebSession(): Promise<WebSessionInfo> {
if (!isWeb()) {
return { isLoggedIn: false };
}
try {
// First try to call the NextAuth session endpoint
const sessionResponse = await fetch("https://app.cal.com/api/auth/session", {
method: "GET",
credentials: "include", // Include cookies
headers: {
"Content-Type": "application/json",
},
});
if (sessionResponse.ok) {
const sessionData = await sessionResponse.json();
if (sessionData?.user) {
// User is logged in via web session
return {
isLoggedIn: true,
userInfo: sessionData.user,
};
}
}
// Try the internal Cal.com me endpoint (this might work with cookies)
const meResponse = await fetch("https://app.cal.com/api/me", {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
if (meResponse.ok) {
const userData = await meResponse.json();
if (userData && (userData.id || userData.user || userData.data)) {
return {
isLoggedIn: true,
userInfo: userData.user || userData.data || userData,
};
}
}
// Try to check if user is logged in by attempting to access a protected page
const dashboardResponse = await fetch("https://app.cal.com/api/trpc/viewer.me", {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
if (dashboardResponse.ok) {
const dashboardData = await dashboardResponse.json();
if (dashboardData?.result?.data) {
return {
isLoggedIn: true,
userInfo: dashboardData.result.data,
};
}
}
return { isLoggedIn: false };
} catch (_error) {
return { isLoggedIn: false };
}
}
// Try to get API tokens from web session
async function getTokensFromWebSession(): Promise<{ accessToken?: string; refreshToken?: string }> {
if (!isWeb()) {
return {};
}
try {
// Some Cal.com implementations might expose tokens via specific endpoints
// This is a fallback approach - in practice, web sessions might not expose tokens directly
// Check localStorage/sessionStorage for any stored tokens
if (typeof window !== "undefined") {
const localStorage = window.localStorage;
const sessionStorage = window.sessionStorage;
const possibleTokenKeys = [
"cal-access-token",
"cal-auth-token",
"next-auth.token",
"auth-token",
"access-token",
];
for (const key of possibleTokenKeys) {
const localToken = localStorage.getItem(key);
const sessionToken = sessionStorage.getItem(key);
if (localToken || sessionToken) {
return {
accessToken: localToken || sessionToken || undefined,
};
}
}
}
return {};
} catch (_error) {
return {};
}
}
// Redirect to Cal.com web login instead of using WebView
function redirectToWebLogin(): void {
if (!isWeb()) return;
// For web, redirect directly to Cal.com login
const currentUrl = window.location.href;
const loginUrl = `https://app.cal.com/auth/signin?callbackUrl=${encodeURIComponent(currentUrl)}`;
window.location.href = loginUrl;
}
// Export as object to satisfy noStaticOnlyClass rule
export const WebAuthService = {
isWeb,
getCookies,
checkCalComSession,
validateWebSession,
getTokensFromWebSession,
redirectToWebLogin,
};