mirror of
https://github.com/lone-cloud/prism
synced 2026-06-03 08:43:10 -07:00
more docker compose adventures, better logging, readme clarrifications
This commit is contained in:
parent
136ad038a5
commit
9921c98ffc
5 changed files with 78 additions and 51 deletions
|
|
@ -55,6 +55,8 @@ docker compose up -d
|
||||||
|
|
||||||
### 3. ProtonMail Integration (Optional)
|
### 3. ProtonMail Integration (Optional)
|
||||||
|
|
||||||
|
> **Note:** The default ProtonMail Bridge image uses `shenxn/protonmail-bridge:build` which compiles from source and supports multiple architectures. For x86_64 systems, you can use `shenxn/protonmail-bridge:latest` (pre-built binary, smaller and faster). For ARM devices (Raspberry Pi), stick with `:build`.
|
||||||
|
|
||||||
To receive ProtonMail notifications via Signal:
|
To receive ProtonMail notifications via Signal:
|
||||||
|
|
||||||
1. **Initialize ProtonMail Bridge** (one-time setup):
|
1. **Initialize ProtonMail Bridge** (one-time setup):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import chalk from 'chalk';
|
|
||||||
import { API_KEY, BRIDGE_IMAP_PASSWORD, BRIDGE_IMAP_USERNAME, PORT } from './constants/config';
|
import { API_KEY, BRIDGE_IMAP_PASSWORD, BRIDGE_IMAP_USERNAME, PORT } from './constants/config';
|
||||||
import { ROUTES } from './constants/server';
|
import { ROUTES } from './constants/server';
|
||||||
import { checkSignalCli, initSignal, startDaemon } from './modules/signal';
|
import { checkSignalCli, initSignal, startDaemon } from './modules/signal';
|
||||||
|
|
@ -13,6 +12,7 @@ import {
|
||||||
handleUnregister,
|
handleUnregister,
|
||||||
} from './routes/unifiedpush';
|
} from './routes/unifiedpush';
|
||||||
import { withAuth, withFormAuth } from './utils/auth';
|
import { withAuth, withFormAuth } from './utils/auth';
|
||||||
|
import { logError, logInfo, logSuccess, logWarn } from './utils/log';
|
||||||
|
|
||||||
let daemon: ReturnType<typeof Bun.spawn> | null = null;
|
let daemon: ReturnType<typeof Bun.spawn> | null = null;
|
||||||
|
|
||||||
|
|
@ -22,24 +22,28 @@ try {
|
||||||
const hasAccount = isLinked && (await initSignal({}));
|
const hasAccount = isLinked && (await initSignal({}));
|
||||||
|
|
||||||
if (hasAccount) {
|
if (hasAccount) {
|
||||||
console.log(chalk.green('✓ Signal account linked'));
|
logSuccess('✓ Signal account linked');
|
||||||
} else {
|
} else {
|
||||||
console.log(chalk.yellow('⚠ No Signal account linked'));
|
logWarn('⚠ No Signal account linked');
|
||||||
console.log(chalk.dim(` Visit http://localhost:${PORT}/link to link your device`));
|
logInfo(` Visit http://localhost:${PORT}/link to link your device`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(chalk.red('✗ Failed to start signal-cli daemon'));
|
logError(` ${error instanceof Error ? error.message : String(error)}`);
|
||||||
console.error(chalk.dim(` ${error instanceof Error ? error.message : String(error)}`));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!API_KEY) {
|
if (!API_KEY) {
|
||||||
console.warn(chalk.yellow('⚠️ Server running without API_KEY'));
|
logWarn('⚠️ Server running without API_KEY');
|
||||||
console.warn(chalk.dim(' Set API_KEY env var for production deployments.'));
|
console.warn(' Set API_KEY env var for production deployments.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BRIDGE_IMAP_USERNAME && BRIDGE_IMAP_PASSWORD) {
|
if (BRIDGE_IMAP_USERNAME && BRIDGE_IMAP_PASSWORD) {
|
||||||
const { startProtonMonitor } = await import('./modules/protonmail');
|
try {
|
||||||
await startProtonMonitor();
|
const { startProtonMonitor } = await import('./modules/protonmail');
|
||||||
|
await startProtonMonitor();
|
||||||
|
} catch (err) {
|
||||||
|
logError('❌ Failed to start ProtonMail monitor:', err);
|
||||||
|
logWarn('⚠️ Continuing without ProtonMail integration');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
|
|
@ -99,4 +103,4 @@ const server = Bun.serve({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(chalk.cyan.bold(`\n🚀 SUP running on http://localhost:${server.port}`));
|
logInfo(`\n🚀 SUP running on http://localhost:${server.port}`);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import chalk from 'chalk';
|
|
||||||
import Imap from 'imap';
|
import Imap from 'imap';
|
||||||
import {
|
import {
|
||||||
BRIDGE_IMAP_PASSWORD,
|
BRIDGE_IMAP_PASSWORD,
|
||||||
|
|
@ -9,32 +8,37 @@ import {
|
||||||
PROTON_BRIDGE_PORT,
|
PROTON_BRIDGE_PORT,
|
||||||
SUP_TOPIC,
|
SUP_TOPIC,
|
||||||
} from '../constants/config';
|
} from '../constants/config';
|
||||||
import { log } from '../utils/log';
|
import { logError, logInfo, logSuccess, logVerbose, logWarn } from '../utils/log';
|
||||||
import { createGroup, sendGroupMessage } from './signal';
|
import { createGroup, sendGroupMessage } from './signal';
|
||||||
import { getGroupId, register } from './store';
|
import { getGroupId, register } from './store';
|
||||||
|
|
||||||
export async function startProtonMonitor() {
|
export async function startProtonMonitor() {
|
||||||
if (!BRIDGE_IMAP_USERNAME || !BRIDGE_IMAP_PASSWORD) {
|
if (!BRIDGE_IMAP_USERNAME || !BRIDGE_IMAP_PASSWORD) {
|
||||||
console.error(
|
logError('Missing required env vars: BRIDGE_IMAP_USERNAME and BRIDGE_IMAP_PASSWORD');
|
||||||
chalk.red('Missing required env vars: BRIDGE_IMAP_USERNAME and BRIDGE_IMAP_PASSWORD'),
|
logWarn('Run: docker compose run --rm protonmail-bridge init');
|
||||||
);
|
logWarn('Then use `login` and `info` commands to get IMAP credentials');
|
||||||
console.error(chalk.yellow('Run: docker compose run --rm protonmail-bridge init'));
|
|
||||||
console.error(chalk.yellow('Then use `login` and `info` commands to get IMAP credentials'));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(chalk.blue(`🔗 Connecting to Proton Bridge at ${PROTON_BRIDGE_HOST}:${PROTON_BRIDGE_PORT}`));
|
logInfo(`🔗 Connecting to Proton Bridge at ${PROTON_BRIDGE_HOST}:${PROTON_BRIDGE_PORT}`);
|
||||||
log(chalk.blue(`📨 Monitoring mailbox: ${BRIDGE_IMAP_USERNAME}`));
|
logInfo(`📨 Monitoring mailbox: ${BRIDGE_IMAP_USERNAME}`);
|
||||||
|
|
||||||
const imap = new Imap({
|
let imap: Imap;
|
||||||
user: BRIDGE_IMAP_USERNAME,
|
try {
|
||||||
password: BRIDGE_IMAP_PASSWORD,
|
imap = new Imap({
|
||||||
host: PROTON_BRIDGE_HOST,
|
user: BRIDGE_IMAP_USERNAME,
|
||||||
port: PROTON_BRIDGE_PORT,
|
password: BRIDGE_IMAP_PASSWORD,
|
||||||
tls: true,
|
host: PROTON_BRIDGE_HOST,
|
||||||
tlsOptions: { rejectUnauthorized: false },
|
port: PROTON_BRIDGE_PORT,
|
||||||
keepalive: true,
|
tls: true,
|
||||||
});
|
tlsOptions: { rejectUnauthorized: false },
|
||||||
|
keepalive: true,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logError('❌ Failed to initialize IMAP client:', err);
|
||||||
|
logWarn('⚠️ ProtonMail integration disabled (bridge not reachable)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
async function sendNotification(title: string, message: string) {
|
async function sendNotification(title: string, message: string) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -50,23 +54,23 @@ export async function startProtonMonitor() {
|
||||||
: '';
|
: '';
|
||||||
await sendGroupMessage(groupId, `${prefix}**${title}**\n${message}`);
|
await sendGroupMessage(groupId, `${prefix}**${title}**\n${message}`);
|
||||||
|
|
||||||
console.log(chalk.green(`✅ Notification sent: ${title}`));
|
logSuccess(`✅ Notification sent: ${title}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(chalk.red('❌ Failed to send notification:'), error);
|
logError('❌ Failed to send notification:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openInbox() {
|
function openInbox() {
|
||||||
imap.openBox('INBOX', false, (err, box) => {
|
imap.openBox('INBOX', false, (err, box) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(chalk.red('Failed to open inbox:'), err);
|
logError('Failed to open inbox:', err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`✅ Connected to inbox (${box.messages.total} messages)`);
|
logVerbose(`✅ Connected to inbox (${box.messages.total} messages)`);
|
||||||
|
|
||||||
imap.on('mail', async (numNewMsgs: number) => {
|
imap.on('mail', async (numNewMsgs: number) => {
|
||||||
log(`📬 ${numNewMsgs} new message(s) received`);
|
logVerbose(`📬 ${numNewMsgs} new message(s) received`);
|
||||||
|
|
||||||
const fetch = imap.seq.fetch(`${box.messages.total}:*`, {
|
const fetch = imap.seq.fetch(`${box.messages.total}:*`, {
|
||||||
bodies: 'HEADER.FIELDS (FROM SUBJECT)',
|
bodies: 'HEADER.FIELDS (FROM SUBJECT)',
|
||||||
|
|
@ -91,26 +95,38 @@ export async function startProtonMonitor() {
|
||||||
});
|
});
|
||||||
|
|
||||||
imap.on('update', () => {
|
imap.on('update', () => {
|
||||||
log('📊 Mailbox updated');
|
logVerbose('📊 Mailbox updated');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
imap.once('ready', () => {
|
imap.once('ready', () => {
|
||||||
log('✅ IMAP connection ready');
|
logVerbose('✅ IMAP connection ready');
|
||||||
openInbox();
|
openInbox();
|
||||||
});
|
});
|
||||||
|
|
||||||
imap.once('error', (err: Error) => {
|
imap.once('error', (err: Error) => {
|
||||||
console.error(chalk.red('❌ IMAP error:'), err);
|
logError('❌ IMAP error:', err);
|
||||||
|
logWarn('⚠️ ProtonMail integration disabled due to connection error');
|
||||||
});
|
});
|
||||||
|
|
||||||
imap.once('end', () => {
|
imap.once('end', () => {
|
||||||
log('⚠️ IMAP connection ended, reconnecting...');
|
logVerbose('⚠️ IMAP connection ended, reconnecting...');
|
||||||
setTimeout(() => imap.connect(), 5000);
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
imap.connect();
|
||||||
|
} catch (err) {
|
||||||
|
logError('❌ Failed to reconnect:', err);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
});
|
});
|
||||||
|
|
||||||
imap.connect();
|
try {
|
||||||
|
imap.connect();
|
||||||
|
} catch (err) {
|
||||||
|
logError('❌ Failed to connect to Proton Bridge:', err);
|
||||||
|
logWarn('⚠️ ProtonMail integration disabled');
|
||||||
|
}
|
||||||
|
|
||||||
process.on('SIGTERM', () => {
|
process.on('SIGTERM', () => {
|
||||||
imap.end();
|
imap.end();
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import { rm } from 'node:fs/promises';
|
import { rm } from 'node:fs/promises';
|
||||||
import chalk from 'chalk';
|
|
||||||
import { DAEMON_START_MAX_ATTEMPTS, DEVICE_NAME, VERBOSE } from '../constants/config';
|
import { DAEMON_START_MAX_ATTEMPTS, DEVICE_NAME, VERBOSE } from '../constants/config';
|
||||||
import { SIGNAL_CLI, SIGNAL_CLI_DATA, SIGNAL_CLI_SOCKET } from '../constants/paths';
|
import { SIGNAL_CLI, SIGNAL_CLI_DATA, SIGNAL_CLI_SOCKET } from '../constants/paths';
|
||||||
import type { ListAccountsResult, StartLinkResult, UpdateGroupResult } from '../types';
|
import type { ListAccountsResult, StartLinkResult, UpdateGroupResult } from '../types';
|
||||||
import { log } from '../utils/log';
|
import { logError, logInfo, logSuccess, logVerbose, logWarn } from '../utils/log';
|
||||||
import { call } from '../utils/rpc';
|
import { call } from '../utils/rpc';
|
||||||
|
|
||||||
log(`Running signal-cli from ${SIGNAL_CLI}`);
|
logVerbose(`Running signal-cli from ${SIGNAL_CLI}`);
|
||||||
|
|
||||||
let account: string | null = null;
|
let account: string | null = null;
|
||||||
let currentLinkUri: string | null = null;
|
let currentLinkUri: string | null = null;
|
||||||
|
|
@ -141,19 +140,19 @@ export async function startDaemon() {
|
||||||
|
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
if (trimmed.includes('ERROR')) {
|
if (trimmed.includes('ERROR')) {
|
||||||
console.error(chalk.red('[signal-cli]'), trimmed);
|
logError('[signal-cli]', trimmed);
|
||||||
} else if (trimmed.includes('WARN')) {
|
} else if (trimmed.includes('WARN')) {
|
||||||
console.warn(chalk.yellow('[signal-cli]'), trimmed);
|
logWarn('[signal-cli]', trimmed);
|
||||||
} else {
|
} else {
|
||||||
console.log(chalk.dim('[signal-cli]'), trimmed);
|
logInfo('[signal-cli]', trimmed);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmed.includes('WARN')) {
|
if (trimmed.includes('WARN')) {
|
||||||
console.warn(chalk.yellow('[signal-cli]'), trimmed);
|
logWarn('[signal-cli]', trimmed);
|
||||||
} else if (trimmed.includes('ERROR') || !trimmed.includes('INFO')) {
|
} else if (trimmed.includes('ERROR') || !trimmed.includes('INFO')) {
|
||||||
console.error(chalk.red('[signal-cli]'), trimmed);
|
logError('[signal-cli]', trimmed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
@ -169,11 +168,11 @@ export async function startDaemon() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
socket.end();
|
socket.end();
|
||||||
console.log(chalk.green('✓ signal-cli daemon started'));
|
logSuccess('✓ signal-cli daemon started');
|
||||||
return proc;
|
return proc;
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
if (authError && attempts > 5 && !cleaned) {
|
if (authError && attempts > 5 && !cleaned) {
|
||||||
console.log(chalk.yellow('⚠ Detected stale account data, cleaning up and retrying...'));
|
logWarn('⚠ Detected stale account data, cleaning up and retrying...');
|
||||||
proc.kill();
|
proc.kill();
|
||||||
await unlinkDevice();
|
await unlinkDevice();
|
||||||
cleaned = true;
|
cleaned = true;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
import chalk from 'chalk';
|
||||||
import { VERBOSE } from '../constants/config';
|
import { VERBOSE } from '../constants/config';
|
||||||
|
|
||||||
export const log = (...args: unknown[]) => VERBOSE && console.log(...args);
|
export const logVerbose = (...args: unknown[]) => VERBOSE && console.log(...args);
|
||||||
|
|
||||||
|
export const logError = (...args: unknown[]) => console.error(chalk.red(...args));
|
||||||
|
export const logWarn = (...args: unknown[]) => console.warn(chalk.yellow(...args));
|
||||||
|
export const logInfo = (...args: unknown[]) => console.log(chalk.blue(...args));
|
||||||
|
export const logSuccess = (...args: unknown[]) => console.log(chalk.green(...args));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue