One Calendar is a privacy-first calendar web app built with Next.js. It has modern security features, including e2ee, password-protected sharing, and self-destructing share links ๐Ÿ“… calendar.xyehr.cn
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(env): simplify ATProto config to base URL and session secret

+182 -230
+1 -13
.env.example
··· 1 1 # Required 2 2 NEXT_PUBLIC_BASE_URL=http://localhost:3000 3 - NEXT_PUBLIC_APP_URL=http://localhost:3000 4 3 SALT=Backup-Salt 5 4 6 5 # Auth (Required) ··· 8 7 CLERK_SECRET_KEY=your-clerk-secret 9 8 10 9 # ATProto / Atmosphere (Required if enabling ATProto login) 11 - # Preferred: key rotation format (first key is active): kid:secret,kid_old:secret_old 12 - ATPROTO_SESSION_KEYS=v1:replace-with-strong-random-secret 13 - # Alternative rotation envs (used when ATPROTO_SESSION_KEYS is empty) 14 - ATPROTO_SESSION_SECRET_CURRENT= 15 - ATPROTO_SESSION_SECRET_PREVIOUS= 16 - # Legacy fallback secret (used only when rotation envs above are empty) 17 - ATPROTO_SESSION_SECRET= 18 - # Optional explicit OAuth client_id. If empty, app uses: ${NEXT_PUBLIC_APP_URL}/oauth-client-metadata.json 19 - ATPROTO_CLIENT_ID= 20 - 21 - # Optional fallback secret for legacy compatibility 22 - NEXTAUTH_SECRET= 10 + ATPROTO_SESSION_SECRET=replace-with-strong-random-secret 23 11 24 12 # Optional, database 25 13 POSTGRES_URL=postgres://postgres:postgres@localhost:5432/onecalendar
+170 -176
README.md
··· 1 - # One Calendar 2 - 3 - > A privacy-first, weekly-focused open-source calendar built for clarity and control. 4 - 5 - <picture> 6 - <source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/cb179685-f792-42c8-bad8-ef1739659906"> 7 - <source media="(prefers-color-scheme: light)" srcset="/public/Banner.jpg"> 8 - <img src="/public/Banner.jpg" alt="Image"> 9 - </picture> 10 - 11 - - [Live Product](https://calendar.xyehr.cn) 12 - - [Status](https://calendarstatus.xyehr.cn) 13 - - [Bluesky](https://bsky.app/profile/calendar.xyehr.cn) 14 - 15 - <a href="https://vercel.com/new/clone?repository-url=https://github.com/EvanTechDev/One-Calendar&env=NEXT_PUBLIC_BASE_URL,NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,CLERK_SECRET_KEY,POSTGRES_URL,SALT&project-name=one-calendar&repo-name=one-calendar" style="display: inline-block;"><img src="https://vercel.com/button" alt="Deploy with Vercel" style="height: 32px;"></a> 16 - 17 - <a href="https://producthunt.com/product/one-calendar"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=955482&theme=light&t=1748791250175"></img></a> 18 - 19 - ## Vision 20 - 21 - Most modern calendar tools are overloaded with automation, notifications, and analytics. 22 - **One Calendar** takes a different approach: 23 - 24 - - Respect user privacy. 25 - - Provide a smooth, local-first planning experience. 26 - - Keep the system understandable. 27 - 28 - This project is built for individuals and small teams who value clarity over complexity. 29 - 30 - ## Features 31 - 32 - ### Weekly planning 33 - 34 - - **Drag & drop scheduling** &ndash; move and resize events directly on the calendar 35 - - **Inline editing** &ndash; create and update events without modal overload 36 - - **Right-click actions** &ndash; fast contextual controls for power users 37 - - **Keyboard-friendly interactions** &ndash; efficient navigation and editing workflows 38 - 39 - ### Event management 40 - 41 - - **Rich event metadata** &ndash; title, description, time range, and structured fields 42 - - **Precise time control** &ndash; flexible duration handling and adjustments 43 - - **Instant updates** &ndash; optimistic UI for a responsive experience 44 - - **Event persistence** &ndash; reliable storage with PostgreSQL backend 45 - - **Soft state handling** &ndash; controlled updates to avoid accidental data loss 46 - 47 - ### Privacy & security 48 - 49 - - **No AI tracking** &ndash; no behavioral profiling or data mining 50 - - **No analytics by default** &ndash; zero third-party tracking scripts 51 - - **End-to-end encryption (E2EE)** &ndash; optional encrypted data handling 52 - - **User-controlled exports** &ndash; backup and portability without lock-in 53 - - **Secure authentication** &ndash; hardened session management via Clerk 54 - 55 - ### Sync & collaboration 56 - 57 - - **Cloud sync (optional)** &ndash; multi-device synchronization using PostgreSQL 58 - - **Account-based access** &ndash; sign in with third-party providers 59 - - **Share-ready architecture** &ndash; designed for future team and shared calendar support 60 - 61 - ### Customization & UX 62 - 63 - - **Theme configuration** &ndash; adaptable visual styling 64 - - **Default view control** &ndash; choose how your calendar opens 65 - - **Locale-aware formatting** &ndash; proper date and time formatting per region 66 - - **Internationalization (i18n)** &ndash; language support built-in 67 - - **Composable UI system** &ndash; built with reusable components (shadcn/ui + Tailwind) 68 - 69 - ### Comparison with other calendar tools 70 - 71 - | Feature | One Calendar | Google Calendar | Apple Calendar | Outlook Calendar | Proton Calendar | 72 - | -------------------------------------------- | :----------: | :-------------: | :------------: | :--------------: | :-------------: | 73 - | Event creation & editing | โœ… | โœ… | โœ… | โœ… | โœ… | 74 - | Drag & drop scheduling | โœ… | โœ… | โœ… | โœ… | โœ… | 75 - | All-day events | โœ… | โœ… | โœ… | โœ… | โœ… | 76 - | Event reminders & notifications | โœ… | โœ… | โœ… | โœ… | โœ… | 77 - | Time zone support | โœ… | โœ… | โœ… | โœ… | โœ… | 78 - | Calendar sharing | โœ… | โœ… | โœ… | โœ… | โœ… | 79 - | Multiple calendar views (day/week/month) | โœ… | โœ… | โœ… | โœ… | โœ… | 80 - | Keyboard shortcuts | โœ… | โœ… | โš ๏ธ | โœ… | โœ… | 81 - | Search events | โœ… | โœ… | โœ… | โœ… | โœ… | 82 - | Quick add / natural input | โœ… | โœ… | โš ๏ธ | โœ… | โš ๏ธ | 83 - | Cloud sync | โœ… | โœ… | โœ… | โœ… | โœ… | 84 - | Web application | โœ… | โœ… | โš ๏ธ | โœ… | โœ… | 85 - | End-to-end encryption (E2EE) | โœ… | โŒ | โŒ | โŒ | โœ… | 86 - | Privacy-first architecture | โœ… | โŒ | โš ๏ธ | โŒ | โœ… | 87 - | No analytics / tracking by default | โœ… | โŒ | โš ๏ธ | โŒ | โœ… | 88 - | Open-source | โœ… | โŒ | โŒ | โŒ | โš ๏ธ | 89 - | Self-hostable | โœ… | โŒ | โŒ | โŒ | โŒ | 90 - | Data export | โœ… | โœ… | โœ… | โœ… | โœ… | 91 - | ICS import / export | โœ… | โœ… | โœ… | โœ… | โœ… | 92 - | Custom themes | โœ… | โš ๏ธ | โŒ | โš ๏ธ | โš ๏ธ | 93 - | Custom default view | โœ… | โš ๏ธ | โŒ | โš ๏ธ | โš ๏ธ | 94 - 95 - โš ๏ธ = limited or partial support 96 - 97 - ## Getting Started 98 - 99 - ### Prerequisites 100 - 101 - Required Versions: 102 - 103 - - [NodeJS](https://nodejs.org) (v20 or higher) 104 - - [Bun](https://bun.sh) (v1.2 or higher) 105 - 106 - ### Quick Start 107 - 108 - ```bash 109 - # Clone the repo 110 - git clone https://github.com/EvanTechDev/One-Calendar.git 111 - cd One-Calendar 112 - 113 - # Install dependencies 114 - bun install 115 - 116 - # Start the app 117 - bun run dev 118 - ``` 119 - 120 - Then visit `http://localhost:3000` 121 - 122 - ### Environment Variables 123 - 124 - Copy `.env.example` to `.env` and fill in. 125 - 126 - Key variables: 127 - 128 - ```env 129 - # Core 130 - NEXT_PUBLIC_BASE_URL=http://localhost:3000 131 - NEXT_PUBLIC_APP_URL=http://localhost:3000 132 - SALT=Backup-Salt 133 - 134 - # Clerk 135 - NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=... 136 - CLERK_SECRET_KEY=... 137 - 138 - # ATProto / Atmosphere (required for /at-oauth) 139 - ATPROTO_SESSION_KEYS=v1:...,v0:... # key rotation (first key is active) 140 - ATPROTO_SESSION_SECRET_CURRENT=... # optional alt rotation vars 141 - ATPROTO_SESSION_SECRET_PREVIOUS=... 142 - ATPROTO_SESSION_SECRET=... # legacy fallback 143 - ATPROTO_CLIENT_ID= # optional override; defaults to ${NEXT_PUBLIC_APP_URL}/oauth-client-metadata.json 144 - NEXTAUTH_SECRET= # optional legacy fallback 145 - 146 - # Optional DB (backup/share sync) 147 - POSTGRES_URL=postgres://postgres:postgres@localhost:5432/onecalendar 148 - ``` 149 - 150 - ## Tech Stack 151 - 152 - - [Next.js](https://nextjs.org) 153 - - [TypeScript](https://www.typescriptlang.org/) 154 - - [Tailwind CSS](https://tailwindcss.com/) 155 - - [Shadcn/UI](https://ui.shadcn.com) 156 - - [PostgreSQL](https://www.postgresql.org/) 157 - - [Clerk](https://clerk.com) 158 - 159 - ## Contributing 160 - 161 - Contributions are welcome! Feel free to explore the project and submit improvements. 162 - 163 - Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions and contribution guidelines. 164 - 165 - ## License 166 - 167 - Made with โค๏ธ 168 - 169 - Published under [MIT License](./LICENSE). 170 - 171 - This project is supported by [Cloudflare Project Alexandria](https://blog.cloudflare.com/expanding-our-support-for-oss-projects-with-project-alexandria/). 172 - 173 - ## Star History 174 - 175 - [![Star History Chart](https://api.star-history.com/svg?repos=EvanTechDev/One-Calendar&type=Date)](https://www.star-history.com/#EvanTechDev/One-Calendar&Date) 176 - 1 + # One Calendar 2 + 3 + > A privacy-first, weekly-focused open-source calendar built for clarity and control. 4 + 5 + <picture> 6 + <source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/cb179685-f792-42c8-bad8-ef1739659906"> 7 + <source media="(prefers-color-scheme: light)" srcset="/public/Banner.jpg"> 8 + <img src="/public/Banner.jpg" alt="Image"> 9 + </picture> 10 + 11 + - [Live Product](https://calendar.xyehr.cn) 12 + - [Status](https://calendarstatus.xyehr.cn) 13 + - [Bluesky](https://bsky.app/profile/calendar.xyehr.cn) 14 + 15 + <a href="https://vercel.com/new/clone?repository-url=https://github.com/EvanTechDev/One-Calendar&env=NEXT_PUBLIC_BASE_URL,NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,CLERK_SECRET_KEY,POSTGRES_URL,SALT&project-name=one-calendar&repo-name=one-calendar" style="display: inline-block;"><img src="https://vercel.com/button" alt="Deploy with Vercel" style="height: 32px;"></a> 16 + 17 + <a href="https://producthunt.com/product/one-calendar"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=955482&theme=light&t=1748791250175"></img></a> 18 + 19 + ## Vision 20 + 21 + Most modern calendar tools are overloaded with automation, notifications, and analytics. 22 + **One Calendar** takes a different approach: 23 + 24 + - Respect user privacy. 25 + - Provide a smooth, local-first planning experience. 26 + - Keep the system understandable. 27 + 28 + This project is built for individuals and small teams who value clarity over complexity. 29 + 30 + ## Features 31 + 32 + ### Weekly planning 33 + 34 + - **Drag & drop scheduling** &ndash; move and resize events directly on the calendar 35 + - **Inline editing** &ndash; create and update events without modal overload 36 + - **Right-click actions** &ndash; fast contextual controls for power users 37 + - **Keyboard-friendly interactions** &ndash; efficient navigation and editing workflows 38 + 39 + ### Event management 40 + 41 + - **Rich event metadata** &ndash; title, description, time range, and structured fields 42 + - **Precise time control** &ndash; flexible duration handling and adjustments 43 + - **Instant updates** &ndash; optimistic UI for a responsive experience 44 + - **Event persistence** &ndash; reliable storage with PostgreSQL backend 45 + - **Soft state handling** &ndash; controlled updates to avoid accidental data loss 46 + 47 + ### Privacy & security 48 + 49 + - **No AI tracking** &ndash; no behavioral profiling or data mining 50 + - **No analytics by default** &ndash; zero third-party tracking scripts 51 + - **End-to-end encryption (E2EE)** &ndash; optional encrypted data handling 52 + - **User-controlled exports** &ndash; backup and portability without lock-in 53 + - **Secure authentication** &ndash; hardened session management via Clerk 54 + 55 + ### Sync & collaboration 56 + 57 + - **Cloud sync (optional)** &ndash; multi-device synchronization using PostgreSQL 58 + - **Account-based access** &ndash; sign in with third-party providers 59 + - **Share-ready architecture** &ndash; designed for future team and shared calendar support 60 + 61 + ### Customization & UX 62 + 63 + - **Theme configuration** &ndash; adaptable visual styling 64 + - **Default view control** &ndash; choose how your calendar opens 65 + - **Locale-aware formatting** &ndash; proper date and time formatting per region 66 + - **Internationalization (i18n)** &ndash; language support built-in 67 + - **Composable UI system** &ndash; built with reusable components (shadcn/ui + Tailwind) 68 + 69 + ### Comparison with other calendar tools 70 + 71 + | Feature | One Calendar | Google Calendar | Apple Calendar | Outlook Calendar | Proton Calendar | 72 + | -------------------------------------------- | :----------: | :-------------: | :------------: | :--------------: | :-------------: | 73 + | Event creation & editing | โœ… | โœ… | โœ… | โœ… | โœ… | 74 + | Drag & drop scheduling | โœ… | โœ… | โœ… | โœ… | โœ… | 75 + | All-day events | โœ… | โœ… | โœ… | โœ… | โœ… | 76 + | Event reminders & notifications | โœ… | โœ… | โœ… | โœ… | โœ… | 77 + | Time zone support | โœ… | โœ… | โœ… | โœ… | โœ… | 78 + | Calendar sharing | โœ… | โœ… | โœ… | โœ… | โœ… | 79 + | Multiple calendar views (day/week/month) | โœ… | โœ… | โœ… | โœ… | โœ… | 80 + | Keyboard shortcuts | โœ… | โœ… | โš ๏ธ | โœ… | โœ… | 81 + | Search events | โœ… | โœ… | โœ… | โœ… | โœ… | 82 + | Quick add / natural input | โœ… | โœ… | โš ๏ธ | โœ… | โš ๏ธ | 83 + | Cloud sync | โœ… | โœ… | โœ… | โœ… | โœ… | 84 + | Web application | โœ… | โœ… | โš ๏ธ | โœ… | โœ… | 85 + | End-to-end encryption (E2EE) | โœ… | โŒ | โŒ | โŒ | โœ… | 86 + | Privacy-first architecture | โœ… | โŒ | โš ๏ธ | โŒ | โœ… | 87 + | No analytics / tracking by default | โœ… | โŒ | โš ๏ธ | โŒ | โœ… | 88 + | Open-source | โœ… | โŒ | โŒ | โŒ | โš ๏ธ | 89 + | Self-hostable | โœ… | โŒ | โŒ | โŒ | โŒ | 90 + | Data export | โœ… | โœ… | โœ… | โœ… | โœ… | 91 + | ICS import / export | โœ… | โœ… | โœ… | โœ… | โœ… | 92 + | Custom themes | โœ… | โš ๏ธ | โŒ | โš ๏ธ | โš ๏ธ | 93 + | Custom default view | โœ… | โš ๏ธ | โŒ | โš ๏ธ | โš ๏ธ | 94 + 95 + โš ๏ธ = limited or partial support 96 + 97 + ## Getting Started 98 + 99 + ### Prerequisites 100 + 101 + Required Versions: 102 + 103 + - [NodeJS](https://nodejs.org) (v20 or higher) 104 + - [Bun](https://bun.sh) (v1.2 or higher) 105 + 106 + ### Quick Start 107 + 108 + ```bash 109 + # Clone the repo 110 + git clone https://github.com/EvanTechDev/One-Calendar.git 111 + cd One-Calendar 112 + 113 + # Install dependencies 114 + bun install 115 + 116 + # Start the app 117 + bun run dev 118 + ``` 119 + 120 + Then visit `http://localhost:3000` 121 + 122 + ### Environment Variables 123 + 124 + Copy `.env.example` to `.env` and fill in. 125 + 126 + Key variables: 127 + 128 + ```env 129 + # Core 130 + NEXT_PUBLIC_BASE_URL=http://localhost:3000 131 + SALT=Backup-Salt 132 + 133 + # Clerk 134 + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=... 135 + CLERK_SECRET_KEY=... 136 + 137 + # ATProto / Atmosphere (required for /at-oauth) 138 + ATPROTO_SESSION_SECRET=... 139 + 140 + # Optional DB (backup/share sync) 141 + POSTGRES_URL=postgres://postgres:postgres@localhost:5432/onecalendar 142 + ``` 143 + 144 + ## Tech Stack 145 + 146 + - [Next.js](https://nextjs.org) 147 + - [TypeScript](https://www.typescriptlang.org/) 148 + - [Tailwind CSS](https://tailwindcss.com/) 149 + - [Shadcn/UI](https://ui.shadcn.com) 150 + - [PostgreSQL](https://www.postgresql.org/) 151 + - [Clerk](https://clerk.com) 152 + 153 + ## Contributing 154 + 155 + Contributions are welcome! Feel free to explore the project and submit improvements. 156 + 157 + Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions and contribution guidelines. 158 + 159 + ## License 160 + 161 + Made with โค๏ธ 162 + 163 + Published under [MIT License](./LICENSE). 164 + 165 + This project is supported by [Cloudflare Project Alexandria](https://blog.cloudflare.com/expanding-our-support-for-oss-projects-with-project-alexandria/). 166 + 167 + ## Star History 168 + 169 + [![Star History Chart](https://api.star-history.com/svg?repos=EvanTechDev/One-Calendar&type=Date)](https://www.star-history.com/#EvanTechDev/One-Calendar&Date) 170 +
+2 -2
app/api/atproto/callback/route.ts
··· 37 37 const state = request.nextUrl.searchParams.get("state"); 38 38 const iss = request.nextUrl.searchParams.get("iss"); 39 39 40 - const baseUrl = process.env.NEXT_PUBLIC_APP_URL || request.nextUrl.origin; 40 + const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || request.nextUrl.origin; 41 41 const txn = getAtprotoOAuthTxnFromRequest(request); 42 42 43 43 if (!code || !state || !txn || state !== txn.state) { ··· 67 67 return redirectWithError(baseUrl, "invalid_issuer", "issuer_mismatch"); 68 68 } 69 69 70 - const clientId = process.env.ATPROTO_CLIENT_ID || `${baseUrl}/oauth-client-metadata.json`; 70 + const clientId = `${baseUrl}/oauth-client-metadata.json`; 71 71 const redirectUri = `${baseUrl}/api/atproto/callback`; 72 72 const tokenUrl = `${issuerOrigin}/oauth/token`; 73 73
+2 -2
app/api/atproto/login/route.ts
··· 9 9 const loginRateCache = new Map<string, { count: number; resetAt: number }>(); 10 10 11 11 function getExpectedBaseUrl(request: NextRequest) { 12 - return process.env.NEXT_PUBLIC_APP_URL || request.nextUrl.origin; 12 + return process.env.NEXT_PUBLIC_BASE_URL || request.nextUrl.origin; 13 13 } 14 14 15 15 function isAllowedOrigin(request: NextRequest, expectedBaseUrl: string) { ··· 80 80 const dpop = generateDpopKeyMaterial(); 81 81 82 82 const redirectUri = `${expectedBaseUrl}/api/atproto/callback`; 83 - const clientId = process.env.ATPROTO_CLIENT_ID || `${expectedBaseUrl}/oauth-client-metadata.json`; 83 + const clientId = `${expectedBaseUrl}/oauth-client-metadata.json`; 84 84 85 85 const authUrl = new URL(`${pds.replace(/\/$/, "")}/oauth/authorize`); 86 86 authUrl.searchParams.set("client_id", clientId);
+2 -2
app/api/atproto/register-url/route.ts
··· 7 7 const ROSE_PDS_ORIGIN = "https://rose.madebydanny.uk"; 8 8 9 9 function getBaseUrl(request: NextRequest) { 10 - return process.env.NEXT_PUBLIC_APP_URL || request.nextUrl.origin; 10 + return process.env.NEXT_PUBLIC_BASE_URL || request.nextUrl.origin; 11 11 } 12 12 13 13 export async function POST(request: NextRequest) { 14 14 const baseUrl = getBaseUrl(request); 15 - const clientId = process.env.ATPROTO_CLIENT_ID || `${baseUrl}/oauth-client-metadata.json`; 15 + const clientId = `${baseUrl}/oauth-client-metadata.json`; 16 16 const authorizeUrl = new URL(`${ROSE_PDS_ORIGIN}/oauth/authorize`); 17 17 18 18 const { verifier, challenge } = createPkcePair();
+4 -34
lib/atproto-auth.ts
··· 25 25 return process.env.NODE_ENV === "production"; 26 26 } 27 27 28 - function getRawLegacySecret() { 29 - return process.env.ATPROTO_SESSION_SECRET || process.env.NEXTAUTH_SECRET || ""; 30 - } 31 - 32 28 export function getKeyEntries(): KeyEntry[] { 33 - const configured = process.env.ATPROTO_SESSION_KEYS?.trim(); 34 - if (configured) { 35 - const parsed = configured 36 - .split(",") 37 - .map((part) => part.trim()) 38 - .filter(Boolean) 39 - .map((pair) => { 40 - const idx = pair.indexOf(":"); 41 - if (idx <= 0 || idx === pair.length - 1) return null; 42 - return { 43 - kid: pair.slice(0, idx).trim(), 44 - secret: pair.slice(idx + 1).trim(), 45 - }; 46 - }) 47 - .filter((v): v is KeyEntry => !!v && !!v.kid && !!v.secret); 48 - 49 - if (parsed.length > 0) return parsed; 50 - } 51 - 52 - const current = process.env.ATPROTO_SESSION_SECRET_CURRENT?.trim(); 53 - const previous = process.env.ATPROTO_SESSION_SECRET_PREVIOUS?.trim(); 54 - const entries: KeyEntry[] = []; 55 - if (current) entries.push({ kid: "v1", secret: current }); 56 - if (previous) entries.push({ kid: "v0", secret: previous }); 57 - if (entries.length > 0) return entries; 58 - 59 - const legacy = getRawLegacySecret(); 60 - if (!legacy) return []; 61 - return [{ kid: "legacy", secret: legacy }]; 29 + const secret = process.env.ATPROTO_SESSION_SECRET?.trim(); 30 + if (!secret) return []; 31 + return [{ kid: "v1", secret }]; 62 32 } 63 33 64 34 function deriveKey(secret: string, kid: string) { ··· 129 99 const keys = getKeyEntries(); 130 100 const activeKey = keys[0]; 131 101 if (!activeKey) { 132 - throw new Error("Missing ATProto cookie key. Set ATPROTO_SESSION_KEYS or ATPROTO_SESSION_SECRET_CURRENT/ATPROTO_SESSION_SECRET"); 102 + throw new Error("Missing ATProto cookie key. Set ATPROTO_SESSION_SECRET"); 133 103 } 134 104 135 105 return sealJsonPayload(session, activeKey);
+1 -1
lib/gen-oauth-metadata.mjs
··· 1 1 import { writeFileSync } from "node:fs"; 2 2 3 - const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "https://calendar.xyehr.cn"; 3 + const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "https://calendar.xyehr.cn"; 4 4 const normalizedBase = baseUrl.replace(/\/$/, ""); 5 5 6 6 const metadata = {