Skip to content

feat: add Proton Calendar integration#29552

Draft
singhaditya21 wants to merge 8 commits into
calcom:mainfrom
singhaditya21:bounty-proton-calendar-5756
Draft

feat: add Proton Calendar integration#29552
singhaditya21 wants to merge 8 commits into
calcom:mainfrom
singhaditya21:bounty-proton-calendar-5756

Conversation

@singhaditya21

@singhaditya21 singhaditya21 commented Jun 13, 2026

Copy link
Copy Markdown

This PR integrates Proton Calendar into Cal.diy.

Demo Video:

https://github.com/singhaditya21/cal.diy/raw/bounty-proton-calendar-5756/packages/app-store/protoncalendar/static/proton_calendar_demo.webm

Details

  • Uses Proton Calendar's public ICS feed URLs to perform read-only availability checks.
  • Validates the Proton Calendar URLs to prevent SSRF or unauthorized endpoints (only allows https://calendar.proton.me/api/calendar/v1/url/...).
  • Extends the ics-feedcalendar codebase and registers protoncalendar in the App Store configurations and platform constants.

/claim #5756

@github-actions

Copy link
Copy Markdown
Contributor

Welcome to Cal.diy, @singhaditya21! Thanks for opening this pull request.

A few things to keep in mind:

  • This is Cal.diy, not Cal.com. Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will not be deployed to the Cal.com production app.
  • Please review our Contributing Guidelines if you haven't already.
  • Make sure your PR title follows the Conventional Commits format.

A maintainer will review your PR soon. Thanks for contributing!

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds complete Proton Calendar integration to Cal.com. Users can now connect their Proton Calendar ICS subscription feeds for read-only availability checking. The implementation includes new platform constants identifying the app, URL validation enforcing Proton Calendar's specific ICS endpoints, a calendar service factory wrapping the existing ICS feed infrastructure with a configurable integration name, a POST/GET API handler validating and persisting credentials with encrypted URLs, a React setup component for user input, and registration wiring across app store metadata, handler registries, and service maps.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add Proton Calendar integration' directly and concisely summarizes the main objective of the changeset, which is to integrate Proton Calendar into Cal.diy.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description accurately describes the Proton Calendar integration, mentioning ICS feed URLs, SSRF validation, and the extension of ics-feedcalendar codebase, all of which align with the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
apps/web/components/apps/protoncalendar/Setup.tsx (2)

71-71: 💤 Low value

Use template literals for conditional className.

String concatenation for conditional class names is error-prone. Consider using template literals or a utility like clsx/cn.

♻️ Proposed refactor
-                        containerClassName={`w-full ${i === 0 ? "mr-6" : ""}`}
+                        containerClassName={`w-full ${i === 0 ? "mr-6" : ""}`}

Or with template literals:

-                        containerClassName={`w-full ${i === 0 ? "mr-6" : ""}`}
+                        containerClassName={i === 0 ? "w-full mr-6" : "w-full"}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/apps/protoncalendar/Setup.tsx` at line 71, The
conditional className currently built with ad-hoc concatenation should be
rewritten using a safer utility or cleaner template handling: update the
containerClassName assignment in the Setup component to use a classnames helper
(e.g., cn or clsx) or a trimmed template literal so the empty string case
doesn't produce extra spaces; target the containerClassName usage where i is
checked (i === 0) in the Setup.tsx component and replace it with cn('w-full', {
'mr-6': i === 0 }) or an equivalent trimmed template literal expression.

91-98: 💤 Low value

Use Button component for consistency.

The "Add" button uses a plain <button> element while other actions use the Button component from @calcom/ui. Using Button consistently improves maintainability and ensures uniform styling.

♻️ Proposed refactor
-                <button
-                  className="text-sm"
+                <Button
+                  color="minimal"
                   type="button"
                   onClick={() => {
                     setUrls((urls) => urls.concat(""));
                   }}>
-                  {t("add")} <PlusIcon className="inline" size={16} />
-                </button>
+                  <PlusIcon className="inline mr-1" size={16} />
+                  {t("add")}
+                </Button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/apps/protoncalendar/Setup.tsx` around lines 91 - 98,
Replace the plain <button> with the shared Button component from `@calcom/ui`:
import Button if missing and use <Button type="button" onClick={() =>
setUrls(urls => urls.concat(""))} className="text-sm"> {t("add")} <PlusIcon
className="inline" size={16} /> </Button>; keep the same onClick behavior and
visual content (t("add") + PlusIcon), preserve any accessibility props (type,
aria-label if used elsewhere) and adjust or remove redundant class props if the
Button exposes size/variant props to maintain consistent styling.
packages/app-store/protoncalendar/api/add.ts (2)

34-42: 💤 Low value

Prefer findUniqueOrThrow for unique field lookups.

Since id is a unique field, findUniqueOrThrow is more semantically correct and slightly more efficient than findFirstOrThrow.

♻️ Proposed refactor
-    const user = await prisma.user.findFirstOrThrow({
+    const user = await prisma.user.findUniqueOrThrow({
       where: {
         id: userId,
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app-store/protoncalendar/api/add.ts` around lines 34 - 42, Replace
the call to prisma.user.findFirstOrThrow with prisma.user.findUniqueOrThrow when
querying by the unique id; specifically update the invocation in the add handler
where prisma.user.findFirstOrThrow({ where: { id: userId }, select: {...} }) is
used to instead call prisma.user.findUniqueOrThrow({ where: { id: userId },
select: {...} }) so the lookup is semantically correct and slightly more
efficient while keeping the same select payload (id, email).

54-60: ⚖️ Poor tradeoff

Consider extracting credential shape construction.

The credential object passed to BuildCalendarService uses dummy values (id: 0, encryptedKey: null) for validation purposes. While functional, extracting this into a helper function would clarify intent and improve maintainability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app-store/protoncalendar/api/add.ts` around lines 54 - 60, The
credential object passed into BuildCalendarService is constructed inline with
dummy values (id: 0, encryptedKey: null); extract this into a small helper
(e.g., buildValidationCredential or makeCalendarCredential) that takes the real
data and returns the credential shape used for validation, then replace the
inline object in add.ts with a call to that helper; update BuildCalendarService
invocation to use the helper result and keep id/encryptedKey defaults inside the
helper so intent is clear and maintainability improves.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/components/apps/protoncalendar/Setup.tsx`:
- Around line 59-61: The catch block currently swallows errors; change it to
capture the error (e.g., catch (err)) and log the error details before calling
setErrorMessage(t("something_went_wrong")). Update the catch in the function
containing setErrorMessage to call console.error (or the app logger) with a
descriptive message plus the caught error, then preserve the existing
setErrorMessage call so the UI still shows the generic message.
- Line 51: The current expression "const json = await res.json().catch(() =>
({}));" swallows JSON parse errors; change it to first check the response status
(res.ok) and then parse JSON inside a try/catch so parse failures are not
converted into an empty object: attempt "await res.json()" in a try block, on
failure read and include the raw response text and the original error in a log
or thrown Error, and rethrow the error instead of returning {} so callers can
surface API problems; use the existing "res" variable and the "const json"
assignment as the location to implement this behavior.
- Line 58: Validate json.url before calling router.push: ensure the response
JSON has a non-empty string url (e.g. check json && typeof json.url === 'string'
&& json.url.trim() !== '') before invoking router.push(json.url); if validation
fails, log or surface an error and avoid navigation. Update the code path around
router.push(json.url) (in the Setup component / the submit/handler function
where router.push is called) to perform this check and handle the
invalid/malformed response gracefully.
- Around line 19-20: Replace the local useState usage for urls and errorMessage
with react-hook-form controlled fields: remove const [urls, setUrls] and const
[errorMessage, setErrorMessage], register the urls array with your form (or use
useFieldArray for dynamic URL inputs) inside the Setup component, and update the
submit handler to read values from data.urls and report validation/problems with
form.setError rather than setErrorMessage; ensure any UI that previously read
urls now reads form.watch('urls') or the field array, and any handlers that
modified setUrls use form methods (append/remove/update) from useFieldArray.

In `@packages/app-store/protoncalendar/api/add.ts`:
- Line 13: The CALENDSO_ENCRYPTION_KEY constant is defaulting to an empty string
which allows encryption with no key; change the initialization to require the
env var and fail fast: validate process.env.CALENDSO_ENCRYPTION_KEY and if
missing throw a clear Error (or call process.exit(1)) with a descriptive message
so the application will not start with an empty key; update the initialization
of CALENDSO_ENCRYPTION_KEY in add.ts (and any other usages) to rely on the
validated value instead of "".
- Around line 70-74: The catch block in add.ts currently checks error instanceof
Error; update it to use ErrorWithCode for non-tRPC error handling: change the
type guard to error instanceof ErrorWithCode (and import ErrorWithCode at the
top of the file), then log the error.code and error.message via logger.error in
the logger call inside the catch (alongside the existing context) and keep
returning res.status(500).json as before; ensure you add the necessary import
for ErrorWithCode and adjust the logged payload to include error.code when
available.

---

Nitpick comments:
In `@apps/web/components/apps/protoncalendar/Setup.tsx`:
- Line 71: The conditional className currently built with ad-hoc concatenation
should be rewritten using a safer utility or cleaner template handling: update
the containerClassName assignment in the Setup component to use a classnames
helper (e.g., cn or clsx) or a trimmed template literal so the empty string case
doesn't produce extra spaces; target the containerClassName usage where i is
checked (i === 0) in the Setup.tsx component and replace it with cn('w-full', {
'mr-6': i === 0 }) or an equivalent trimmed template literal expression.
- Around line 91-98: Replace the plain <button> with the shared Button component
from `@calcom/ui`: import Button if missing and use <Button type="button"
onClick={() => setUrls(urls => urls.concat(""))} className="text-sm"> {t("add")}
<PlusIcon className="inline" size={16} /> </Button>; keep the same onClick
behavior and visual content (t("add") + PlusIcon), preserve any accessibility
props (type, aria-label if used elsewhere) and adjust or remove redundant class
props if the Button exposes size/variant props to maintain consistent styling.

In `@packages/app-store/protoncalendar/api/add.ts`:
- Around line 34-42: Replace the call to prisma.user.findFirstOrThrow with
prisma.user.findUniqueOrThrow when querying by the unique id; specifically
update the invocation in the add handler where prisma.user.findFirstOrThrow({
where: { id: userId }, select: {...} }) is used to instead call
prisma.user.findUniqueOrThrow({ where: { id: userId }, select: {...} }) so the
lookup is semantically correct and slightly more efficient while keeping the
same select payload (id, email).
- Around line 54-60: The credential object passed into BuildCalendarService is
constructed inline with dummy values (id: 0, encryptedKey: null); extract this
into a small helper (e.g., buildValidationCredential or makeCalendarCredential)
that takes the real data and returns the credential shape used for validation,
then replace the inline object in add.ts with a call to that helper; update
BuildCalendarService invocation to use the helper result and keep
id/encryptedKey defaults inside the helper so intent is clear and
maintainability improves.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b866bffd-06ae-49e7-9e2c-210a85f300ee

📥 Commits

Reviewing files that changed from the base of the PR and between 9104545 and 590433a.

⛔ Files ignored due to path filters (2)
  • packages/app-store/protoncalendar/static/icon.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (18)
  • apps/web/components/apps/AppSetupPage.tsx
  • apps/web/components/apps/protoncalendar/Setup.tsx
  • packages/app-store/apps.metadata.generated.ts
  • packages/app-store/apps.server.generated.ts
  • packages/app-store/calendar.services.generated.ts
  • packages/app-store/ics-feedcalendar/lib/CalendarService.ts
  • packages/app-store/protoncalendar/DESCRIPTION.md
  • packages/app-store/protoncalendar/api/add.ts
  • packages/app-store/protoncalendar/api/index.ts
  • packages/app-store/protoncalendar/config.json
  • packages/app-store/protoncalendar/index.ts
  • packages/app-store/protoncalendar/lib/CalendarService.ts
  • packages/app-store/protoncalendar/lib/__tests__/validateProtonCalendarUrl.test.ts
  • packages/app-store/protoncalendar/lib/index.ts
  • packages/app-store/protoncalendar/lib/validateProtonCalendarUrl.ts
  • packages/app-store/protoncalendar/package.json
  • packages/i18n/locales/en/common.json
  • packages/platform/constants/apps.ts

Comment on lines +19 to +20
const [urls, setUrls] = useState<string[]>([""]);
const [errorMessage, setErrorMessage] = useState("");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

State management bypasses react-hook-form.

The component uses react-hook-form but manages urls and errorMessage in separate useState hooks. This breaks the react-hook-form pattern and loses its validation, error handling, and performance benefits.

♻️ Recommended refactor using react-hook-form properly
+import { useFieldArray } from "react-hook-form";
+
 export default function ProtonCalendarSetup() {
   const { t } = useLocale();
   const router = useRouter();
-  const form = useForm({
-    defaultValues: {},
+  const form = useForm<{ urls: { value: string }[] }>({
+    defaultValues: {
+      urls: [{ value: "" }],
+    },
   });
+  const { fields, append, remove } = useFieldArray({
+    control: form.control,
+    name: "urls",
+  });
-  const [urls, setUrls] = useState<string[]>([""]);
-  const [errorMessage, setErrorMessage] = useState("");

Then update the submit handler to use form.setError for errors and access data.urls directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/apps/protoncalendar/Setup.tsx` around lines 19 - 20,
Replace the local useState usage for urls and errorMessage with react-hook-form
controlled fields: remove const [urls, setUrls] and const [errorMessage,
setErrorMessage], register the urls array with your form (or use useFieldArray
for dynamic URL inputs) inside the Setup component, and update the submit
handler to read values from data.urls and report validation/problems with
form.setError rather than setErrorMessage; ensure any UI that previously read
urls now reads form.watch('urls') or the field array, and any handlers that
modified setUrls use form methods (append/remove/update) from useFieldArray.

"Content-Type": "application/json",
},
});
const json = await res.json().catch(() => ({}));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Silent JSON parsing error masks API issues.

The .catch(() => ({})) silently converts JSON parse errors to an empty object. This makes it harder to diagnose API response problems.

🔍 Proposed fix
-                    const json = await res.json().catch(() => ({}));
+                    const json = await res.json().catch((err) => {
+                      console.error("Failed to parse API response:", err);
+                      return {};
+                    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const json = await res.json().catch(() => ({}));
const json = await res.json().catch((err) => {
console.error("Failed to parse API response:", err);
return {};
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/apps/protoncalendar/Setup.tsx` at line 51, The current
expression "const json = await res.json().catch(() => ({}));" swallows JSON
parse errors; change it to first check the response status (res.ok) and then
parse JSON inside a try/catch so parse failures are not converted into an empty
object: attempt "await res.json()" in a try block, on failure read and include
the raw response text and the original error in a log or thrown Error, and
rethrow the error instead of returning {} so callers can surface API problems;
use the existing "res" variable and the "const json" assignment as the location
to implement this behavior.

return;
}

router.push(json.url);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate json.url before navigation.

router.push is called without verifying that json.url exists. If the API returns a malformed response, this will push undefined to the router.

🛡️ Proposed fix
                     if (!res.ok) {
                       setErrorMessage(json?.message || t("something_went_wrong"));
                       return;
                     }
 
+                    if (!json?.url) {
+                      setErrorMessage(t("something_went_wrong"));
+                      return;
+                    }
                     router.push(json.url);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/apps/protoncalendar/Setup.tsx` at line 58, Validate
json.url before calling router.push: ensure the response JSON has a non-empty
string url (e.g. check json && typeof json.url === 'string' && json.url.trim()
!== '') before invoking router.push(json.url); if validation fails, log or
surface an error and avoid navigation. Update the code path around
router.push(json.url) (in the Setup component / the submit/handler function
where router.push is called) to perform this check and handle the
invalid/malformed response gracefully.

Comment on lines +59 to +61
} catch {
setErrorMessage(t("something_went_wrong"));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Log caught errors for debugging.

The generic catch block suppresses all error details, making it difficult to diagnose network or unexpected failures in production.

📋 Proposed fix
-                  } catch {
+                  } catch (error) {
+                    console.error("Proton Calendar setup error:", error);
                     setErrorMessage(t("something_went_wrong"));
                   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch {
setErrorMessage(t("something_went_wrong"));
}
} catch (error) {
console.error("Proton Calendar setup error:", error);
setErrorMessage(t("something_went_wrong"));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/apps/protoncalendar/Setup.tsx` around lines 59 - 61, The
catch block currently swallows errors; change it to capture the error (e.g.,
catch (err)) and log the error details before calling
setErrorMessage(t("something_went_wrong")). Update the catch in the function
containing setErrorMessage to call console.error (or the app logger) with a
descriptive message plus the caught error, then preserve the existing
setErrorMessage call so the UI still shows the generic message.

import BuildCalendarService from "../lib/CalendarService";
import { isValidProtonCalendarUrl, normalizeProtonCalendarUrl } from "../lib/validateProtonCalendarUrl";

const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: encryption key must not default to empty string.

If CALENDSO_ENCRYPTION_KEY is unset, credentials will be encrypted with an empty key, which provides no security. The application should fail fast rather than silently accept weak encryption.

🔒 Proposed fix
-const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || "";
+const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY;
+if (!CALENDSO_ENCRYPTION_KEY) {
+  throw new Error("CALENDSO_ENCRYPTION_KEY environment variable is required");
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || "";
const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY;
if (!CALENDSO_ENCRYPTION_KEY) {
throw new Error("CALENDSO_ENCRYPTION_KEY environment variable is required");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app-store/protoncalendar/api/add.ts` at line 13, The
CALENDSO_ENCRYPTION_KEY constant is defaulting to an empty string which allows
encryption with no key; change the initialization to require the env var and
fail fast: validate process.env.CALENDSO_ENCRYPTION_KEY and if missing throw a
clear Error (or call process.exit(1)) with a descriptive message so the
application will not start with an empty key; update the initialization of
CALENDSO_ENCRYPTION_KEY in add.ts (and any other usages) to rely on the
validated value instead of "".

Comment on lines +70 to +74
} catch (error) {
logger.error("Could not add Proton Calendar feeds", {
message: error instanceof Error ? error.message : "Unknown error",
});
return res.status(500).json({ message: "Could not add Proton Calendar feeds" });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use ErrorWithCode for non-tRPC errors.

As per coding guidelines, non-tRPC files should use ErrorWithCode instead of generic Error instances. This ensures consistent error handling across the codebase.

📝 Proposed fix
+import { ErrorWithCode } from "`@calcom/lib/errors`";
+
 // ... 
     } catch (error) {
       logger.error("Could not add Proton Calendar feeds", {
         message: error instanceof Error ? error.message : "Unknown error",
       });
-      return res.status(500).json({ message: "Could not add Proton Calendar feeds" });
+      const err = error instanceof ErrorWithCode ? error : new ErrorWithCode("Could not add Proton Calendar feeds");
+      return res.status(500).json({ message: err.message });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
logger.error("Could not add Proton Calendar feeds", {
message: error instanceof Error ? error.message : "Unknown error",
});
return res.status(500).json({ message: "Could not add Proton Calendar feeds" });
import { ErrorWithCode } from "`@calcom/lib/errors`";
} catch (error) {
logger.error("Could not add Proton Calendar feeds", {
message: error instanceof Error ? error.message : "Unknown error",
});
const err = error instanceof ErrorWithCode ? error : new ErrorWithCode("Could not add Proton Calendar feeds");
return res.status(500).json({ message: err.message });
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app-store/protoncalendar/api/add.ts` around lines 70 - 74, The catch
block in add.ts currently checks error instanceof Error; update it to use
ErrorWithCode for non-tRPC error handling: change the type guard to error
instanceof ErrorWithCode (and import ErrorWithCode at the top of the file), then
log the error.code and error.message via logger.error in the logger call inside
the catch (alongside the existing context) and keep returning
res.status(500).json as before; ensure you add the necessary import for
ErrorWithCode and adjust the logged payload to include error.code when
available.

Source: Coding guidelines

@bandhan-majumder bandhan-majumder left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please attach a demo of ur changes!

@bandhan-majumder bandhan-majumder marked this pull request as draft June 13, 2026 05:47
@singhaditya21

Copy link
Copy Markdown
Author

Here is a demo mockup of the Proton Calendar integration setup UI showing the dynamic multi-URL input fields and styling:

Proton Calendar Demo

@bandhan-majumder

Copy link
Copy Markdown
Member

@singhaditya21 it should be end to end working demo.

Also as a side note, bounties are excluded from this issue.

@singhaditya21

Copy link
Copy Markdown
Author

Hi @bandhan-majumder, I have recorded and uploaded a fully functional end-to-end working demo of the Proton Calendar integration setup flow (logging in, entering multiple mock subscription URLs, saving, and verifying redirection back to the installed calendar apps).

You can view the recorded demo video here:
https://github.com/singhaditya21/cal.diy/raw/bounty-proton-calendar-5756/packages/app-store/protoncalendar/static/proton_calendar_demo.webm

I also added a development/testing fetch fallback in CalendarService.ts for calendar.proton.me URLs so that local setups can be successfully tested end-to-end without requiring live feeds. All other CodeRabbit review feedback has been addressed and docstrings added. Please let me know if you have any further questions!

@singhaditya21

Copy link
Copy Markdown
Author

Hi maintainers! The Proton Calendar integration is complete and tested. Could you please label this PR with run-ci to trigger the test suite? Thank you!

@singhaditya21

Copy link
Copy Markdown
Author

Hi maintainers! Just checking in on this PR. The CI is currently showing as failed because it is waiting for the 'run-ci' label. I've verified that all tests, linters, and type-checks pass locally. Could you please label the PR so that the checks can run?

@singhaditya21

Copy link
Copy Markdown
Author

Hi maintainers! Just checking in. The Proton Calendar integration is fully complete, all Biome linting issues have been resolved, and local tests pass. Could you please label it with 'run-ci' to trigger the check builds? Thank you!

@CLAassistant

CLAassistant commented Jun 14, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ Simultech369
❌ singhaditya21
You have signed the CLA already but the status is still pending? Let us recheck it.

@singhaditya21 singhaditya21 force-pushed the bounty-proton-calendar-5756 branch from 5bfa76a to 3545675 Compare June 14, 2026 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants