mirror of
https://github.com/abort-retry-ignore/joplock.git
synced 2026-10-21 22:30:06 +00:00
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
- TinyMCE Enter at end of a non-empty checkbox now always inserts a new checkbox item, regardless of what follows (paragraph, another checkbox, or nothing). Exiting the list is done via Enter on an empty item. This fixes being unable to add items to an existing checklist that is followed by other text; only bottom-of-note appends worked before. - Restore rendered-mode title auto-fill broken by ffb7bd0: onEdit now calls a cheap DOM-only autoTitleFromTinyMCE() instead of relying on a per-edit synthetic textarea 'input' event. - Tests: rewrite checkbox-enter-behavior.spec.js (end-of-note, followed-by-text, empty-item exit); remove checkbox-exit.spec.js (encoded old exit-on-following-paragraph behavior); add autotitle-rendered.spec.js and tinymceOnEditSync auto-title guards.
131 lines
4.9 KiB
JavaScript
131 lines
4.9 KiB
JavaScript
const path = require('path');
|
|
const fs = require('fs');
|
|
const { createPoolFromEnv, createSessionService } = require('./app/auth/sessionService');
|
|
|
|
const { createItemService, ensureIndexes } = require('./app/items/itemService');
|
|
const { createItemWriteService } = require('./app/items/itemWriteService');
|
|
const { createSettingsService } = require('./app/settingsService');
|
|
const { createHistoryService } = require('./app/historyService');
|
|
const { createAdminService } = require('./app/adminService');
|
|
const { createVaultService } = require('./app/vaultService');
|
|
const { createBackupService } = require('./app/backupService');
|
|
const { createRecoveryService } = require('./app/recoveryService');
|
|
const { createRateLimitService } = require('./app/auth/rateLimitService');
|
|
const { createServer } = require('./app/createServer');
|
|
const { normalizeEnvValue } = require('./app/env');
|
|
|
|
const host = process.env.HOST || '0.0.0.0';
|
|
const port = Number(process.env.PORT || '3001');
|
|
const joplinServerOrigin = process.env.JOPLIN_SERVER_ORIGIN || 'http://server:22300';
|
|
const joplinPublicBasePath = process.env.JOPLIN_PUBLIC_BASE_PATH || '';
|
|
const joplinPublicBaseUrl = process.env.JOPLOCK_PUBLIC_BASE_URL || `http://localhost:${port}`;
|
|
const joplinServerPublicUrl = process.env.JOPLIN_SERVER_PUBLIC_URL || `${joplinPublicBaseUrl}${joplinPublicBasePath}`;
|
|
const publicDir = path.join(__dirname, 'public');
|
|
|
|
const adminEmail = normalizeEnvValue(process.env.JOPLOCK_ADMIN_EMAIL || '');
|
|
const adminPassword = normalizeEnvValue(process.env.JOPLOCK_ADMIN_PASSWORD || '');
|
|
const ignoreAdminMfa = process.env.IGNORE_ADMIN_MFA === 'true' || process.env.IGNORE_ADMIN_MFA === '1';
|
|
const backupDir = process.env.JOPLOCK_BACKUP_DIR || '';
|
|
const recoveryEnabled = process.env.JOPLOCK_RECOVERY_ENABLED === 'true' || process.env.JOPLOCK_RECOVERY_ENABLED === '1';
|
|
const recoveryPassword = normalizeEnvValue(process.env.JOPLOCK_RECOVERY_PASSWORD || '');
|
|
const recoverySessionTtlMinutes = Number(process.env.JOPLOCK_RECOVERY_SESSION_TTL_MINUTES || '30');
|
|
const backupCompression = process.env.JOPLOCK_BACKUP_COMPRESSION || 'zstd:19';
|
|
const backupCompressionLevel = Number(process.env.JOPLOCK_BACKUP_COMPRESSION_LEVEL || '9');
|
|
const sessionCookieMaxAge = Number(process.env.JOPLOCK_SESSION_COOKIE_MAX_AGE_SECONDS || '31536000');
|
|
const loginRateLimitService = createRateLimitService();
|
|
|
|
// Joplock version: env var → version.txt (Docker build) → package.json
|
|
let joplockVersion = '';
|
|
if (process.env.JOPLOCK_VERSION) {
|
|
joplockVersion = process.env.JOPLOCK_VERSION;
|
|
} else {
|
|
try {
|
|
const vPath = path.join(__dirname, 'version.txt');
|
|
if (fs.existsSync(vPath)) {
|
|
joplockVersion = fs.readFileSync(vPath, 'utf8').trim();
|
|
}
|
|
} catch {}
|
|
}
|
|
if (!joplockVersion) {
|
|
try { joplockVersion = require('./package.json').version; } catch {}
|
|
}
|
|
|
|
const databasePool = createPoolFromEnv(process.env);
|
|
const sessionService = createSessionService(databasePool);
|
|
const itemService = createItemService(databasePool);
|
|
const settingsService = createSettingsService(databasePool);
|
|
const historyService = createHistoryService(databasePool);
|
|
const vaultService = createVaultService(databasePool);
|
|
const backupService = createBackupService({
|
|
backupDir,
|
|
compression: backupCompression,
|
|
compressionLevel: backupCompressionLevel,
|
|
postgresConfig: {
|
|
host: process.env.POSTGRES_HOST || '127.0.0.1',
|
|
port: Number(process.env.POSTGRES_PORT || '5432'),
|
|
user: process.env.POSTGRES_USER || 'joplin',
|
|
password: process.env.POSTGRES_PASSWORD || 'joplin',
|
|
database: process.env.POSTGRES_DATABASE || 'joplin',
|
|
},
|
|
});
|
|
const recoveryService = createRecoveryService({
|
|
enabled: recoveryEnabled,
|
|
password: recoveryPassword,
|
|
sessionTtlMinutes: recoverySessionTtlMinutes,
|
|
});
|
|
const itemWriteService = createItemWriteService({
|
|
joplinServerOrigin,
|
|
joplinServerPublicUrl,
|
|
});
|
|
|
|
const adminService = adminEmail ? createAdminService({
|
|
database: databasePool,
|
|
joplinServerOrigin,
|
|
joplinServerPublicUrl,
|
|
adminEmail,
|
|
adminPassword,
|
|
}) : null;
|
|
|
|
// Bootstrap admin user (non-blocking, best-effort after server starts)
|
|
if (adminService) {
|
|
adminService.ensureAdminUser().catch(err => {
|
|
process.stderr.write(`[joplock] Admin bootstrap error: ${err.message}\n`);
|
|
});
|
|
}
|
|
|
|
// Create DB indexes (non-blocking, best-effort)
|
|
ensureIndexes(databasePool).catch(err => {
|
|
process.stderr.write(`[joplock] Index creation error: ${err.message}\n`);
|
|
});
|
|
|
|
const server = createServer({
|
|
publicDir,
|
|
joplinPublicBasePath,
|
|
joplinPublicBaseUrl,
|
|
joplinServerPublicUrl,
|
|
joplinServerOrigin,
|
|
sessionService,
|
|
itemService,
|
|
settingsService,
|
|
historyService,
|
|
itemWriteService,
|
|
adminService,
|
|
adminEmail,
|
|
ignoreAdminMfa,
|
|
database: databasePool,
|
|
vaultService,
|
|
backupService,
|
|
recoveryService,
|
|
rateLimitService: loginRateLimitService,
|
|
sessionCookieMaxAge,
|
|
debug: process.env.DEBUG === 'true' || process.env.DEBUG === '1',
|
|
joplockVersion,
|
|
});
|
|
|
|
server.listen(port, host, () => {
|
|
process.stdout.write(`Joplock listening on http://${host}:${port}\n`);
|
|
});
|
|
|
|
server.on('close', () => {
|
|
void databasePool.end();
|
|
});
|