mirror of
https://github.com/lone-cloud/prism
synced 2026-06-03 08:43:10 -07:00
clean code, improve readme
This commit is contained in:
parent
c6f702098d
commit
a6a8653455
8 changed files with 105 additions and 336 deletions
|
|
@ -1,3 +1,7 @@
|
|||
# Optional: Server port
|
||||
# Default: 8080
|
||||
# PORT=8080
|
||||
|
||||
# Optional: Protect endpoint registration with an API key (recommended for public deployments)
|
||||
# Default: unset (no authentication required)
|
||||
# API_KEY=your-secret-key-here
|
||||
|
|
@ -14,6 +18,7 @@
|
|||
# PROTON_BRIDGE_HOST=protonmail-bridge
|
||||
# PROTON_BRIDGE_PORT=143
|
||||
# SUP_TOPIC=Proton Mail
|
||||
|
||||
# Optional: Enable Android app integration for ProtonMail notifications
|
||||
# When enabled, sends special format that SUP Android app intercepts to show custom notifications
|
||||
# that open ProtonMail app on click. When disabled, ProtonMail notifications appear as plain Signal messages.
|
||||
|
|
|
|||
70
README.md
70
README.md
|
|
@ -1,8 +1,17 @@
|
|||
# SUP (Signal Unified Push)
|
||||
<div align="center">
|
||||
|
||||
Privacy-preserving push notifications using Signal as transport.
|
||||
<img src="assets/sup.webp" alt="SUP Icon" width="80" height="80" />
|
||||
|
||||
## What is SUP?
|
||||
# SUP
|
||||
|
||||
**SUP (Signal Unified Push) is a privacy-preserving push notifications using Signal as transport**
|
||||
|
||||
|
||||
[Setup](#setup) • [Architecture](#architecture)
|
||||
|
||||
</div>
|
||||
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
SUP is a UnifiedPush distributor that routes push notifications through Signal, allowing you to receive app notifications without exposing unique network fingerprints to your ISP or network observers. All notification traffic appears as regular Signal messages.
|
||||
|
||||
|
|
@ -12,9 +21,17 @@ Traditional push notification systems (ntfy, FCM) require persistent WebSocket c
|
|||
|
||||
## Setup
|
||||
|
||||
**⚠️ DOCKER COMPOSE REQUIRED**: The services must be deployed together using `docker compose`. Running individual Dockerfiles separately is not supported and will compromise security.
|
||||
### 1. Install Android App
|
||||
|
||||
### Quick Start with Docker Compose
|
||||
Download the latest APK from [GitHub Releases](https://github.com/lone-cloud/sup/releases).
|
||||
|
||||
**Certificate Fingerprint:**
|
||||
|
||||
```text
|
||||
0D:3C:99:15:0E:12:1A:DE:0D:AE:05:CB:16:46:5E:65:31:56:DC:D6:98:87:59:4E:79:B1:0D:AE:1E:56:F2:E8
|
||||
```
|
||||
|
||||
### 2. Start SUP Server with Docker Compose on a self-hosted server
|
||||
|
||||
**Without ProtonMail** (just UnifiedPush):
|
||||
|
||||
|
|
@ -22,18 +39,12 @@ Traditional push notification systems (ntfy, FCM) require persistent WebSocket c
|
|||
# Download docker-compose.yml
|
||||
curl -L -O https://raw.githubusercontent.com/lone-cloud/sup/master/docker-compose.yml
|
||||
|
||||
# Create .env file
|
||||
cat > .env << 'EOF'
|
||||
# Optional: API key for remote access
|
||||
# Set this to protect your server when accessing it from outside your home network
|
||||
# (e.g., registering UnifiedPush apps while away from home)
|
||||
# Default: unset (no authentication required)
|
||||
API_KEY=your-random-secret-key-here
|
||||
# Download .env.example (optional)
|
||||
curl -L -O https://raw.githubusercontent.com/lone-cloud/sup/master/.env.example
|
||||
|
||||
# Optional: Enable verbose logging
|
||||
# Default: false
|
||||
VERBOSE=false
|
||||
EOF
|
||||
# Configure (optional)
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
|
||||
# Start SUP server
|
||||
docker compose up -d
|
||||
|
|
@ -42,7 +53,7 @@ docker compose up -d
|
|||
# Visit http://localhost:8080/link and scan QR code with Signal app
|
||||
```
|
||||
|
||||
### ProtonMail Integration (Optional)
|
||||
### 3. ProtonMail Integration (Optional)
|
||||
|
||||
To receive ProtonMail notifications via Signal:
|
||||
|
||||
|
|
@ -68,7 +79,7 @@ To receive ProtonMail notifications via Signal:
|
|||
|
||||
```bash
|
||||
# Add these to your .env file
|
||||
BRIDGE_IMAP_USERNAME=your-email@proton.me
|
||||
BRIDGE_IMAP_USERNAME=bridge-username-from-info-command
|
||||
BRIDGE_IMAP_PASSWORD=bridge-generated-password-from-info-command
|
||||
```
|
||||
|
||||
|
|
@ -80,17 +91,6 @@ To receive ProtonMail notifications via Signal:
|
|||
|
||||
Your phone will now receive Signal notifications when ProtonMail receives new emails.
|
||||
|
||||
#### ProtonMail Android App Integration (Optional)
|
||||
|
||||
If you have the ProtonMail Android app installed, you can enable integration so that clicking on email notifications opens the ProtonMail app directly:
|
||||
|
||||
```bash
|
||||
# Add this to your .env file
|
||||
ENABLE_PROTON_ANDROID=true
|
||||
```
|
||||
|
||||
When enabled, the SUP Android app will intercept email notifications and show them as custom notifications that launch the ProtonMail app on click. When disabled, email notifications appear as regular Signal messages.
|
||||
|
||||
### Development
|
||||
|
||||
For local development, install Bun and signal-cli:
|
||||
|
|
@ -118,18 +118,6 @@ bun install
|
|||
bun --filter sup-server dev
|
||||
```
|
||||
|
||||
## Android App
|
||||
|
||||
Download the latest APK from [GitHub Releases](https://github.com/lone-cloud/sup/releases).
|
||||
|
||||
**Install via Obtainium:** [obtainium://add/https://github.com/lone-cloud/sup](obtainium://add/https://github.com/lone-cloud/sup)
|
||||
|
||||
**Certificate Fingerprint:**
|
||||
|
||||
```text
|
||||
0D:3C:99:15:0E:12:1A:DE:0D:AE:05:CB:16:46:5E:65:31:56:DC:D6:98:87:59:4E:79:B1:0D:AE:1E:56:F2:E8
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -984,197 +984,6 @@
|
|||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "8cvm889GbAY8ittddtEJB",
|
||||
"type": "rectangle",
|
||||
"x": 612.4082539876304,
|
||||
"y": 24.463267008463504,
|
||||
"width": 166.41015625,
|
||||
"height": 133.2421875,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "ar",
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1821620749,
|
||||
"version": 425,
|
||||
"versionNonce": 990282635,
|
||||
"isDeleted": true,
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "Lbesiv2QZE4qMpdejIPzY",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "RNCrlFh5EZBwnYr-7bA_j",
|
||||
"type": "arrow"
|
||||
}
|
||||
],
|
||||
"updated": 1768722055737,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "VFsafA6rMCSAeOHHPzZuL",
|
||||
"type": "text",
|
||||
"x": 630.0674293154764,
|
||||
"y": 52.289853050595184,
|
||||
"width": 139.55943298339844,
|
||||
"height": 75,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "dashed",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "at",
|
||||
"roundness": null,
|
||||
"seed": 210388483,
|
||||
"version": 182,
|
||||
"versionNonce": 738978149,
|
||||
"isDeleted": true,
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "RNCrlFh5EZBwnYr-7bA_j",
|
||||
"type": "arrow"
|
||||
}
|
||||
],
|
||||
"updated": 1768722056624,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "ProtonMail\nBridge\n(Optional)",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "ProtonMail\nBridge\n(Optional)",
|
||||
"autoResize": false,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "Lbesiv2QZE4qMpdejIPzY",
|
||||
"type": "arrow",
|
||||
"x": 623.8659261067706,
|
||||
"y": 56.516159369287976,
|
||||
"width": 139.52478027343722,
|
||||
"height": 1.795223729407006,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "b00",
|
||||
"roundness": null,
|
||||
"seed": 669405859,
|
||||
"version": 147,
|
||||
"versionNonce": 1201807467,
|
||||
"isDeleted": true,
|
||||
"boundElements": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "A2at5s2ss9zqWtu77JXsW"
|
||||
}
|
||||
],
|
||||
"updated": 1768722096436,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-69.76239013671864,
|
||||
0
|
||||
],
|
||||
[
|
||||
-69.76239013671864,
|
||||
-1.795223729407006
|
||||
],
|
||||
[
|
||||
-139.52478027343722,
|
||||
-1.795223729407006
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"elementId": "l68p6MrICi0j1MxyTO7FJ",
|
||||
"mode": "orbit",
|
||||
"fixedPoint": [
|
||||
-0.03605549165512547,
|
||||
0.33691893735853506
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "ubnB-MCtd3Y84dUGFa-b7",
|
||||
"mode": "orbit",
|
||||
"fixedPoint": [
|
||||
1.036055491655126,
|
||||
0.2969845457972111
|
||||
]
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true,
|
||||
"fixedSegments": null,
|
||||
"startIsSpecial": null,
|
||||
"endIsSpecial": null
|
||||
},
|
||||
{
|
||||
"id": "A2at5s2ss9zqWtu77JXsW",
|
||||
"type": "text",
|
||||
"x": 515.2700388574564,
|
||||
"y": 21.91319036172733,
|
||||
"width": 61.11991882324219,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "b01",
|
||||
"roundness": null,
|
||||
"seed": 1606941325,
|
||||
"version": 9,
|
||||
"versionNonce": 324972485,
|
||||
"isDeleted": true,
|
||||
"boundElements": [],
|
||||
"updated": 1768722096436,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Notify",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "Lbesiv2QZE4qMpdejIPzY",
|
||||
"originalText": "Notify",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "pDL0AmXVDpmeLiMPNhgXB",
|
||||
"type": "ellipse",
|
||||
|
|
@ -2640,10 +2449,10 @@
|
|||
{
|
||||
"id": "KsN6VJCu_Js44TB79mnZV",
|
||||
"type": "text",
|
||||
"x": 719.617652529762,
|
||||
"y": 551.1148158482142,
|
||||
"x": 759.0713678023156,
|
||||
"y": 546.2597602123432,
|
||||
"width": 392.6328125,
|
||||
"height": 275,
|
||||
"height": 250,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
|
|
@ -2657,20 +2466,20 @@
|
|||
"index": "b0Z",
|
||||
"roundness": null,
|
||||
"seed": 302706755,
|
||||
"version": 328,
|
||||
"versionNonce": 1529226149,
|
||||
"version": 383,
|
||||
"versionNonce": 310610591,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 1768724761730,
|
||||
"updated": 1768763155267,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "signal-cli:\nhttps://github.com/AsamK/signal-cli\n\nprotonmail bridge:\nhttps://github.com/ProtonMail/proton-\nbridge\n\nprotonmail bridge docker image:\nhttps://github.com/shenxn/protonmail-\nbridge-docker\n",
|
||||
"text": "signal-cli: \ngithub.com/AsamK/signal-cli\n\nprotonmail bridge:\ngithub.com/ProtonMail/proton-bridge\n\nprotonmail bridge docker image:\ngithub.com/shenxn/protonmail-bridge-\ndocker\n",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "signal-cli: https://github.com/AsamK/signal-cli\n\nprotonmail bridge: https://github.com/ProtonMail/proton-bridge\n\nprotonmail bridge docker image: https://github.com/shenxn/protonmail-bridge-docker\n",
|
||||
"originalText": "signal-cli: \ngithub.com/AsamK/signal-cli\n\nprotonmail bridge: github.com/ProtonMail/proton-bridge\n\nprotonmail bridge docker image: github.com/shenxn/protonmail-bridge-docker\n",
|
||||
"autoResize": false,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
|
|
@ -2838,7 +2647,7 @@
|
|||
"version": 6,
|
||||
"versionNonce": 1252528491,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"boundElements": [],
|
||||
"updated": 1768724671332,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
|
|
@ -2853,11 +2662,11 @@
|
|||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "1PI-IqUd4I8EvRfkkzFlI",
|
||||
"id": "Yu4GTjs9vKLTNlA076t3-",
|
||||
"type": "text",
|
||||
"x": 886.7487327938989,
|
||||
"y": 65.70355980282736,
|
||||
"width": 8,
|
||||
"x": 551.856965948593,
|
||||
"y": 625.0930346523853,
|
||||
"width": 78.7799072265625,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
|
|
@ -2871,21 +2680,21 @@
|
|||
"frameId": null,
|
||||
"index": "b0d",
|
||||
"roundness": null,
|
||||
"seed": 1872296133,
|
||||
"version": 3,
|
||||
"versionNonce": 878177861,
|
||||
"seed": 74100447,
|
||||
"version": 4,
|
||||
"versionNonce": 1570293599,
|
||||
"isDeleted": true,
|
||||
"boundElements": null,
|
||||
"updated": 1768722227703,
|
||||
"updated": 1768763176302,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "",
|
||||
"text": "https://",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "arkYz5xpoKLpnfgkI-VOo",
|
||||
"originalText": "",
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "https://",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,6 @@ export const ROUTES = {
|
|||
TOPICS: '/topics',
|
||||
} as const;
|
||||
|
||||
export const CONTENT_TYPE = {
|
||||
HTML: 'text/html',
|
||||
JSON: 'application/json',
|
||||
TEXT: 'text/plain',
|
||||
} as const;
|
||||
|
||||
export const TEMPLATES = {
|
||||
LINKED: 'templates/linked.html',
|
||||
LINK: 'templates/link.html',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import chalk from 'chalk';
|
||||
import { API_KEY, BRIDGE_IMAP_PASSWORD, BRIDGE_IMAP_USERNAME, PORT } from './constants/config';
|
||||
import { CONTENT_TYPE, ROUTES, TEMPLATES } from './constants/server';
|
||||
import { checkSignalCli, hasValidAccount, initSignal, startDaemon } from './modules/signal';
|
||||
import { ROUTES } from './constants/server';
|
||||
import { checkSignalCli, initSignal, startDaemon } from './modules/signal';
|
||||
import { handleHealth } from './routes/health';
|
||||
import { handleLink, handleLinkQR, handleLinkStatus, handleUnlink } from './routes/link';
|
||||
import { handleNotify, handleTopics } from './routes/notify';
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
handleRegister,
|
||||
handleUnregister,
|
||||
} from './routes/unifiedpush';
|
||||
import { checkAuth } from './utils/auth';
|
||||
import { withAuth, withFormAuth } from './utils/auth';
|
||||
|
||||
let daemon: ReturnType<typeof Bun.spawn> | null = null;
|
||||
|
||||
|
|
@ -60,13 +60,12 @@ const server = Bun.serve({
|
|||
},
|
||||
|
||||
[ROUTES.LINK_UNLINK]: {
|
||||
POST: async (req) => {
|
||||
const response = await handleUnlink(req, daemon);
|
||||
if (response.status === 303) {
|
||||
POST: withFormAuth(() =>
|
||||
handleUnlink(async () => {
|
||||
daemon?.kill();
|
||||
daemon = await startDaemon();
|
||||
}
|
||||
return response;
|
||||
},
|
||||
}),
|
||||
),
|
||||
},
|
||||
|
||||
[ROUTES.UP]: {
|
||||
|
|
@ -74,19 +73,11 @@ const server = Bun.serve({
|
|||
},
|
||||
|
||||
[ROUTES.ENDPOINTS]: {
|
||||
GET: (req) => {
|
||||
const auth = checkAuth(req);
|
||||
if (auth) return auth;
|
||||
return handleEndpoints();
|
||||
},
|
||||
GET: withAuth(handleEndpoints),
|
||||
},
|
||||
|
||||
[ROUTES.TOPICS]: {
|
||||
GET: (req) => {
|
||||
const auth = checkAuth(req);
|
||||
if (auth) return auth;
|
||||
return handleTopics();
|
||||
},
|
||||
GET: withAuth(handleTopics),
|
||||
},
|
||||
|
||||
[ROUTES.MATRIX_NOTIFY]: {
|
||||
|
|
@ -94,35 +85,14 @@ const server = Bun.serve({
|
|||
},
|
||||
|
||||
[ROUTES.UP_INSTANCE]: {
|
||||
POST: (req) => {
|
||||
const auth = checkAuth(req);
|
||||
if (auth) return auth;
|
||||
return handleRegister(req, new URL(req.url));
|
||||
},
|
||||
DELETE: (req) => {
|
||||
const auth = checkAuth(req);
|
||||
if (auth) return auth;
|
||||
return handleUnregister(new URL(req.url));
|
||||
},
|
||||
POST: withAuth((req) => handleRegister(req, new URL(req.url))),
|
||||
DELETE: withAuth((req) => handleUnregister(new URL(req.url))),
|
||||
},
|
||||
|
||||
[ROUTES.NOTIFY_TOPIC]: {
|
||||
POST: (req) => {
|
||||
const auth = checkAuth(req);
|
||||
if (auth) return auth;
|
||||
return handleNotify(req, new URL(req.url));
|
||||
},
|
||||
POST: withAuth((req) => handleNotify(req, new URL(req.url))),
|
||||
},
|
||||
},
|
||||
|
||||
async fetch(_req) {
|
||||
if (!(await hasValidAccount())) {
|
||||
const html = await Bun.file(TEMPLATES.SETUP).text();
|
||||
return new Response(html, { headers: { 'content-type': CONTENT_TYPE.HTML } });
|
||||
}
|
||||
|
||||
return new Response(null, { status: 404 });
|
||||
},
|
||||
});
|
||||
|
||||
console.log(chalk.cyan.bold(`\n🚀 SUP running on http://localhost:${server.port}`));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { API_KEY } from '../constants/config';
|
||||
import { CONTENT_TYPE, ROUTES, TEMPLATES } from '../constants/server';
|
||||
import { ROUTES, TEMPLATES } from '../constants/server';
|
||||
import {
|
||||
finishLink,
|
||||
generateLinkQR,
|
||||
|
|
@ -22,14 +22,14 @@ export const handleLink = async () => {
|
|||
}
|
||||
|
||||
return new Response(html, {
|
||||
headers: { 'content-type': CONTENT_TYPE.HTML },
|
||||
headers: { 'content-type': 'text/html' },
|
||||
});
|
||||
};
|
||||
|
||||
export const handleLinkQR = async () => {
|
||||
const qrDataUrl = await generateLinkQR();
|
||||
return new Response(qrDataUrl, {
|
||||
headers: { 'content-type': CONTENT_TYPE.TEXT },
|
||||
headers: { 'content-type': 'text/plain' },
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -47,24 +47,9 @@ export const handleLinkStatus = async () => {
|
|||
return Response.json({ linked });
|
||||
};
|
||||
|
||||
export const handleUnlink = async (req: Request, daemon: ReturnType<typeof Bun.spawn> | null) => {
|
||||
if (API_KEY) {
|
||||
const body = await req.text();
|
||||
const params = new URLSearchParams(body);
|
||||
const password = params.get('password');
|
||||
|
||||
if (password !== API_KEY) {
|
||||
return new Response(null, { status: 403 });
|
||||
}
|
||||
}
|
||||
|
||||
export const handleUnlink = async (killAndRestart: () => Promise<void>) => {
|
||||
await unlinkDevice();
|
||||
|
||||
if (daemon) {
|
||||
daemon.kill();
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
await killAndRestart();
|
||||
|
||||
return new Response('', {
|
||||
status: 303,
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SUP - Setup Required</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
</head>
|
||||
<body style="font-family: system-ui; max-width: 600px; margin: 50px auto; padding: 20px;">
|
||||
<h1>Setup Required</h1>
|
||||
<p>Please <a href="/link">link your Signal account</a> to use SUP.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { API_KEY } from '../constants/config';
|
||||
|
||||
export const checkAuth = (req: Request) => {
|
||||
const checkAuth = (req: Request) => {
|
||||
if (!API_KEY) return null;
|
||||
|
||||
const proto = req.headers.get('x-forwarded-proto') || 'http';
|
||||
|
|
@ -17,3 +17,33 @@ export const checkAuth = (req: Request) => {
|
|||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const checkFormAuth = async (req: Request) => {
|
||||
const bearerAuth = checkAuth(req);
|
||||
if (!bearerAuth) return null;
|
||||
|
||||
// Fall back to form password
|
||||
const body = await req.text();
|
||||
const params = new URLSearchParams(body);
|
||||
if (params.get('password') === API_KEY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Response(null, { status: 403 });
|
||||
};
|
||||
|
||||
export const withAuth =
|
||||
<T extends unknown[]>(handler: (req: Request, ...args: T) => Response | Promise<Response>) =>
|
||||
(req: Request, ...args: T) => {
|
||||
const auth = checkAuth(req);
|
||||
if (auth) return auth;
|
||||
return handler(req, ...args);
|
||||
};
|
||||
|
||||
export const withFormAuth =
|
||||
<T extends unknown[]>(handler: (req: Request, ...args: T) => Response | Promise<Response>) =>
|
||||
async (req: Request, ...args: T) => {
|
||||
const auth = await checkFormAuth(req);
|
||||
if (auth) return auth;
|
||||
return handler(req, ...args);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue