diff --git a/bun.lock b/bun.lock index b0e3a56..fc44dac 100644 --- a/bun.lock +++ b/bun.lock @@ -6,12 +6,12 @@ "name": "sup", "dependencies": { "chalk": "5.6.2", - "hono": "4.11.5", + "hono": "4.11.7", "hono-rate-limiter": "0.5.3", "imap": "0.8.19", }, "devDependencies": { - "@biomejs/biome": "2.3.12", + "@biomejs/biome": "2.3.13", "@types/bun": "1.3.6", "@types/imap": "0.8.43", "typescript": "5.9.3", @@ -19,23 +19,23 @@ }, }, "packages": { - "@biomejs/biome": ["@biomejs/biome@2.3.12", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.12", "@biomejs/cli-darwin-x64": "2.3.12", "@biomejs/cli-linux-arm64": "2.3.12", "@biomejs/cli-linux-arm64-musl": "2.3.12", "@biomejs/cli-linux-x64": "2.3.12", "@biomejs/cli-linux-x64-musl": "2.3.12", "@biomejs/cli-win32-arm64": "2.3.12", "@biomejs/cli-win32-x64": "2.3.12" }, "bin": { "biome": "bin/biome" } }, "sha512-AR7h4aSlAvXj7TAajW/V12BOw2EiS0AqZWV5dGozf4nlLoUF/ifvD0+YgKSskT0ylA6dY1A8AwgP8kZ6yaCQnA=="], + "@biomejs/biome": ["@biomejs/biome@2.3.13", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.13", "@biomejs/cli-darwin-x64": "2.3.13", "@biomejs/cli-linux-arm64": "2.3.13", "@biomejs/cli-linux-arm64-musl": "2.3.13", "@biomejs/cli-linux-x64": "2.3.13", "@biomejs/cli-linux-x64-musl": "2.3.13", "@biomejs/cli-win32-arm64": "2.3.13", "@biomejs/cli-win32-x64": "2.3.13" }, "bin": { "biome": "bin/biome" } }, "sha512-Fw7UsV0UAtWIBIm0M7g5CRerpu1eKyKAXIazzxhbXYUyMkwNrkX/KLkGI7b+uVDQ5cLUMfOC9vR60q9IDYDstA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cO6fn+KiMBemva6EARDLQBxeyvLzgidaFRJi8G7OeRqz54kWK0E+uSjgFaiHlc3DZYoa0+1UFE8mDxozpc9ieg=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0OCwP0/BoKzyJHnFdaTk/i7hIP9JHH9oJJq6hrSCPmJPo8JWcJhprK4gQlhFzrwdTBAW4Bjt/RmCf3ZZe59gwQ=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-/fiF/qmudKwSdvmSrSe/gOTkW77mHHkH8Iy7YC2rmpLuk27kbaUOPa7kPiH5l+3lJzTUfU/t6x1OuIq/7SGtxg=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-AGr8OoemT/ejynbIu56qeil2+F2WLkIjn2d8jGK1JkchxnMUhYOfnqc9sVzcRxpG9Ycvw4weQ5sprRvtb7Yhcw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-nbOsuQROa3DLla5vvsTZg+T5WVPGi9/vYxETm9BOuLHBJN3oWQIg3MIkE2OfL18df1ZtNkqXkH6Yg9mdTPem7A=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-xvOiFkrDNu607MPMBUQ6huHmBG1PZLOrqhtK6pXJW3GjfVqJg0Z/qpTdhXfcqWdSZHcT+Nct2fOgewZvytESkw=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-aqkeSf7IH+wkzFpKeDVPSXy9uDjxtLpYA6yzkYsY+tVjwFFirSuajHDI3ul8en90XNs1NA0n8kgBrjwRi5JeyA=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-TUdDCSY+Eo/EHjhJz7P2GnWwfqet+lFxBZzGHldrvULr59AgahamLs/N85SC4+bdF86EhqDuuw9rYLvLFWWlXA=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.12", "", { "os": "linux", "cpu": "x64" }, "sha512-CQtqrJ+qEEI8tgRSTjjzk6wJAwfH3wQlkIGsM5dlecfRZaoT+XCms/mf7G4kWNexrke6mnkRzNy6w8ebV177ow=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.13", "", { "os": "linux", "cpu": "x64" }, "sha512-s+YsZlgiXNq8XkgHs6xdvKDFOj/bwTEevqEY6rC2I3cBHbxXYU1LOZstH3Ffw9hE5tE1sqT7U23C00MzkXztMw=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.12", "", { "os": "linux", "cpu": "x64" }, "sha512-kVGWtupRRsOjvw47YFkk5mLiAdpCPMWBo1jOwAzh+juDpUb2sWarIp+iq+CPL1Wt0LLZnYtP7hH5kD6fskcxmg=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.13", "", { "os": "linux", "cpu": "x64" }, "sha512-0bdwFVSbbM//Sds6OjtnmQGp4eUjOTt6kHvR/1P0ieR9GcTUAlPNvPC3DiavTqq302W34Ae2T6u5VVNGuQtGlQ=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-Re4I7UnOoyE4kHMqpgtG6UvSBGBbbtvsOvBROgCCoH7EgANN6plSQhvo2W7OCITvTp7gD6oZOyZy72lUdXjqZg=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-QweDxY89fq0VvrxME+wS/BXKmqMrOTZlN9SqQ79kQSIc3FrEwvW/PvUegQF6XIVaekncDykB5dzPqjbwSKs9DA=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.12", "", { "os": "win32", "cpu": "x64" }, "sha512-qqGVWqNNek0KikwPZlOIoxtXgsNGsX+rgdEzgw82Re8nF02W+E2WokaQhpF5TdBh/D/RQ3TLppH+otp6ztN0lw=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.13", "", { "os": "win32", "cpu": "x64" }, "sha512-trDw2ogdM2lyav9WFQsdsfdVy1dvZALymRpgmWsvSez0BJzBjulhOT/t+wyKeh3pZWvwP3VMs1SoOKwO3wecMQ=="], "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="], @@ -49,7 +49,7 @@ "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], - "hono": ["hono@4.11.5", "", {}, "sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g=="], + "hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="], "hono-rate-limiter": ["hono-rate-limiter@0.5.3", "", { "peerDependencies": { "hono": "^4.10.8", "unstorage": "^1.17.3" }, "optionalPeers": ["unstorage"] }, "sha512-M0DxbVMpPELEzLi0AJg1XyBHLGJXz7GySjsPoK+gc5YeeBsdGDGe+2RvVuCAv8ydINiwlbxqYMNxUEyYfRji/A=="], diff --git a/package.json b/package.json index 18e8283..c8c73c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sup", - "version": "0.1.3", + "version": "0.1.4", "description": "Privacy-preserving push notifications using Signal as transport", "private": true, "type": "module", @@ -23,12 +23,12 @@ }, "dependencies": { "chalk": "5.6.2", - "hono": "4.11.5", + "hono": "4.11.7", "hono-rate-limiter": "0.5.3", "imap": "0.8.19" }, "devDependencies": { - "@biomejs/biome": "2.3.12", + "@biomejs/biome": "2.3.13", "@types/bun": "1.3.6", "@types/imap": "0.8.43", "typescript": "5.9.3" diff --git a/scripts/release.ts b/scripts/release.ts index b93e339..31e5f62 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -27,28 +27,11 @@ try { Pull the latest version: \`\`\`bash docker pull ghcr.io/lone-cloud/sup:${version} -\`\`\` - -Or use in your \`docker-compose.yml\`: -\`\`\`yaml -services: - server: - image: ghcr.io/lone-cloud/sup:${version} -\`\`\` - -### Architectures -- linux/amd64 -- linux/arm64 - -### Changes -See commit history for details." --generate-notes`; +\`\`\`" --generate-notes`; console.log(` ✨ Release ${version} complete! -GitHub release: https://github.com/lone-cloud/sup/releases/tag/${version} -GitHub Actions: https://github.com/lone-cloud/sup/actions - Once CI completes, images will be available: docker pull ghcr.io/lone-cloud/sup:${version} `); diff --git a/server/index.ts b/server/index.ts index f0de5bc..dd92a11 100644 --- a/server/index.ts +++ b/server/index.ts @@ -16,6 +16,7 @@ import { PUBLIC_DIR } from '@/constants/paths'; import { cleanupDaemon, initSignal } from '@/modules/signal'; import { admin } from '@/routes/admin'; import { ntfy } from '@/routes/ntfy'; +import { protonMail } from '@/routes/proton-mail'; import { getLanIP, isLocalIP } from '@/utils/auth'; import { formatToCspString } from '@/utils/format'; import { logError, logInfo, logVerbose, logWarn } from '@/utils/log'; @@ -80,6 +81,7 @@ app.use('*', serveStatic({ root: PUBLIC_DIR })); app.route('/', ntfy); app.route('/', admin); +app.route('/', protonMail); app.notFound((c) => c.text('Not Found', 404)); diff --git a/server/modules/proton-mail.ts b/server/modules/proton-mail.ts index df0c58c..1d06ca8 100644 --- a/server/modules/proton-mail.ts +++ b/server/modules/proton-mail.ts @@ -14,6 +14,7 @@ let imapConnected = false; let monitorStartTime = 0; let reconnectAttempts = 0; const MAX_RECONNECT_DELAY = 300000; // 5 minutes +let imapInstance: Imap | null = null; export const isImapConnected = () => imapConnected; @@ -167,4 +168,28 @@ export async function startProtonMonitor() { process.on('SIGTERM', () => imap.end()); process.on('SIGINT', () => imap.end()); + + imapInstance = imap; +} + +export async function markEmailAsRead(uid: number) { + if (!imapInstance || !imapConnected) { + return { success: false, error: 'IMAP not connected' }; + } + + return new Promise<{ success: boolean; error?: string }>((resolve) => { + try { + imapInstance?.addFlags(uid, '\\Seen', (err) => { + if (err) { + logError('Failed to mark email as read:', err); + resolve({ success: false, error: err.message }); + } else { + resolve({ success: true }); + } + }); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + resolve({ success: false, error: errorMessage }); + } + }); } diff --git a/server/routes/proton-mail.ts b/server/routes/proton-mail.ts new file mode 100644 index 0000000..d031c61 --- /dev/null +++ b/server/routes/proton-mail.ts @@ -0,0 +1,39 @@ +import { Hono } from 'hono'; +import { basicAuth } from 'hono/basic-auth'; +import { markEmailAsRead } from '@/modules/proton-mail'; +import { verifyApiKey } from '@/utils/auth'; +import { logError, logVerbose } from '@/utils/log'; + +export const protonMail = new Hono(); + +protonMail.use( + '*', + basicAuth({ + verifyUser: (_, password, c) => verifyApiKey(password, c), + realm: 'SUP Proton Mail - Username: any, Password: API_KEY', + }), +); + +protonMail.post('/api/proton-mail/mark-read', async (c) => { + try { + const body = await c.req.json(); + const { uid } = body; + + if (!uid || typeof uid !== 'number') { + return c.json({ error: 'uid (number) is required' }, 400); + } + + const result = await markEmailAsRead(uid); + + if (!result.success) { + return c.json({ error: result.error }, 500); + } + + logVerbose(`Marked email as read: UID ${uid}`); + + return c.json({ success: true }); + } catch (error) { + logError('Failed to mark email as read:', error); + return c.json({ error: 'Internal server error' }, 500); + } +});