Add relay server for remote HTTPS access

Node.js/TypeScript relay server that enables remote access to MacroPad:
- WebSocket-based communication between desktop and relay
- Password authentication with bcrypt hashing
- Session management with consistent IDs
- REST API proxying to desktop app
- Web client WebSocket relay
- Login page and PWA-ready app page
- Designed for cloud-node-container deployment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-05 19:46:33 -08:00
parent 8e4c32fea4
commit 1d7f18018d
16 changed files with 1947 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
// Unique ID generation utilities
import { randomBytes } from 'crypto';
const BASE62_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
/**
* Generate a random base62 string of specified length.
* Uses cryptographically secure random bytes.
*/
export function generateSessionId(length: number = 6): string {
const bytes = randomBytes(length);
let result = '';
for (let i = 0; i < length; i++) {
result += BASE62_CHARS[bytes[i] % BASE62_CHARS.length];
}
return result;
}
/**
* Generate a UUID v4 for request IDs.
*/
export function generateRequestId(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}

View File

@@ -0,0 +1,36 @@
// Logger utility using Winston
import winston from 'winston';
import { config } from '../config';
const logFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.printf(({ level, message, timestamp, stack }) => {
return `${timestamp} [${level.toUpperCase()}]: ${stack || message}`;
})
);
export const logger = winston.createLogger({
level: config.logLevel,
format: logFormat,
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
logFormat
)
})
]
});
// Add file transport in production
if (!config.isDevelopment) {
logger.add(new winston.transports.File({
filename: 'error.log',
level: 'error'
}));
logger.add(new winston.transports.File({
filename: 'combined.log'
}));
}