diff --git a/box.js b/box.js index a7aa4c328..932368d30 100755 --- a/box.js +++ b/box.js @@ -1,17 +1,18 @@ #!/usr/bin/env node -'use strict'; +import constants from './src/constants.js'; +import fs from 'node:fs'; +import * as ldapServer from './src/ldapserver.js'; +import net from 'node:net'; +import * as oidcServer from './src/oidcserver.js'; +import paths from './src/paths.js'; +import * as proxyAuth from './src/proxyauth.js'; +import safe from 'safetydance'; +import * as server from './src/server.js'; +import * as directoryServer from './src/directoryserver.js'; +import debugModule from 'debug'; -const constants = require('./src/constants.js'), - fs = require('node:fs'), - ldapServer = require('./src/ldapserver.js'), - net = require('node:net'), - oidcServer = require('./src/oidcserver.js'), - paths = require('./src/paths.js'), - proxyAuth = require('./src/proxyauth.js'), - safe = require('safetydance'), - server = require('./src/server.js'), - directoryServer = require('./src/directoryserver.js'); +const debug = debugModule('box:box'); let logFd; @@ -59,7 +60,6 @@ async function main() { if (error) return exitSync({ error, code: 1, message: 'Error starting servers' }); // require this here so that logging handler is already setup - const debug = require('debug')('box:box'); process.on('SIGHUP', async function () { debug('Received SIGHUP. Re-reading configs.'); diff --git a/eslint.config.js b/eslint.config.js index b40ce7d9f..1048f0afb 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,7 +1,7 @@ -const js = require('@eslint/js'); -const globals = require('globals'); +import js from '@eslint/js'; +import globals from 'globals'; -module.exports = [ +export default [ js.configs.recommended, { files: ["**/*.js"], @@ -10,7 +10,7 @@ module.exports = [ ...globals.node, }, ecmaVersion: 13, - sourceType: "commonjs" + sourceType: "module" }, rules: { semi: "error", @@ -18,4 +18,3 @@ module.exports = [ } } ]; - diff --git a/migrations/package.json b/migrations/package.json new file mode 100644 index 000000000..5bbefffba --- /dev/null +++ b/migrations/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/package.json b/package.json index 153f8d266..ed6721ba2 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "description": "Cloudron Platform (Box)", "version": "9.0.0", "private": true, + "type": "module", "contributors": [ "Girish", "Johannes" diff --git a/scripts/cjs-to-esm/check-import-mismatches.mjs b/scripts/cjs-to-esm/check-import-mismatches.mjs new file mode 100644 index 000000000..49c7e6d8f --- /dev/null +++ b/scripts/cjs-to-esm/check-import-mismatches.mjs @@ -0,0 +1,96 @@ +#!/usr/bin/env node + +// Check for import/export mismatches: +// 1. Files using `import X from './file.js'` where file.js has no default export +// 2. Files using `import * as X from './file.js'` where file.js ONLY has default export + +import fs from 'node:fs'; +import path from 'node:path'; + +const ROOT = process.cwd(); + +function walk(dir) { + const result = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, e.name); + if (e.isDirectory()) result.push(...walk(p)); + else if (e.name.endsWith('.js')) result.push(p); + } + return result; +} + +const srcFiles = walk(path.join(ROOT, 'src')); +srcFiles.push(path.join(ROOT, 'syslog.js')); + +// Build export map +const exportMap = {}; // file -> 'default' | 'named' | 'both' | 'none' +for (const file of srcFiles) { + const content = fs.readFileSync(file, 'utf-8'); + const hasDefault = /^export default /m.test(content); + const hasNamed = /^export \{/m.test(content) || /^export (const|let|var|function|class) /m.test(content); + + if (hasDefault && hasNamed) exportMap[file] = 'both'; + else if (hasDefault) exportMap[file] = 'default'; + else if (hasNamed) exportMap[file] = 'named'; + else exportMap[file] = 'none'; +} + +// Check imports +let mismatches = 0; +const fixes = []; // { file, line, from, to } + +for (const file of srcFiles) { + const content = fs.readFileSync(file, 'utf-8'); + const lines = content.split('\n'); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Default import of local file + let m = line.match(/^import\s+(\w+)\s+from\s+'(\.[^']+\.js)';$/); + if (m) { + const [, name, importPath] = m; + const resolved = path.resolve(path.dirname(file), importPath); + const exportType = exportMap[resolved]; + if (exportType === 'named') { + console.log(`MISMATCH: ${path.relative(ROOT, file)}:${i+1} - import ${name} from '${importPath}' but ${path.relative(ROOT, resolved)} has only named exports`); + fixes.push({ file, lineNum: i, old: line, new: line.replace(`import ${name}`, `import * as ${name}`) }); + mismatches++; + } + } + + // Namespace import of local file + m = line.match(/^import\s+\*\s+as\s+(\w+)\s+from\s+'(\.[^']+\.js)';$/); + if (m) { + const [, name, importPath] = m; + const resolved = path.resolve(path.dirname(file), importPath); + const exportType = exportMap[resolved]; + if (exportType === 'default') { + console.log(`MISMATCH: ${path.relative(ROOT, file)}:${i+1} - import * as ${name} from '${importPath}' but ${path.relative(ROOT, resolved)} has only default export`); + fixes.push({ file, lineNum: i, old: line, new: line.replace(`import * as ${name}`, `import ${name}`) }); + mismatches++; + } + } + } +} + +console.log(`\nFound ${mismatches} mismatches.`); + +if (process.argv.includes('--fix') && fixes.length > 0) { + // Group by file + const byFile = {}; + for (const fix of fixes) { + if (!byFile[fix.file]) byFile[fix.file] = []; + byFile[fix.file].push(fix); + } + + for (const [file, fileFixes] of Object.entries(byFile)) { + let content = fs.readFileSync(file, 'utf-8'); + for (const fix of fileFixes) { + content = content.replace(fix.old, fix.new); + } + fs.writeFileSync(file, content); + console.log(`Fixed: ${path.relative(ROOT, file)} (${fileFixes.length} changes)`); + } + console.log('All mismatches fixed.'); +} diff --git a/scripts/cjs-to-esm/cjs-to-esm.mjs b/scripts/cjs-to-esm/cjs-to-esm.mjs new file mode 100644 index 000000000..f0b0b5434 --- /dev/null +++ b/scripts/cjs-to-esm/cjs-to-esm.mjs @@ -0,0 +1,355 @@ +#!/usr/bin/env node + +// One-shot CJS → ESM conversion script for the box codebase. +// Usage: +// node scripts/cjs-to-esm.mjs # convert all src/ + syslog.js +// node scripts/cjs-to-esm.mjs --dry-run # preview without writing +// node scripts/cjs-to-esm.mjs src/paths.js # convert specific file(s) + +import fs from 'node:fs'; +import path from 'node:path'; + +const ROOT = process.cwd(); +const DRY_RUN = process.argv.includes('--dry-run'); +const args = process.argv.slice(2).filter(a => !a.startsWith('--')); + +// ========== File Collection ========== + +function walk(dir) { + const result = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, e.name); + if (e.isDirectory()) result.push(...walk(p)); + else if (e.name.endsWith('.js')) result.push(p); + } + return result; +} + +function getFiles() { + if (args.length) return args.map(f => path.resolve(f)); + const files = walk(path.join(ROOT, 'src')); + files.push(path.join(ROOT, 'syslog.js')); + return files; +} + +// ========== NPM Package ESM Detection ========== + +const esmCache = {}; +function isEsmPkg(modPath) { + const name = modPath.startsWith('@') ? modPath.split('/').slice(0, 2).join('/') : modPath.split('/')[0]; + if (name in esmCache) return esmCache[name]; + try { + const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'node_modules', name, 'package.json'), 'utf-8')); + esmCache[name] = pkg.type === 'module'; + } catch { + esmCache[name] = false; + } + return esmCache[name]; +} + +// ========== Import Path Fixup ========== + +function fixPath(mod, fromFile) { + if (!mod.startsWith('.')) return mod; + if (mod.endsWith('.js') || mod.endsWith('.mjs')) return mod; + const dir = path.dirname(fromFile); + const resolved = path.resolve(dir, mod); + if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) return mod + '/index.js'; + if (fs.existsSync(resolved + '.js')) return mod + '.js'; + return mod; +} + +// ========== Convert a Single Require Declaration ========== +// Input: trimmed declaration text WITHOUT leading const/let/var and trailing ,; +// Returns { imports: string[], consts: string[] } or null + +function convertDecl(decl, fromFile) { + decl = decl.trim().replace(/[,;]\s*$/, '').trim(); + if (!decl || !decl.includes('require(')) return null; + + let m; + + // 1) name = require('mod')('args...') e.g. debug = require('debug')('box:dns') + m = decl.match(/^(\w+)\s*=\s*require\(['"]([^'"]+)['"]\)\((.+)\)$/); + if (m) { + const [, name, mod, callArgs] = m; + const alias = name + 'Module'; + return { + imports: [`import ${alias} from '${fixPath(mod, fromFile)}';`], + consts: [`const ${name} = ${alias}(${callArgs});`] + }; + } + + // 2) name = require('mod').prop e.g. HttpError = require('@cloudron/connect-lastmile').HttpError + m = decl.match(/^(\w+)\s*=\s*require\(['"]([^'"]+)['"]\)\.(\w+)$/); + if (m) { + const [, name, mod, prop] = m; + const fp = fixPath(mod, fromFile); + const imp = name === prop + ? `import { ${prop} } from '${fp}';` + : `import { ${prop} as ${name} } from '${fp}';`; + return { imports: [imp], consts: [] }; + } + + // 3) { a, b } = require('mod') e.g. { CronTime } = require('cron') + m = decl.match(/^(\{[^}]+\})\s*=\s*require\(['"]([^'"]+)['"]\)$/); + if (m) { + const [, destr, mod] = m; + return { imports: [`import ${destr} from '${fixPath(mod, fromFile)}';`], consts: [] }; + } + + // 4) name = require('mod') e.g. apps = require('./apps.js') + m = decl.match(/^(\w+)\s*=\s*require\(['"]([^'"]+)['"]\)$/); + if (m) { + const [, name, mod] = m; + const fp = fixPath(mod, fromFile); + // Use namespace import for ESM-only npm packages (they likely have no default export) + const useNamespace = !mod.startsWith('.') && !mod.startsWith('node:') && isEsmPkg(mod); + const imp = useNamespace + ? `import * as ${name} from '${fp}';` + : `import ${name} from '${fp}';`; + return { imports: [imp], consts: [] }; + } + + return null; // unknown pattern +} + +// ========== Process a const-require Block ========== + +function handleBlock(blockLines, imports, consts, warnings, fromFile) { + const block = blockLines.join('\n'); + // Strip leading const/let/var and trailing ; + let inner = block.replace(/^\s*(const|let|var)\s+/, ''); + inner = inner.replace(/;\s*$/, ''); + + // Split individual declarations by comma-at-end-of-line + const decls = inner.split(/,\n/).map(d => d.trim()); + + for (const decl of decls) { + if (!decl) continue; + + if (!decl.includes('require(')) { + // Non-require declaration in a mixed block — keep as const + consts.push(`const ${decl};`); + continue; + } + + const result = convertDecl(decl, fromFile); + if (result) { + imports.push(...result.imports); + if (result.consts.length) consts.push(...result.consts); + } else { + warnings.push(`Unknown require pattern: ${decl}`); + consts.push(`const ${decl};`); + } + } +} + +// ========== Convert Exports Block ========== + +function convertExports(block) { + // exports = module.exports = { ... }; + let m = block.match(/exports\s*=\s*module\.exports\s*=\s*(\{[\s\S]*\});/); + if (m) return `export default ${m[1]};`; + + // exports = module.exports = name; + m = block.match(/exports\s*=\s*module\.exports\s*=\s*(\w+)\s*;/); + if (m) return `export default ${m[1]};`; + + return block; // fallback +} + +// ========== Main File Processor ========== + +function processFile(filePath) { + const lines = fs.readFileSync(filePath, 'utf-8').split('\n'); + const imports = []; + const consts = []; + const output = []; + const warnings = []; + + let i = 0; + let inBlock = false; + let blockLines = []; + + while (i < lines.length) { + const line = lines[i]; + const t = line.trim(); + + // --- Skip 'use strict' --- + if (t === "'use strict';" || t === '"use strict";') { + i++; + if (i < lines.length && lines[i].trim() === '') i++; + continue; + } + + // --- Detect start of const/let/var block --- + if (!inBlock && /^\s*(const|let|var)\s+/.test(line)) { + blockLines = [line]; + if (t.endsWith(';')) { + // Single-line block + if (/require\(/.test(line)) { + handleBlock(blockLines, imports, consts, warnings, filePath); + } else { + output.push(line); + } + } else { + inBlock = true; + } + i++; + continue; + } + + // --- Continue multi-line const block --- + if (inBlock) { + blockLines.push(line); + if (t.endsWith(';')) { + const fullBlock = blockLines.join('\n'); + if (/require\(/.test(fullBlock)) { + handleBlock(blockLines, imports, consts, warnings, filePath); + } else { + output.push(...blockLines); + } + inBlock = false; + } + i++; + continue; + } + + // --- Exports block --- + if (/^\s*exports\s*=\s*module\.exports\s*=/.test(line)) { + let block = line; + if (!t.endsWith(';')) { + i++; + while (i < lines.length) { + block += '\n' + lines[i]; + if (lines[i].trim().endsWith(';')) { i++; break; } + i++; + } + } else { + i++; + } + + if (/require\(/.test(block)) { + warnings.push('Inline require() in exports block — needs manual fix'); + output.push(block); + } else { + output.push(convertExports(block)); + } + continue; + } + + // --- require.main === module --- + if (/require\.main\s*===\s*module/.test(t)) { + output.push(line.replace(/require\.main\s*===\s*module/, 'process.argv[1] === import.meta.filename')); + i++; + continue; + } + + // --- Warn about any remaining require() calls --- + if (/\brequire\s*\(/.test(t) && !/^\s*\/\//.test(line) && !/^\s*\*/.test(line)) { + warnings.push(`Line ${i + 1}: Unconverted require(): ${t}`); + } + + output.push(line); + i++; + } + + // --- Replace __dirname / __filename with import.meta equivalents (Node 21.2+) --- + for (let j = 0; j < output.length; j++) { + output[j] = output[j].replace(/\b__dirname\b/g, 'import.meta.dirname'); + output[j] = output[j].replace(/\b__filename\b/g, 'import.meta.filename'); + } + + // --- Reconstruct file --- + // Preserve shebang and leading block comments before imports + const leading = []; + let start = 0; + + // Shebang + if (output.length > 0 && output[0].trimStart().startsWith('#!')) { + leading.push(output[0]); + start = 1; + // skip blank line after shebang + while (start < output.length && output[start].trim() === '') start++; + } + + // Leading block comment (/* jslint */, /* global */, /* eslint */) + if (start < output.length && output[start].trim().startsWith('/*')) { + // Could be single-line or multi-line + if (output[start].includes('*/')) { + leading.push(output[start]); + start++; + } else { + while (start < output.length) { + leading.push(output[start]); + if (output[start].includes('*/')) { start++; break; } + start++; + } + } + // skip blank line after comment + if (start < output.length && output[start].trim() === '') start++; + } + + const rest = output.slice(start); + // Remove leading blank lines from rest + while (rest.length > 0 && rest[0].trim() === '') rest.shift(); + + // Build final content + const parts = []; + if (leading.length > 0) { + parts.push(...leading); + parts.push(''); + } + if (imports.length > 0) { + parts.push(...imports); + } + if (consts.length > 0) { + if (imports.length > 0) parts.push(''); + parts.push(...consts); + } + if (imports.length > 0 || consts.length > 0) { + parts.push(''); + } + parts.push(...rest); + + let result = parts.join('\n'); + // Clean up 3+ consecutive blank lines → 2 + result = result.replace(/\n{3,}/g, '\n\n'); + // Ensure trailing newline + if (!result.endsWith('\n')) result += '\n'; + + return { content: result, warnings }; +} + +// ========== Run ========== + +const files = getFiles(); +console.log(`Processing ${files.length} files${DRY_RUN ? ' (dry run)' : ''}...\n`); + +let totalWarnings = 0; +for (const file of files) { + const rel = path.relative(ROOT, file); + try { + const { content, warnings } = processFile(file); + + if (warnings.length > 0) { + console.log(`⚠ ${rel}:`); + for (const w of warnings) console.log(` ${w}`); + totalWarnings += warnings.length; + } + + if (!DRY_RUN) { + fs.writeFileSync(file, content); + console.log(`✓ ${rel}`); + } else { + console.log(` ${rel}${warnings.length ? '' : ' (ok)'}`); + } + } catch (err) { + console.error(`✗ ${rel}: ${err.message}`); + totalWarnings++; + } +} + +console.log(`\nDone. ${files.length} files processed, ${totalWarnings} warnings.`); +if (totalWarnings > 0) console.log('Review warnings above and fix manually.'); diff --git a/scripts/cjs-to-esm/fix-consumer-imports.mjs b/scripts/cjs-to-esm/fix-consumer-imports.mjs new file mode 100644 index 000000000..17a7fd841 --- /dev/null +++ b/scripts/cjs-to-esm/fix-consumer-imports.mjs @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +// Fix consumers of files that still use export default { ... } +// Change `import * as X from './file.js'` back to `import X from './file.js'` +// for files whose target still uses export default. + +import fs from 'node:fs'; +import path from 'node:path'; + +const ROOT = process.cwd(); + +function walk(dir) { + const result = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, e.name); + if (e.isDirectory()) result.push(...walk(p)); + else if (e.name.endsWith('.js')) result.push(p); + } + return result; +} + +const srcFiles = walk(path.join(ROOT, 'src')); +srcFiles.push(path.join(ROOT, 'syslog.js')); + +// Find files that use export default (not named exports) +const defaultExportFiles = new Set(); +for (const file of srcFiles) { + const content = fs.readFileSync(file, 'utf-8'); + if (/^export default /m.test(content)) { + defaultExportFiles.add(file); + } +} + +console.log(`Files with export default: ${defaultExportFiles.size}`); +for (const f of defaultExportFiles) { + console.log(` ${path.relative(ROOT, f)}`); +} + +// Fix consumers: change `import * as X from './file.js'` → `import X from './file.js'` +// for files that use export default +let changes = 0; + +for (const file of srcFiles) { + let content = fs.readFileSync(file, 'utf-8'); + let changed = false; + + const importRegex = /^(import\s+)\* as (\w+)(\s+from\s+')(\.[^']+\.js)(';)$/gm; + const newContent = content.replace(importRegex, (match, pre, name, mid, importPath, post) => { + const dir = path.dirname(file); + const resolved = path.resolve(dir, importPath); + + if (defaultExportFiles.has(resolved)) { + changed = true; + changes++; + return `${pre}${name}${mid}${importPath}${post}`; + } + return match; + }); + + if (changed) { + fs.writeFileSync(file, newContent); + console.log(`✓ Fixed imports: ${path.relative(ROOT, file)}`); + } +} + +console.log(`\nReverted ${changes} imports to default import style.`); diff --git a/scripts/cjs-to-esm/fix-exports-self-ref.mjs b/scripts/cjs-to-esm/fix-exports-self-ref.mjs new file mode 100644 index 000000000..0cbfbf74d --- /dev/null +++ b/scripts/cjs-to-esm/fix-exports-self-ref.mjs @@ -0,0 +1,177 @@ +#!/usr/bin/env node + +// Fix `exports.CONSTANT` self-references in ESM files. +// For files with `export default { ... }`: +// - Extract constant properties (simple key: 'literal' pairs) as standalone const declarations +// - Replace the inline definition in the export block with shorthand property +// - Replace `exports.CONSTANT` with `CONSTANT` everywhere +// For files with `export { ... }` (constants already standalone): +// - Just replace `exports.CONSTANT` with `CONSTANT` + +import fs from 'node:fs'; +import path from 'node:path'; + +const ROOT = path.resolve(import.meta.dirname, '..'); +const args = process.argv.slice(2).filter(a => !a.startsWith('--')); + +function findFilesWithExportsRef(dir) { + const results = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === 'node_modules' || entry.name === 'migrations' || entry.name === 'dashboard') continue; + results.push(...findFilesWithExportsRef(full)); + } else if (entry.name.endsWith('.js')) { + const content = fs.readFileSync(full, 'utf8'); + if (/\bexports\./.test(content)) { + results.push(full); + } + } + } + return results; +} + +function processFile(filePath) { + let content = fs.readFileSync(filePath, 'utf8'); + const relPath = path.relative(ROOT, filePath); + + // Find all exports.SOMETHING references + const exportsRefs = new Set(); + const re = /\bexports\.(\w+)/g; + let m; + while ((m = re.exec(content)) !== null) { + exportsRefs.add(m[1]); + } + + if (exportsRefs.size === 0) return; + + console.log(`\n=== ${relPath} ===`); + console.log(` Self-referenced constants: ${[...exportsRefs].join(', ')}`); + + // Check if these constants already exist as standalone const/let/var declarations + const alreadyDeclared = new Set(); + for (const name of exportsRefs) { + const declPattern = new RegExp(`^(?:const|let|var)\\s+${name}\\s*=`, 'm'); + if (declPattern.test(content)) { + alreadyDeclared.add(name); + } + } + + const needsExtraction = [...exportsRefs].filter(n => !alreadyDeclared.has(n)); + + if (alreadyDeclared.size > 0) { + console.log(` Already declared: ${[...alreadyDeclared].join(', ')}`); + } + + // For constants that need extraction, find them in the export default block + if (needsExtraction.length > 0) { + console.log(` Need extraction: ${needsExtraction.join(', ')}`); + + // Find export default { ... } block + // Parse it carefully - we need to handle the block properly + const exportStart = content.indexOf('export default {'); + if (exportStart === -1) { + console.log(` WARNING: No 'export default {' found but need extraction for: ${needsExtraction.join(', ')}`); + // Just do the replacement anyway + } else { + // Find the matching closing brace + let braceDepth = 0; + let exportEnd = -1; + let inString = false; + let stringChar = ''; + for (let i = exportStart + 'export default '.length; i < content.length; i++) { + const ch = content[i]; + if (inString) { + if (ch === '\\') { i++; continue; } + if (ch === stringChar) inString = false; + continue; + } + if (ch === '\'' || ch === '"' || ch === '`') { + inString = true; + stringChar = ch; + continue; + } + if (ch === '{') braceDepth++; + else if (ch === '}') { + braceDepth--; + if (braceDepth === 0) { + exportEnd = i + 1; + // Include trailing semicolon + if (content[exportEnd] === ';') exportEnd++; + break; + } + } + } + + if (exportEnd === -1) { + console.log(` WARNING: Could not find end of export default block`); + } else { + const exportBlock = content.slice(exportStart, exportEnd); + const lines = exportBlock.split('\n'); + + const extracted = []; // { name, value, originalLine } + const newLines = []; + + for (const line of lines) { + const trimmed = line.trim(); + let found = false; + + for (const name of needsExtraction) { + // Match: NAME: 'value', or NAME: 123, or NAME: true, + // Only match simple literal values (strings, numbers, booleans) + const pattern = new RegExp(`^${name}:\\s*('[^']*'|"[^"]*"|\\d+(?:\\.\\d+)?|true|false|null),?\\s*(?:\\/\\/.*)?$`); + const match = trimmed.match(pattern); + if (match) { + extracted.push({ name, value: match[1] }); + // Replace the line with just the shorthand property + const indent = line.match(/^(\s*)/)[1]; + newLines.push(`${indent}${name},`); + found = true; + break; + } + } + + if (!found) { + newLines.push(line); + } + } + + if (extracted.length > 0) { + const constDecls = extracted.map(e => `const ${e.name} = ${e.value};`).join('\n'); + const newExportBlock = newLines.join('\n'); + content = content.slice(0, exportStart) + constDecls + '\n\n' + newExportBlock + content.slice(exportEnd); + console.log(` Extracted ${extracted.length} constants: ${extracted.map(e => e.name).join(', ')}`); + } + + const notExtracted = needsExtraction.filter(n => !extracted.find(e => e.name === n)); + if (notExtracted.length > 0) { + console.log(` WARNING: Could not extract (complex values?): ${notExtracted.join(', ')}`); + } + } + } + } + + // Replace all exports.CONSTANT references with just CONSTANT + for (const name of exportsRefs) { + const pattern = new RegExp(`\\bexports\\.${name}\\b`, 'g'); + content = content.replace(pattern, name); + } + + fs.writeFileSync(filePath, content); + console.log(` Done!`); +} + +let files; +if (args.length) { + files = args.map(f => path.resolve(f)); +} else { + files = findFilesWithExportsRef(path.join(ROOT, 'src')); +} + +console.log(`Processing ${files.length} files...`); + +for (const file of files) { + processFile(file); +} + +console.log('\nAll done!'); diff --git a/scripts/cjs-to-esm/fix-exports.mjs b/scripts/cjs-to-esm/fix-exports.mjs new file mode 100644 index 000000000..93631c865 --- /dev/null +++ b/scripts/cjs-to-esm/fix-exports.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +// Phase 2: Convert export default { shorthand } → export { shorthand } +// and update consumers from `import X from` → `import * as X from` for local files. + +import fs from 'node:fs'; +import path from 'node:path'; + +const ROOT = process.cwd(); +const DRY_RUN = process.argv.includes('--dry-run'); + +function walk(dir) { + const result = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, e.name); + if (e.isDirectory()) result.push(...walk(p)); + else if (e.name.endsWith('.js')) result.push(p); + } + return result; +} + +const srcFiles = walk(path.join(ROOT, 'src')); +srcFiles.push(path.join(ROOT, 'syslog.js')); + +// Phase 1: Identify files with shorthand-only export default { ... } +// and convert them to export { ... } +const convertedFiles = new Set(); + +for (const file of srcFiles) { + let content = fs.readFileSync(file, 'utf-8'); + + // Match export default { ... }; + const match = content.match(/^export default (\{[\s\S]*?\});/m); + if (!match) continue; + + const objContent = match[1]; + + // Check if this is shorthand-only (no "key: value" patterns) + // Shorthand entries look like: " funcName," or " funcName" (last one) + // Key-value entries look like: " KEY: value," or " key: value" + // We strip the braces and check each line + const inner = objContent.slice(1, -1); // remove { and } + const lines = inner.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('//')); + + // Check if any line has a colon that's NOT in a comment + const hasKeyValue = lines.some(line => { + // Remove trailing comma + const cleaned = line.replace(/,\s*$/, '').trim(); + if (!cleaned) return false; + // A shorthand property is just an identifier (possibly with leading/trailing whitespace) + // A key-value has "identifier: something" or "'key': something" + return cleaned.includes(':'); + }); + + if (hasKeyValue) { + // Cannot convert to named exports - has computed values + continue; + } + + // Convert export default { ... } → export { ... } + const newContent = content.replace(/^export default (\{[\s\S]*?\});/m, 'export $1;'); + if (newContent !== content) { + if (!DRY_RUN) fs.writeFileSync(file, newContent); + convertedFiles.add(file); + console.log(`✓ Converted exports: ${path.relative(ROOT, file)}`); + } +} + +console.log(`\nConverted ${convertedFiles.size} files to named exports.`); + +// Phase 2: Update consumers +// For each converted file, find all files that import it with `import X from './path'` +// and change to `import * as X from './path'` +let importChanges = 0; + +for (const file of srcFiles) { + let content = fs.readFileSync(file, 'utf-8'); + let changed = false; + + // Match: import identifier from './relative/path.js'; + // or: import identifier from '../relative/path.js'; + const importRegex = /^(import\s+)(\w+)(\s+from\s+')(\.[^']+\.js)(';)$/gm; + const newContent = content.replace(importRegex, (match, pre, name, mid, importPath, post) => { + // Resolve the import path to absolute + const dir = path.dirname(file); + const resolved = path.resolve(dir, importPath); + + if (convertedFiles.has(resolved)) { + changed = true; + importChanges++; + return `${pre}* as ${name}${mid}${importPath}${post}`; + } + return match; + }); + + if (changed && !DRY_RUN) { + fs.writeFileSync(file, newContent); + console.log(`✓ Updated imports: ${path.relative(ROOT, file)}`); + } +} + +console.log(`\nUpdated ${importChanges} import statements.`); +console.log('Done.'); diff --git a/scripts/cjs-to-esm/fix-imports-for-named.mjs b/scripts/cjs-to-esm/fix-imports-for-named.mjs new file mode 100644 index 000000000..fc3f5b732 --- /dev/null +++ b/scripts/cjs-to-esm/fix-imports-for-named.mjs @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +// For a set of target files, find imports that use `import X from './module.js'` +// where the module uses `export { ... }` (named exports, no default). +// Convert those to `import * as X from './module.js'`. + +import fs from 'node:fs'; +import path from 'node:path'; + +const ROOT = path.resolve(import.meta.dirname, '..'); +const targetFiles = process.argv.slice(2).map(f => path.resolve(f)); + +if (targetFiles.length === 0) { + console.log('Usage: node scripts/fix-imports-for-named.mjs file1.js file2.js ...'); + process.exit(1); +} + +// Build a set of files that use named exports (export { ... }) without a default export +function hasOnlyNamedExports(filePath) { + if (!fs.existsSync(filePath)) return false; + const content = fs.readFileSync(filePath, 'utf8'); + const hasNamedExport = /^export \{/m.test(content); + const hasDefaultExport = /^export default /m.test(content); + return hasNamedExport && !hasDefaultExport; +} + +for (const filePath of targetFiles) { + let content = fs.readFileSync(filePath, 'utf8'); + const relPath = path.relative(ROOT, filePath); + const dir = path.dirname(filePath); + let changes = 0; + + // Find all default imports: import NAME from './something.js' + const importRe = /^import (\w+) from '([^']+)';$/gm; + let match; + const replacements = []; + + while ((match = importRe.exec(content)) !== null) { + const [fullMatch, name, specifier] = match; + + // Only handle local/relative imports + if (!specifier.startsWith('.')) continue; + + // Resolve the full path + const resolved = path.resolve(dir, specifier); + + if (hasOnlyNamedExports(resolved)) { + replacements.push({ + from: fullMatch, + to: `import * as ${name} from '${specifier}';` + }); + } + } + + for (const r of replacements) { + content = content.replace(r.from, r.to); + changes++; + } + + if (changes > 0) { + fs.writeFileSync(filePath, content); + console.log(`${relPath}: fixed ${changes} imports`); + } else { + console.log(`${relPath}: no changes needed`); + } +} diff --git a/scripts/cjs-to-esm/fix-remaining-exports.mjs b/scripts/cjs-to-esm/fix-remaining-exports.mjs new file mode 100644 index 000000000..bcd76d597 --- /dev/null +++ b/scripts/cjs-to-esm/fix-remaining-exports.mjs @@ -0,0 +1,129 @@ +#!/usr/bin/env node + +// Phase 3: Convert remaining export default { ... } (with computed properties) +// to named exports by extracting computed values as const declarations. +// Also updates consumers from `import X from` → `import * as X from`. + +import fs from 'node:fs'; +import path from 'node:path'; + +const ROOT = process.cwd(); +const DRY_RUN = process.argv.includes('--dry-run'); + +function walk(dir) { + const result = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, e.name); + if (e.isDirectory()) result.push(...walk(p)); + else if (e.name.endsWith('.js')) result.push(p); + } + return result; +} + +const srcFiles = walk(path.join(ROOT, 'src')); +srcFiles.push(path.join(ROOT, 'syslog.js')); + +const convertedFiles = new Set(); + +for (const file of srcFiles) { + let content = fs.readFileSync(file, 'utf-8'); + + // Match export default { ... }; + const match = content.match(/^(export default )(\{[\s\S]*?\});/m); + if (!match) continue; + + const fullMatch = match[0]; + const objContent = match[2]; + const inner = objContent.slice(1, -1); // strip { } + + // Parse entries + const lines = inner.split('\n'); + const extractedConsts = []; + const exportNames = []; + + for (const rawLine of lines) { + const line = rawLine.trim(); + if (!line || line.startsWith('//') || line.startsWith('/*') || line.startsWith('*')) continue; + + // Remove trailing comma + const cleaned = line.replace(/,\s*$/, '').trim(); + if (!cleaned) continue; + + // Check if it's a key-value pair (has : that's not in a string or ternary) + // Simple heuristic: if the line matches "identifier: expression" + const kvMatch = cleaned.match(/^(\w+)\s*:\s*(.+)$/); + if (kvMatch) { + const [, key, value] = kvMatch; + // Check if this is truly a key-value or just a shorthand with same name + // If key === value, it's shorthand (e.g. "x: x" is rare but possible) + if (key !== value) { + extractedConsts.push(`const ${key} = ${value};`); + exportNames.push(key); + continue; + } + } + + // Shorthand property + exportNames.push(cleaned); + } + + // Build new export block + const newExport = `export {\n ${exportNames.join(',\n ')},\n};`; + + // Build replacement: extracted consts + newline + export block + let replacement; + if (extractedConsts.length > 0) { + replacement = extractedConsts.join('\n') + '\n\n' + newExport; + } else { + replacement = newExport; + } + + const newContent = content.replace(fullMatch, replacement); + if (newContent !== content) { + if (!DRY_RUN) fs.writeFileSync(file, newContent); + convertedFiles.add(file); + console.log(`✓ Converted exports: ${path.relative(ROOT, file)}`); + } +} + +console.log(`\nConverted ${convertedFiles.size} files.`); + +// Phase 2: Update consumers +let importChanges = 0; + +for (const file of srcFiles) { + let content = fs.readFileSync(file, 'utf-8'); + let changed = false; + + const importRegex = /^(import\s+)(\w+)(\s+from\s+')(\.[^']+\.js)(';)$/gm; + const newContent = content.replace(importRegex, (match, pre, name, mid, importPath, post) => { + const dir = path.dirname(file); + const resolved = path.resolve(dir, importPath); + + if (convertedFiles.has(resolved)) { + changed = true; + importChanges++; + return `${pre}* as ${name}${mid}${importPath}${post}`; + } + return match; + }); + + if (changed && !DRY_RUN) { + fs.writeFileSync(file, newContent); + console.log(`✓ Updated imports: ${path.relative(ROOT, file)}`); + } +} + +console.log(`\nUpdated ${importChanges} import statements.`); + +// Verify no more export default { ... } remain +let remaining = 0; +for (const file of srcFiles) { + const content = fs.readFileSync(file, 'utf-8'); + if (/^export default \{/m.test(content)) { + console.log(`⚠ Still has export default: ${path.relative(ROOT, file)}`); + remaining++; + } +} +if (remaining > 0) console.log(`\n${remaining} files still need manual attention.`); +else console.log('\nAll export default { ... } converted to named exports.'); diff --git a/scripts/cjs-to-esm/revert-and-fix.mjs b/scripts/cjs-to-esm/revert-and-fix.mjs new file mode 100644 index 000000000..6ce5daeda --- /dev/null +++ b/scripts/cjs-to-esm/revert-and-fix.mjs @@ -0,0 +1,60 @@ +#!/usr/bin/env node + +// Revert the broken extraction from fix-remaining-exports.mjs +// and instead use a proper approach: +// 1. Keep export default { ... } for files with computed properties +// 2. Change all `import X from './local.js'` for those files to +// `import * as _X from './local.js'; const X = _X.default;` +// Actually that's ugly. Better approach: +// Just lazify the top-level constant evaluation in services.js +// OR use a re-export pattern. +// +// Actually, the simplest approach: restore the 34 files from git, +// then re-run the first two conversion scripts on them, +// then for files with complex exports, add a thin named-export wrapper. + +import fs from 'node:fs'; +import path from 'node:path'; +import { execSync } from 'node:child_process'; + +const ROOT = process.cwd(); + +// Find files that still have export default but with broken syntax +const files = execSync('git diff --name-only', { cwd: ROOT, encoding: 'utf-8' }) + .trim().split('\n') + .filter(f => f.startsWith('src/') || f === 'syslog.js'); + +// Check which files have syntax errors +const brokenFiles = []; +for (const file of files) { + try { + execSync(`node --check ${file}`, { cwd: ROOT, stdio: 'pipe' }); + } catch { + brokenFiles.push(file); + } +} + +console.log(`Found ${brokenFiles.length} files with syntax errors:`); +brokenFiles.forEach(f => console.log(` ${f}`)); + +// Restore these files from git +for (const file of brokenFiles) { + execSync(`git checkout -- ${file}`, { cwd: ROOT }); + console.log(`Restored: ${file}`); +} + +// Now re-run the initial CJS→ESM conversion on just these files +console.log('\nRe-running CJS→ESM conversion on restored files...'); +const fileArgs = brokenFiles.join(' '); +execSync(`node scripts/cjs-to-esm.mjs ${fileArgs}`, { cwd: ROOT, stdio: 'inherit' }); + +// Now re-run the first export fix (shorthand-only) on these files +// But we need a targeted approach: for each file, check if its exports +// are shorthand-only, and if so convert to named exports. +// Files with computed properties will keep export default. + +console.log('\nRe-running export fixes...'); +execSync('node scripts/fix-exports.mjs', { cwd: ROOT, stdio: 'inherit' }); + +console.log('\nDone. Files with complex exports will keep export default.'); +console.log('Circular dependency issues will be fixed by lazifying top-level access.'); diff --git a/scripts/cloudron-support b/scripts/cloudron-support index 9aa292879..ffce1a755 100755 --- a/scripts/cloudron-support +++ b/scripts/cloudron-support @@ -749,7 +749,7 @@ function recreate_containers() { function download_docker_images() { info "Downloading addon images" - images=$(node -e "const i = require('/home/yellowtent/box/src/infra_version.js'); console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));") + images=$(node --input-type=module -e "import i from '/home/yellowtent/box/src/infra_version.js'; console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));") for image_ref in ${images}; do info "Pulling ${image_ref}" diff --git a/scripts/find-unused-translations b/scripts/find-unused-translations index 153eda701..719fb3fdb 100755 --- a/scripts/find-unused-translations +++ b/scripts/find-unused-translations @@ -1,10 +1,9 @@ #!/usr/bin/env node -const fs = require('fs'); -const path = require('path'); -const superagent = require('@cloudron/superagent'); - -const translations = require('../dashboard/public/translation/en.json'); +import fs from 'fs'; +import path from 'path'; +import superagent from '@cloudron/superagent'; +import translations from '../dashboard/public/translation/en.json'; // get token from https://translate.cloudron.io/accounts/profile/#api const token = ''; @@ -129,4 +128,4 @@ await findDuplicates(); // await purge(key); // } -})(); \ No newline at end of file +})(); diff --git a/scripts/hotfix b/scripts/hotfix index 32a82089a..e217f9952 100755 --- a/scripts/hotfix +++ b/scripts/hotfix @@ -1,18 +1,16 @@ #!/usr/bin/env node -'use strict'; - -const assert = require('assert'), - async = require('async'), - execSync = require('child_process').execSync, - fs = require('fs'), - net = require('net'), - os = require('os'), - path = require('path'), - { program } = require('commander'), - safe = require('safetydance'), - SshClient = require('ssh2').Client, - util = require('util'); +import assert from 'node:assert'; +import async from 'async'; +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import net from 'node:net'; +import os from 'node:os'; +import path from 'node:path'; +import { program } from 'commander'; +import safe from 'safetydance'; +import { Client as SshClient } from 'ssh2'; +import util from 'node:util'; function exit(error) { if (error instanceof Error) console.log(error.message); @@ -107,7 +105,7 @@ function hotfix(options) { if (net.isIP(options.cloudron)) { ip = options.cloudron; } else { - let out = safe.child_process.execSync(`host -t A ${options.cloudron}`, { encoding: 'utf8' }); + const out = safe.child_process.execSync(`host -t A ${options.cloudron}`, { encoding: 'utf8' }); if (!out) exit(`Could not resolve ${options.cloudron}`); ip = out.trim().split(' ')[3]; } @@ -119,7 +117,7 @@ function hotfix(options) { const sshPort = options.sshPort || 22; const sshUser = options.sshUser || 'root'; - let tarballScript = path.join(__dirname, 'create-release-tarball'); + const tarballScript = path.join(import.meta.dirname, 'create-release-tarball'); if (!fs.existsSync(tarballScript)) exit('Could not find create-release-taball script. Run this command from the box repo checkout directory.'); const tarball = `${os.tmpdir()}/boxtarball-${version}.tar.gz`; @@ -134,7 +132,7 @@ function hotfix(options) { { cmd: 'sudo rm -rf /tmp/box-src-hotfix' }, { cmd: 'sudo mkdir -p /tmp/box-src-hotfix' }, { cmd: 'sudo tar zxf - -C /tmp/box-src-hotfix', stdin: fs.createReadStream(tarball) }, - { cmd: 'sudo dd of=/tmp/remote_hotfix.js', stdin: fs.createReadStream(path.resolve(__dirname, './remote_hotfix.js')) }, + { cmd: 'sudo dd of=/tmp/remote_hotfix.js', stdin: fs.createReadStream(path.resolve(import.meta.dirname, './remote_hotfix.js')) }, { cmd: 'sudo HOME=/home/yellowtent BOX_ENV=cloudron node /tmp/remote_hotfix.js' }, { cmd: 'sudo rm -rf /tmp/box-src-hotfix' } ]; diff --git a/scripts/installer.sh b/scripts/installer.sh index 73f02434a..d6ddb5965 100755 --- a/scripts/installer.sh +++ b/scripts/installer.sh @@ -167,7 +167,7 @@ if [[ ${try} -eq 10 ]]; then fi log "downloading new addon images" -images=$(node -e "const i = require('${box_src_next_dir}/src/infra_version.js'); console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));") +images=$(node --input-type=module -e "import i from '${box_src_next_dir}/src/infra_version.js'; console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));") # docker hub only uses first 64 bits for ipv6 addressing. this causes many ipv6 rate limit errors # https://www.docker.com/blog/beta-ipv6-support-on-docker-hub-registry/ diff --git a/scripts/release b/scripts/release index 68356c363..49f2d9df0 100755 --- a/scripts/release +++ b/scripts/release @@ -1,22 +1,20 @@ #!/usr/bin/env node -'use strict'; - -const assert = require('assert'), - { execSync, spawnSync } = require('child_process'), - fs = require('fs'), - net = require('net'), - os = require('os'), - path = require('path'), - { program } = require('commander'), - safe = require('safetydance'), - semver = require('semver'), - superagent = require('@cloudron/superagent'), - Table = require('easy-table'), - url = require('url'), - util = require('util'), - readline = require('readline'), - _ = require('../src/underscore.js'); +import assert from 'assert'; +import { execSync, spawnSync } from 'child_process'; +import fs from 'fs'; +import net from 'net'; +import os from 'os'; +import path from 'path'; +import { program } from 'commander'; +import safe from 'safetydance'; +import semver from 'semver'; +import superagent from '@cloudron/superagent'; +import Table from 'easy-table'; +import url from 'url'; +import util from 'util'; +import readline from 'readline'; +import * as _ from '../src/underscore.js'; // slightly simplified from https://github.com/tcql/node-yesno/blob/master/yesno.js async function yesno({ question, defaultValue, yesValues, noValues }) { @@ -84,7 +82,7 @@ function exit(error) { function parseChangelog(version) { const changelog = [ ]; - const lines = fs.readFileSync(__dirname + '/../CHANGES', 'utf8').split('\n'); + const lines = fs.readFileSync(import.meta.dirname + '/../CHANGES', 'utf8').split('\n'); version = version.replace(/[+-].*/, ''); // strip prerelease @@ -398,7 +396,6 @@ async function listRelease(options) { t.cell('Author', releases[release].author.split(' ')[0]); t.cell('Next', releases[release].next); - var v = releases[release].sourceTarballUrl.match(/\/box-([^-]*)-?(.*).tar.gz/); t.cell('Source', v[1].slice(0, 7)); t.cell('Webadmin', v[2].slice(0, 7) || '-'); diff --git a/scripts/remote_hotfix.js b/scripts/remote_hotfix.js index 8ea9b46cd..7472f0c5c 100644 --- a/scripts/remote_hotfix.js +++ b/scripts/remote_hotfix.js @@ -1,13 +1,11 @@ -'use strict'; +import { spawn } from 'child_process'; +import path from 'path'; if (process.env.BOX_ENV !== 'cloudron') { console.error('!! This is only meant to be run with cloudron hotfix'); process.exit(1); } -const spawn = require('child_process').spawn, - path = require('path'); - const NEW_BOX_SOURCE_DIR = '/tmp/box-src-hotfix'; console.log('=> Running installer.sh'); diff --git a/src/acme2.js b/src/acme2.js index 7eceb1cc6..fce8ae619 100644 --- a/src/acme2.js +++ b/src/acme2.js @@ -1,28 +1,29 @@ -'use strict'; +import assert from 'node:assert'; +import blobs from './blobs.js'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import * as openssl from './openssl.js'; +import path from 'node:path'; +import paths from './paths.js'; +import promiseRetry from './promise-retry.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import * as users from './users.js'; -exports = module.exports = { +const debug = debugModule('box:cert/acme2'); + +const _name = 'acme'; +const _getChallengeSubdomain = getChallengeSubdomain; + +export { getCertificate, getRenewalInfo, - - // testing - _name: 'acme', - _getChallengeSubdomain: getChallengeSubdomain + _name, + _getChallengeSubdomain, }; -const assert = require('node:assert'), - blobs = require('./blobs.js'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - debug = require('debug')('box:cert/acme2'), - dns = require('./dns.js'), - openssl = require('./openssl.js'), - path = require('node:path'), - paths = require('./paths.js'), - promiseRetry = require('./promise-retry.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - users = require('./users.js'); - const CA_PROD_DIRECTORY_URL = 'https://acme-v02.api.letsencrypt.org/directory', CA_STAGING_DIRECTORY_URL = 'https://acme-staging-v02.api.letsencrypt.org/directory'; diff --git a/src/addonconfigs.js b/src/addonconfigs.js index 1a933d5b6..2515301d5 100644 --- a/src/addonconfigs.js +++ b/src/addonconfigs.js @@ -1,6 +1,7 @@ -'use strict'; +import assert from 'node:assert'; +import * as database from './database.js'; -exports = module.exports = { +export { get, set, unset, @@ -11,9 +12,6 @@ exports = module.exports = { getAppIdByValue, }; -const assert = require('node:assert'), - database = require('./database.js'); - async function set(appId, addonId, env) { assert.strictEqual(typeof appId, 'string'); assert.strictEqual(typeof addonId, 'string'); diff --git a/src/apphealthmonitor.js b/src/apphealthmonitor.js index c96142c5a..ecabdaccd 100644 --- a/src/apphealthmonitor.js +++ b/src/apphealthmonitor.js @@ -1,20 +1,20 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import * as docker from './docker.js'; +import eventlog from './eventlog.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; -exports = module.exports = { +const debug = debugModule('box:apphealthmonitor'); + +export { run }; -const apps = require('./apps.js'), - assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:apphealthmonitor'), - docker = require('./docker.js'), - eventlog = require('./eventlog.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - const UNHEALTHY_THRESHOLD = 20 * 60 * 1000; // 20 minutes const OOM_EVENT_LIMIT = 60 * 60 * 1000; // will only raise 1 oom event every hour diff --git a/src/applinks.js b/src/applinks.js index 16f86f243..36e095c17 100644 --- a/src/applinks.js +++ b/src/applinks.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import apps from './apps.js'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import debugModule from 'debug'; +import jsdom from 'jsdom'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; -exports = module.exports = { +const debug = debugModule('box:applinks'); + +export { list, listByUser, add, @@ -9,16 +19,6 @@ exports = module.exports = { del, }; -const assert = require('node:assert'), - apps = require('./apps.js'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - database = require('./database.js'), - debug = require('debug')('box:applinks'), - jsdom = require('jsdom'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - const APPLINKS_FIELDS= [ 'id', 'accessRestrictionJson', 'creationTime', 'updateTime', 'ts', 'label', 'tagsJson', 'icon', 'upstreamUri' ].join(','); function postProcess(result) { diff --git a/src/apppasswords.js b/src/apppasswords.js index 4afbd0c8b..adcaa95d4 100644 --- a/src/apppasswords.js +++ b/src/apppasswords.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import hat from './hat.js'; +import safe from 'safetydance'; +import * as _ from './underscore.js'; -exports = module.exports = { +export { get, add, list, @@ -9,14 +15,6 @@ exports = module.exports = { removePrivateFields }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - database = require('./database.js'), - hat = require('./hat.js'), - safe = require('safetydance'), - _ = require('./underscore.js'); - const APP_PASSWORD_FIELDS = [ 'id', 'name', 'userId', 'identifier', 'hashedPassword', 'creationTime', 'expiresAt' ].join(','); function validateAppPasswordName(name) { diff --git a/src/apps.js b/src/apps.js index 4ef3fac7f..24230cd64 100644 --- a/src/apps.js +++ b/src/apps.js @@ -1,6 +1,75 @@ -'use strict'; +import * as appTaskManager from './apptaskmanager.js'; +import * as appstore from './appstore.js'; +import * as archives from './archives.js'; +import assert from 'node:assert'; +import backups from './backups.js'; +import * as backupSites from './backupsites.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import crypto from 'node:crypto'; +import { CronTime } from 'cron'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import * as docker from './docker.js'; +import * as domains from './domains.js'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import Location from './location.js'; +import locks from './locks.js'; +import * as logs from './logs.js'; +import * as mail from './mail.js'; +import manifestFormat from '@cloudron/manifest-format'; +import mysql from 'mysql2'; +import * as notifications from './notifications.js'; +import once from './once.js'; +import path from 'node:path'; +import paths from './paths.js'; +import { PassThrough } from 'node:stream'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import semver from 'semver'; +import services from './services.js'; +import shellModule from './shell.js'; +import tasks from './tasks.js'; +import { Transform as TransformStream } from 'node:stream'; +import * as users from './users.js'; +import util from 'node:util'; +import * as volumes from './volumes.js'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:apps'); +const shell = shellModule('apps'); + +const PORT_TYPE_TCP = 'tcp'; +const PORT_TYPE_UDP = 'udp'; +const ISTATE_PENDING_INSTALL = 'pending_install'; +const ISTATE_PENDING_CLONE = 'pending_clone'; +const ISTATE_PENDING_CONFIGURE = 'pending_configure'; +const ISTATE_PENDING_RECREATE_CONTAINER = 'pending_recreate_container'; +const ISTATE_PENDING_LOCATION_CHANGE = 'pending_location_change'; +const ISTATE_PENDING_SERVICES_CHANGE = 'pending_services_change'; +const ISTATE_PENDING_DATA_DIR_MIGRATION = 'pending_data_dir_migration'; +const ISTATE_PENDING_RESIZE = 'pending_resize'; +const ISTATE_PENDING_DEBUG = 'pending_debug'; +const ISTATE_PENDING_UNINSTALL = 'pending_uninstall'; +const ISTATE_PENDING_RESTORE = 'pending_restore'; +const ISTATE_PENDING_IMPORT = 'pending_import'; +const ISTATE_PENDING_UPDATE = 'pending_update'; +const ISTATE_PENDING_START = 'pending_start'; +const ISTATE_PENDING_STOP = 'pending_stop'; +const ISTATE_PENDING_RESTART = 'pending_restart'; +const ISTATE_ERROR = 'error'; +const ISTATE_INSTALLED = 'installed'; +const RSTATE_RUNNING = 'running'; +const RSTATE_STOPPED = 'stopped'; +const ACCESS_LEVEL_ADMIN = 'admin'; +const ACCESS_LEVEL_OPERATOR = 'operator'; +const ACCESS_LEVEL_USER = 'user'; +const ACCESS_LEVEL_NONE = ''; + +export default { canAccess, isOperator, accessLevel, @@ -99,32 +168,32 @@ exports = module.exports = { canBackupApp, - PORT_TYPE_TCP: 'tcp', - PORT_TYPE_UDP: 'udp', + PORT_TYPE_TCP, + PORT_TYPE_UDP, // task codes - the installation state is now a misnomer (keep in sync in UI) - ISTATE_PENDING_INSTALL: 'pending_install', // installs and fresh reinstalls - ISTATE_PENDING_CLONE: 'pending_clone', // clone - ISTATE_PENDING_CONFIGURE: 'pending_configure', // infra update - ISTATE_PENDING_RECREATE_CONTAINER: 'pending_recreate_container', // env change or addon change - ISTATE_PENDING_LOCATION_CHANGE: 'pending_location_change', - ISTATE_PENDING_SERVICES_CHANGE: 'pending_services_change', - ISTATE_PENDING_DATA_DIR_MIGRATION: 'pending_data_dir_migration', - ISTATE_PENDING_RESIZE: 'pending_resize', - ISTATE_PENDING_DEBUG: 'pending_debug', - ISTATE_PENDING_UNINSTALL: 'pending_uninstall', // uninstallation - ISTATE_PENDING_RESTORE: 'pending_restore', // restore to previous backup or on upgrade - ISTATE_PENDING_IMPORT: 'pending_import', // import from external backup - ISTATE_PENDING_UPDATE: 'pending_update', // update from installed state preserving data - ISTATE_PENDING_START: 'pending_start', - ISTATE_PENDING_STOP: 'pending_stop', - ISTATE_PENDING_RESTART: 'pending_restart', - ISTATE_ERROR: 'error', // error executing last pending_* command - ISTATE_INSTALLED: 'installed', // app is installed + ISTATE_PENDING_INSTALL, + ISTATE_PENDING_CLONE, + ISTATE_PENDING_CONFIGURE, + ISTATE_PENDING_RECREATE_CONTAINER, + ISTATE_PENDING_LOCATION_CHANGE, + ISTATE_PENDING_SERVICES_CHANGE, + ISTATE_PENDING_DATA_DIR_MIGRATION, + ISTATE_PENDING_RESIZE, + ISTATE_PENDING_DEBUG, + ISTATE_PENDING_UNINSTALL, + ISTATE_PENDING_RESTORE, + ISTATE_PENDING_IMPORT, + ISTATE_PENDING_UPDATE, + ISTATE_PENDING_START, + ISTATE_PENDING_STOP, + ISTATE_PENDING_RESTART, + ISTATE_ERROR, + ISTATE_INSTALLED, // run states - RSTATE_RUNNING: 'running', - RSTATE_STOPPED: 'stopped', // app stopped by us + RSTATE_RUNNING, + RSTATE_STOPPED, // health states (keep in sync in UI) HEALTH_HEALTHY: 'healthy', @@ -133,10 +202,10 @@ exports = module.exports = { HEALTH_DEAD: 'dead', // app access levels - ACCESS_LEVEL_ADMIN: 'admin', - ACCESS_LEVEL_OPERATOR: 'operator', - ACCESS_LEVEL_USER: 'user', - ACCESS_LEVEL_NONE: '', + ACCESS_LEVEL_ADMIN, + ACCESS_LEVEL_OPERATOR, + ACCESS_LEVEL_USER, + ACCESS_LEVEL_NONE, // exported for testing _checkForPortBindingConflict: checkForPortBindingConflict, @@ -148,47 +217,6 @@ exports = module.exports = { _clear: clear }; -const appTaskManager = require('./apptaskmanager.js'), - appstore = require('./appstore.js'), - archives = require('./archives.js'), - assert = require('node:assert'), - backups = require('./backups.js'), - backupSites = require('./backupsites.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - crypto = require('node:crypto'), - { CronTime } = require('cron'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - debug = require('debug')('box:apps'), - dns = require('./dns.js'), - docker = require('./docker.js'), - domains = require('./domains.js'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - Location = require('./location.js'), - locks = require('./locks.js'), - logs = require('./logs.js'), - mail = require('./mail.js'), - manifestFormat = require('@cloudron/manifest-format'), - mysql = require('mysql2'), - notifications = require('./notifications.js'), - once = require('./once.js'), - path = require('node:path'), - paths = require('./paths.js'), - PassThrough = require('node:stream').PassThrough, - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - semver = require('semver'), - services = require('./services.js'), - shell = require('./shell.js')('apps'), - tasks = require('./tasks.js'), - TransformStream = require('node:stream').Transform, - users = require('./users.js'), - util = require('node:util'), - volumes = require('./volumes.js'), - _ = require('./underscore.js'); - // NOTE: when adding fields here, update the clone and unarchive logic as well const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.versionsUrl', 'apps.installationState', 'apps.errorJson', 'apps.runState', 'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.accessRestrictionJson', 'apps.memoryLimit', 'apps.cpuQuota', @@ -201,7 +229,7 @@ const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.versionsUrl', // const PORT_BINDINGS_FIELDS = [ 'hostPort', 'type', 'environmentVariable', 'appId', 'count' ].join(','); const LOCATION_FIELDS = [ 'appId', 'subdomain', 'domain', 'type', 'certificateJson' ]; -const CHECKVOLUME_CMD = path.join(__dirname, 'scripts/checkvolume.sh'); +const CHECKVOLUME_CMD = path.join(import.meta.dirname, 'scripts/checkvolume.sh'); // ports is a map of envvar -> hostPort function validatePorts(ports, manifest) { @@ -280,7 +308,7 @@ function translateToPortBindings(ports, manifest) { const tcpPorts = manifest.tcpPorts || {}; for (const portName in ports) { - const portType = portName in tcpPorts ? exports.PORT_TYPE_TCP : exports.PORT_TYPE_UDP; + const portType = portName in tcpPorts ? PORT_TYPE_TCP : PORT_TYPE_UDP; const portCount = portName in tcpPorts ? tcpPorts[portName].portCount : manifest.udpPorts[portName].portCount; // since count is optional, this can be undefined portBindings[portName] = { hostPort: ports[portName], type: portType, count: portCount || 1 }; } @@ -587,10 +615,10 @@ function pickFields(app, accessLevel) { assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof accessLevel, 'string'); - if (accessLevel === exports.ACCESS_LEVEL_NONE) return null; // cannot happen! + if (accessLevel === ACCESS_LEVEL_NONE) return null; // cannot happen! let result; - if (accessLevel === exports.ACCESS_LEVEL_USER) { + if (accessLevel === ACCESS_LEVEL_USER) { result = _.pick(app, [ 'id', 'appStoreId', 'versionsUrl', 'installationState', 'error', 'runState', 'health', 'taskId', 'accessRestriction', 'secondaryDomains', 'redirectDomains', 'aliasDomains', 'sso', 'subdomain', 'domain', 'fqdn', 'certificate', @@ -784,7 +812,7 @@ function canAutoupdateAppSync(app, updateInfo) { return { code: false, reason: 'Major package version requires review of breaking changes' }; // major changes are blocking } - if (app.runState === exports.RSTATE_STOPPED) return { code: false, reason: 'Stopped apps cannot run migration scripts' }; + if (app.runState === RSTATE_STOPPED) return { code: false, reason: 'Stopped apps cannot run migration scripts' }; const newTcpPorts = manifest.tcpPorts || {}; const newUdpPorts = manifest.udpPorts || {}; @@ -849,9 +877,9 @@ function canAccess(app, user) { } function accessLevel(app, user) { - if (isAdmin(user)) return exports.ACCESS_LEVEL_ADMIN; - if (isOperator(app, user)) return exports.ACCESS_LEVEL_OPERATOR; - return canAccess(app, user) ? exports.ACCESS_LEVEL_USER : exports.ACCESS_LEVEL_NONE; + if (isAdmin(user)) return ACCESS_LEVEL_ADMIN; + if (isOperator(app, user)) return ACCESS_LEVEL_OPERATOR; + return canAccess(app, user) ? ACCESS_LEVEL_USER : ACCESS_LEVEL_NONE; } async function checkForPortBindingConflict(portBindings, options) { @@ -1243,10 +1271,10 @@ async function onTaskFinished(error, appId, installationState, taskId, auditSour if (!app || !task) return; switch (installationState) { - case exports.ISTATE_PENDING_DATA_DIR_MIGRATION: + case ISTATE_PENDING_DATA_DIR_MIGRATION: if (success) await safe(services.rebuildService('sftp', auditSource), { debug }); break; - case exports.ISTATE_PENDING_UPDATE: { + case ISTATE_PENDING_UPDATE: { const fromManifest = success ? task.args[1].updateConfig.manifest : app.manifest; const toManifest = success ? app.manifest : task.args[1].updateConfig.manifest; @@ -1264,11 +1292,11 @@ async function scheduleTask(appId, installationState, taskId, auditSource) { assert.strictEqual(typeof auditSource, 'object'); let memoryLimit = 400; - if (installationState === exports.ISTATE_PENDING_CLONE || installationState === exports.ISTATE_PENDING_RESTORE - || installationState === exports.ISTATE_PENDING_IMPORT || installationState === exports.ISTATE_PENDING_UPDATE) { + if (installationState === ISTATE_PENDING_CLONE || installationState === ISTATE_PENDING_RESTORE + || installationState === ISTATE_PENDING_IMPORT || installationState === ISTATE_PENDING_UPDATE) { const sites = await backupSites.listByContentForUpdates(appId); memoryLimit = sites.reduce((acc, cur) => cur.limits?.memoryLimit ? Math.max(cur.limits.memoryLimit/1024/1024, acc) : acc, 400); - } else if (installationState === exports.ISTATE_PENDING_DATA_DIR_MIGRATION) { + } else if (installationState === ISTATE_PENDING_DATA_DIR_MIGRATION) { memoryLimit = 1024; // cp takes more memory than we think } @@ -1286,8 +1314,8 @@ async function scheduleTask(appId, installationState, taskId, auditSource) { taskId, installationState }; - await safe(update(appId, { installationState: exports.ISTATE_ERROR, error: appError, taskId: null }), { debug }); - } else if (!(installationState === exports.ISTATE_PENDING_UNINSTALL && !error)) { // clear out taskId except for successful uninstall + await safe(update(appId, { installationState: ISTATE_ERROR, error: appError, taskId: null }), { debug }); + } else if (!(installationState === ISTATE_PENDING_UNINSTALL && !error)) { // clear out taskId except for successful uninstall await safe(update(appId, { taskId: null }), { debug }); } @@ -1303,7 +1331,7 @@ async function addTask(appId, installationState, task, auditSource) { const { args, values } = task; // TODO: match the SQL logic to match checkAppState. this means checking the error.installationState and installationState. Unfortunately, former is JSON right now - const requiredState = null; // 'requiredState' in task ? task.requiredState : exports.ISTATE_INSTALLED; + const requiredState = null; // 'requiredState' in task ? task.requiredState : ISTATE_INSTALLED; const scheduleNow = 'scheduleNow' in task ? task.scheduleNow : true; const requireNullTaskId = 'requireNullTaskId' in task ? task.requireNullTaskId : true; @@ -1324,17 +1352,17 @@ function checkAppState(app, state) { if (app.taskId) return new BoxError(BoxError.BAD_STATE, `Locked by task ${app.taskId} : ${app.installationState} / ${app.runState}`); - if (app.installationState === exports.ISTATE_ERROR) { + if (app.installationState === ISTATE_ERROR) { // allow task to be called again if that was the errored task if (app.error.installationState === state) return null; // allow uninstall from any state - if (state !== exports.ISTATE_PENDING_UNINSTALL && state !== exports.ISTATE_PENDING_RESTORE && state !== exports.ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in error state'); + if (state !== ISTATE_PENDING_UNINSTALL && state !== ISTATE_PENDING_RESTORE && state !== ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in error state'); } - if (app.runState === exports.RSTATE_STOPPED) { + if (app.runState === RSTATE_STOPPED) { // can't backup or restore since app addons are down. can't update because migration scripts won't run - if (state === exports.ISTATE_PENDING_UPDATE || state === exports.ISTATE_PENDING_RESTORE || state === exports.ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in stopped state'); + if (state === ISTATE_PENDING_UPDATE || state === ISTATE_PENDING_RESTORE || state === ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in stopped state'); } return null; @@ -1521,8 +1549,8 @@ async function install(data, auditSource) { enableRedis, notes, crontab, - runState: exports.RSTATE_RUNNING, - installationState: exports.ISTATE_PENDING_INSTALL + runState: RSTATE_RUNNING, + installationState: ISTATE_PENDING_INSTALL }; const [addError] = await safe(add(appId, appStoreId, versionsUrl, manifest, subdomain, domain, portBindings, app)); @@ -1681,7 +1709,7 @@ async function setMemoryLimit(app, memoryLimit, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_RESIZE); + let error = checkAppState(app, ISTATE_PENDING_RESIZE); if (error) throw error; error = validateMemoryLimit(app.manifest, memoryLimit); @@ -1691,7 +1719,7 @@ async function setMemoryLimit(app, memoryLimit, auditSource) { args: {}, values: { memoryLimit } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_RESIZE, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_RESIZE, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, memoryLimit, taskId }); @@ -1704,7 +1732,7 @@ async function setCpuQuota(app, cpuQuota, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_RESIZE); + let error = checkAppState(app, ISTATE_PENDING_RESIZE); if (error) throw error; error = validateCpuQuota(cpuQuota); @@ -1714,7 +1742,7 @@ async function setCpuQuota(app, cpuQuota, auditSource) { args: {}, values: { cpuQuota } }; - const taskId = await safe(addTask(appId, exports.ISTATE_PENDING_RESIZE, task, auditSource)); + const taskId = await safe(addTask(appId, ISTATE_PENDING_RESIZE, task, auditSource)); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, cpuQuota, taskId }); @@ -1727,14 +1755,14 @@ async function setMounts(app, mounts, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER); + const error = checkAppState(app, ISTATE_PENDING_RECREATE_CONTAINER); if (error) throw error; const task = { args: {}, values: { mounts } }; - const [taskError, taskId] = await safe(addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource)); + const [taskError, taskId] = await safe(addTask(appId, ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource)); if (taskError && taskError.reason === BoxError.ALREADY_EXISTS) throw new BoxError(BoxError.CONFLICT, 'Duplicate mount points'); if (taskError) throw taskError; @@ -1749,7 +1777,7 @@ async function setDevices(app, devices, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER); + let error = checkAppState(app, ISTATE_PENDING_RECREATE_CONTAINER); if (error) throw error; error = validateDevices(devices); @@ -1759,7 +1787,7 @@ async function setDevices(app, devices, auditSource) { args: {}, values: { devices } }; - const [taskError, taskId] = await safe(addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource)); + const [taskError, taskId] = await safe(addTask(appId, ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource)); if (taskError) throw taskError; await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, devices, taskId }); @@ -1773,7 +1801,7 @@ async function setEnvironment(app, env, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER); + let error = checkAppState(app, ISTATE_PENDING_RECREATE_CONTAINER); if (error) throw error; error = validateEnv(env); @@ -1783,7 +1811,7 @@ async function setEnvironment(app, env, auditSource) { args: {}, values: { env } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, env, taskId }); @@ -1796,7 +1824,7 @@ async function setDebugMode(app, debugMode, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_DEBUG); + let error = checkAppState(app, ISTATE_PENDING_DEBUG); if (error) throw error; error = validateDebugMode(debugMode); @@ -1806,7 +1834,7 @@ async function setDebugMode(app, debugMode, auditSource) { args: {}, values: { debugMode } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_DEBUG, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_DEBUG, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, debugMode, taskId }); @@ -1823,7 +1851,7 @@ async function setMailbox(app, data, auditSource) { const enableMailbox = data.enable; const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE); + let error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE); if (error) throw error; if (!app.manifest.addons?.sendmail) throw new BoxError(BoxError.BAD_FIELD, 'App does not use sendmail'); @@ -1854,7 +1882,7 @@ async function setMailbox(app, data, auditSource) { args: {}, values: { enableMailbox, mailboxName, mailboxDomain, mailboxDisplayName } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, mailboxName, mailboxDomain, mailboxDisplayName, taskId }); @@ -1870,7 +1898,7 @@ async function setInbox(app, data, auditSource) { const enableInbox = data.enable; const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE); + let error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE); if (error) throw error; if (!app.manifest.addons?.recvmail) throw new BoxError(BoxError.BAD_FIELD, 'App does not use recvmail addon'); @@ -1889,7 +1917,7 @@ async function setInbox(app, data, auditSource) { args: {}, values: { enableInbox, inboxName, inboxDomain } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, enableInbox, inboxName, inboxDomain, taskId }); @@ -1902,7 +1930,7 @@ async function setTurn(app, enableTurn, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE); + const error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE); if (error) throw error; if (!app.manifest.addons?.turn) throw new BoxError(BoxError.BAD_FIELD, 'App does not use turn addon'); @@ -1912,7 +1940,7 @@ async function setTurn(app, enableTurn, auditSource) { args: {}, values: { enableTurn } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, enableTurn, taskId }); @@ -1925,7 +1953,7 @@ async function setRedis(app, enableRedis, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE); + const error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE); if (error) throw error; if (!app.manifest.addons?.redis) throw new BoxError(BoxError.BAD_FIELD, 'App does not use redis addon'); @@ -1935,7 +1963,7 @@ async function setRedis(app, enableRedis, auditSource) { args: {}, values: { enableRedis } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, enableRedis, taskId }); @@ -2020,7 +2048,7 @@ async function setLocation(app, data, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_LOCATION_CHANGE); + let error = checkAppState(app, ISTATE_PENDING_LOCATION_CHANGE); if (error) throw error; const values = { @@ -2073,7 +2101,7 @@ async function setLocation(app, data, auditSource) { }, values }; - const [taskError, taskId] = await safe(addTask(appId, exports.ISTATE_PENDING_LOCATION_CHANGE, task, auditSource)); + const [taskError, taskId] = await safe(addTask(appId, ISTATE_PENDING_LOCATION_CHANGE, task, auditSource)); if (taskError && taskError.reason !== BoxError.ALREADY_EXISTS) throw taskError; if (taskError && taskError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(taskError.message, locations, values.portBindings); @@ -2094,7 +2122,7 @@ async function setStorage(app, volumeId, volumePrefix, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_DATA_DIR_MIGRATION); + const error = checkAppState(app, ISTATE_PENDING_DATA_DIR_MIGRATION); if (error) throw error; if (volumeId) { @@ -2107,7 +2135,7 @@ async function setStorage(app, volumeId, volumePrefix, auditSource) { args: { newStorageVolumeId: volumeId, newStorageVolumePrefix: volumePrefix }, values: {} }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_DATA_DIR_MIGRATION, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_DATA_DIR_MIGRATION, task, auditSource); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, volumeId, volumePrefix, taskId }); @@ -2123,9 +2151,9 @@ async function updateApp(app, data, auditSource) { const skipBackup = !!data.skipBackup, appId = app.id, manifest = data.manifest; const values = { updateInfo: null }; // clear update indicator immediately - if (app.runState === exports.RSTATE_STOPPED) throw new BoxError(BoxError.BAD_STATE, 'Stopped apps cannot be updated'); + if (app.runState === RSTATE_STOPPED) throw new BoxError(BoxError.BAD_STATE, 'Stopped apps cannot be updated'); - let error = checkAppState(app, exports.ISTATE_PENDING_UPDATE); + let error = checkAppState(app, ISTATE_PENDING_UPDATE); if (error) throw error; error = manifestFormat.parse(manifest); @@ -2192,7 +2220,7 @@ async function updateApp(app, data, auditSource) { args: { updateConfig }, values }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_UPDATE, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_UPDATE, task, auditSource); await eventlog.add(eventlog.ACTION_APP_UPDATE, auditSource, { appId, app, skipBackup, toManifest: manifest, fromManifest: app.manifest, force: data.force, taskId }); @@ -2268,7 +2296,7 @@ async function repair(app, data, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - let errorState = (app.error && app.error.installationState) || exports.ISTATE_PENDING_CONFIGURE; + let errorState = (app.error && app.error.installationState) || ISTATE_PENDING_CONFIGURE; const task = { args: {}, @@ -2277,7 +2305,7 @@ async function repair(app, data, auditSource) { }; // maybe split this into a separate route like reinstall? - if (errorState === exports.ISTATE_PENDING_INSTALL || errorState === exports.ISTATE_PENDING_CLONE) { + if (errorState === ISTATE_PENDING_INSTALL || errorState === ISTATE_PENDING_CLONE) { task.args = { skipDnsSetup: false, overwriteDns: true }; if (data.manifest) { let error = manifestFormat.parse(data.manifest); @@ -2297,7 +2325,7 @@ async function repair(app, data, auditSource) { task.args.oldManifest = app.manifest; } } else { - errorState = exports.ISTATE_PENDING_CONFIGURE; + errorState = ISTATE_PENDING_CONFIGURE; if (data.dockerImage) { const newManifest = Object.assign({}, app.manifest, { dockerImage: data.dockerImage }); task.values.manifest = newManifest; @@ -2318,7 +2346,7 @@ async function restore(app, backupId, auditSource) { const appId = app.id; - let error = checkAppState(app, exports.ISTATE_PENDING_RESTORE); + let error = checkAppState(app, ISTATE_PENDING_RESTORE); if (error) throw error; // for empty or null backupId, use existing manifest to mimic a reinstall @@ -2358,7 +2386,7 @@ async function restore(app, backupId, auditSource) { values }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_RESTORE, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_RESTORE, task, auditSource); await eventlog.add(eventlog.ACTION_APP_RESTORE, auditSource, { app, backupId: backup.id, remotePath: backup.remotePath, fromManifest: app.manifest, toManifest: manifest, taskId }); @@ -2372,7 +2400,7 @@ async function importApp(app, data, auditSource) { const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_IMPORT); + const error = checkAppState(app, ISTATE_PENDING_IMPORT); if (error) throw error; let restoreConfig; @@ -2401,7 +2429,7 @@ async function importApp(app, data, auditSource) { }, values: {} }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_IMPORT, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_IMPORT, task, auditSource); await eventlog.add(eventlog.ACTION_APP_IMPORT, auditSource, { app, remotePath: data.remotePath, inPlace: data.inPlace, taskId }); @@ -2479,8 +2507,8 @@ async function clone(app, data, user, auditSource) { if (!manifest.addons?.recvmail) dolly.inboxDomain = null; // needed because we are cloning _current_ app settings with old manifest const obj = Object.assign(dolly, { - installationState: exports.ISTATE_PENDING_CLONE, - runState: exports.RSTATE_RUNNING, + installationState: ISTATE_PENDING_CLONE, + runState: RSTATE_RUNNING, mailboxName, mailboxDomain, secondaryDomains, @@ -2497,9 +2525,9 @@ async function clone(app, data, user, auditSource) { const task = { args: { restoreConfig, overwriteDns, skipDnsSetup, oldManifest: null }, values: {}, - requiredState: exports.ISTATE_PENDING_CLONE + requiredState: ISTATE_PENDING_CLONE }; - const taskId = await addTask(newAppId, exports.ISTATE_PENDING_CLONE, task, auditSource); + const taskId = await addTask(newAppId, ISTATE_PENDING_CLONE, task, auditSource); const newApp = Object.assign({}, _.omit(obj, ['icon']), { appStoreId, versionsUrl, manifest, subdomain, domain, portBindings }); newApp.fqdn = dns.fqdn(newApp.subdomain, newApp.domain); @@ -2558,8 +2586,8 @@ async function unarchive(archive, data, auditSource) { redirectDomains: [], aliasDomains: [], mailboxDomain: data.domain, // archive's mailboxDomain may not exist - runState: exports.RSTATE_RUNNING, - installationState: exports.ISTATE_PENDING_INSTALL, + runState: RSTATE_RUNNING, + installationState: ISTATE_PENDING_INSTALL, sso: backup.appConfig ? backup.appConfig.sso : true // when no appConfig take a blind guess }); obj.icon = (await archives.getIcons(archive.id))?.icon; @@ -2592,7 +2620,7 @@ async function uninstall(app, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_UNINSTALL); + const error = checkAppState(app, ISTATE_PENDING_UNINSTALL); if (error) throw error; const task = { @@ -2600,7 +2628,7 @@ async function uninstall(app, auditSource) { values: {}, requiredState: null // can run in any state, as long as no task is active }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_UNINSTALL, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_UNINSTALL, task, auditSource); await eventlog.add(eventlog.ACTION_APP_UNINSTALL, auditSource, { appId, app, taskId }); return { taskId }; @@ -2628,14 +2656,14 @@ async function start(app, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_START); + const error = checkAppState(app, ISTATE_PENDING_START); if (error) throw error; const task = { args: {}, - values: { runState: exports.RSTATE_RUNNING } + values: { runState: RSTATE_RUNNING } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_START, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_START, task, auditSource); await eventlog.add(eventlog.ACTION_APP_START, auditSource, { appId, app, taskId }); return { taskId }; } @@ -2645,14 +2673,14 @@ async function stop(app, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_STOP); + const error = checkAppState(app, ISTATE_PENDING_STOP); if (error) throw error; const task = { args: {}, - values: { runState: exports.RSTATE_STOPPED } + values: { runState: RSTATE_STOPPED } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_STOP, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_STOP, task, auditSource); await eventlog.add(eventlog.ACTION_APP_STOP, auditSource, { appId, app, taskId }); @@ -2664,14 +2692,14 @@ async function restart(app, auditSource) { assert.strictEqual(typeof auditSource, 'object'); const appId = app.id; - const error = checkAppState(app, exports.ISTATE_PENDING_RESTART); + const error = checkAppState(app, ISTATE_PENDING_RESTART); if (error) throw error; const task = { args: {}, - values: { runState: exports.RSTATE_RUNNING } + values: { runState: RSTATE_RUNNING } }; - const taskId = await addTask(appId, exports.ISTATE_PENDING_RESTART, task, auditSource); + const taskId = await addTask(appId, ISTATE_PENDING_RESTART, task, auditSource); await eventlog.add(eventlog.ACTION_APP_RESTART, auditSource, { appId, app, taskId }); @@ -2706,7 +2734,7 @@ async function createExec(app, options) { const cmd = options.cmd || [ '/bin/bash' ]; assert(Array.isArray(cmd) && cmd.length > 0); - if (app.installationState !== exports.ISTATE_INSTALLED || app.runState !== exports.RSTATE_RUNNING) { + if (app.installationState !== ISTATE_INSTALLED || app.runState !== RSTATE_RUNNING) { throw new BoxError(BoxError.BAD_STATE, 'App not installed or running'); } @@ -2735,7 +2763,7 @@ async function startExec(app, execId, options) { assert.strictEqual(typeof execId, 'string'); assert(options && typeof options === 'object'); - if (app.installationState !== exports.ISTATE_INSTALLED || app.runState !== exports.RSTATE_RUNNING) { + if (app.installationState !== ISTATE_INSTALLED || app.runState !== RSTATE_RUNNING) { throw new BoxError(BoxError.BAD_STATE, 'App not installed or running'); } @@ -2778,13 +2806,13 @@ function canBackupApp(app) { // only backup apps that are installed or specific pending states // stopped apps cannot be backed up because addons might be down (redis) - if (app.runState === exports.RSTATE_STOPPED) return false; + if (app.runState === RSTATE_STOPPED) return false; // we used to check the health here but that doesn't work for stopped apps. it's better to just fail // and inform the user if the backup fails and the app addons have not been setup yet. - return app.installationState === exports.ISTATE_INSTALLED || - app.installationState === exports.ISTATE_PENDING_CONFIGURE || - app.installationState === exports.ISTATE_PENDING_UPDATE; // called from apptask + return app.installationState === ISTATE_INSTALLED || + app.installationState === ISTATE_PENDING_CONFIGURE || + app.installationState === ISTATE_PENDING_UPDATE; // called from apptask } async function backup(app, backupSiteId, auditSource) { @@ -2872,18 +2900,18 @@ async function restoreApps(apps, options, auditSource) { assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof auditSource, 'object'); - apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand - apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_RESTORE); // safeguard against tasks being created non-stop if we crash on startup + apps = apps.filter(app => app.installationState !== ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand + apps = apps.filter(app => app.installationState !== ISTATE_PENDING_RESTORE); // safeguard against tasks being created non-stop if we crash on startup for (const app of apps) { const [error, results] = await safe(backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, 1, 1)); let installationState, restoreConfig; if (!error && results.length) { - installationState = exports.ISTATE_PENDING_RESTORE; + installationState = ISTATE_PENDING_RESTORE; // intentionally ignore any backupSite provided during restore by the user because the site may not have all the apps restoreConfig = { backupId: results[0].id }; } else { - installationState = exports.ISTATE_PENDING_INSTALL; + installationState = ISTATE_PENDING_INSTALL; restoreConfig = null; } @@ -2907,8 +2935,8 @@ async function configureApps(apps, options, auditSource) { assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof auditSource, 'object'); - apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand - apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_CONFIGURE); // safeguard against tasks being created non-stop if we crash on startup + apps = apps.filter(app => app.installationState !== ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand + apps = apps.filter(app => app.installationState !== ISTATE_PENDING_CONFIGURE); // safeguard against tasks being created non-stop if we crash on startup const scheduleNow = !!options.scheduleNow; @@ -2922,7 +2950,7 @@ async function configureApps(apps, options, auditSource) { requireNullTaskId: false // ignore existing stale taskId }; - const [addTaskError, taskId] = await safe(addTask(app.id, exports.ISTATE_PENDING_CONFIGURE, task, auditSource)); + const [addTaskError, taskId] = await safe(addTask(app.id, ISTATE_PENDING_CONFIGURE, task, auditSource)); if (addTaskError) debug(`configureApps: error marking ${app.fqdn} for configure: ${JSON.stringify(addTaskError)}`); else debug(`configureApps: marked ${app.id} for re-configure with taskId ${taskId}`); } @@ -2935,23 +2963,23 @@ async function restartAppsUsingAddons(changedAddons, auditSource) { let apps = await list(); // TODO: This ends up restarting apps that have optional redis apps = apps.filter(app => app.manifest.addons && _.intersection(Object.keys(app.manifest.addons), changedAddons).length !== 0); - apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand - apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_RESTART); // safeguard against tasks being created non-stop restart if we crash on startup - apps = apps.filter(app => app.runState !== exports.RSTATE_STOPPED); // don't start stopped apps + apps = apps.filter(app => app.installationState !== ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand + apps = apps.filter(app => app.installationState !== ISTATE_PENDING_RESTART); // safeguard against tasks being created non-stop restart if we crash on startup + apps = apps.filter(app => app.runState !== RSTATE_STOPPED); // don't start stopped apps for (const app of apps) { debug(`restartAppsUsingAddons: marking ${app.fqdn} for restart`); const task = { args: {}, - values: { runState: exports.RSTATE_RUNNING } + values: { runState: RSTATE_RUNNING } }; // stop apps before updating the databases because postgres will "lock" them preventing import const [stopError] = await safe(docker.stopContainers(app.id)); if (stopError) debug(`restartAppsUsingAddons: error stopping ${app.fqdn}`, stopError); - const [addTaskError, taskId] = await safe(addTask(app.id, exports.ISTATE_PENDING_RESTART, task, auditSource)); + const [addTaskError, taskId] = await safe(addTask(app.id, ISTATE_PENDING_RESTART, task, auditSource)); if (addTaskError) debug(`restartAppsUsingAddons: error marking ${app.fqdn} for restart: ${JSON.stringify(addTaskError)}`); else debug(`restartAppsUsingAddons: marked ${app.id} for restart with taskId ${taskId}`); } diff --git a/src/appstore.js b/src/appstore.js index d2eb242d7..0ec6fad7c 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -1,57 +1,53 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import * as backupSites from './backupsites.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import * as domains from './domains.js'; +import * as dockerRegistries from './dockerregistries.js'; +import * as externalLdap from './externalldap.js'; +import * as groups from './groups.js'; +import * as mail from './mail.js'; +import manifestFormat from '@cloudron/manifest-format'; +import paths from './paths.js'; +import promiseRetry from './promise-retry.js'; +import safe from 'safetydance'; +import semver from 'semver'; +import * as settings from './settings.js'; +import superagent from '@cloudron/superagent'; +import * as system from './system.js'; +import * as users from './users.js'; +import * as volumes from './volumes.js'; -exports = module.exports = { +const debug = debugModule('box:appstore'); + +const _setApiServerOrigin = setApiServerOrigin; +const _unregister = unregister; + +export { getFeatures, - getApiServerOrigin, getWebServerOrigin, getConsoleServerOrigin, - downloadManifest, getApps, getApp, getAppVersion, downloadIcon, - registerCloudron3, updateCloudron, unlinkAccount, - getSubscription, - checkSubscription, // cron hook + checkSubscription, // cron hook, isFreePlan, - getAppUpdate, getBoxUpdate, - - // exported for tests - _setApiServerOrigin: setApiServerOrigin, - _unregister: unregister + _setApiServerOrigin, + _unregister, }; -const apps = require('./apps.js'), - assert = require('node:assert'), - backupSites = require('./backupsites.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:appstore'), - domains = require('./domains.js'), - dockerRegistries = require('./dockerregistries.js'), - externalLdap = require('./externalldap.js'), - groups = require('./groups.js'), - mail = require('./mail.js'), - manifestFormat = require('@cloudron/manifest-format'), - paths = require('./paths.js'), - promiseRetry = require('./promise-retry.js'), - safe = require('safetydance'), - semver = require('semver'), - settings = require('./settings.js'), - superagent = require('@cloudron/superagent'), - system = require('./system.js'), - users = require('./users.js'), - volumes = require('./volumes.js'); - // These are the default options and will be adjusted once a subscription state is obtained // Keep in sync with appstore/routes/cloudrons.js const DEFAULT_FEATURES = { diff --git a/src/apptask.js b/src/apptask.js index d0da29a5b..b4ecd80ab 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -1,46 +1,49 @@ #!/usr/bin/env node -'use strict'; +import apps from './apps.js'; +import * as appstore from './appstore.js'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import * as backupSites from './backupsites.js'; +import * as backuptask from './backuptask.js'; +import BoxError from './boxerror.js'; +import * as community from './community.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import * as df from './df.js'; +import * as dns from './dns.js'; +import * as docker from './docker.js'; +import ejs from 'ejs'; +import fs from 'node:fs'; +import { promises as fsPromises } from 'node:fs'; +import * as iputils from './iputils.js'; +import manifestFormat from '@cloudron/manifest-format'; +import os from 'node:os'; +import path from 'node:path'; +import paths from './paths.js'; +import promiseRetry from './promise-retry.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import services from './services.js'; +import shellModule from './shell.js'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:apptask'); +const shell = shellModule('apptask'); + +const _createAppDir = createAppDir; +const _deleteAppDir = deleteAppDir; +const _verifyManifest = verifyManifest; + +export { run, - - // exported for testing - _createAppDir: createAppDir, - _deleteAppDir: deleteAppDir, - _verifyManifest: verifyManifest, + _createAppDir, + _deleteAppDir, + _verifyManifest, }; -const apps = require('./apps.js'), - appstore = require('./appstore.js'), - assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - backupSites = require('./backupsites.js'), - backuptask = require('./backuptask.js'), - BoxError = require('./boxerror.js'), - community = require('./community.js'), - constants = require('./constants.js'), - debug = require('debug')('box:apptask'), - df = require('./df.js'), - dns = require('./dns.js'), - docker = require('./docker.js'), - ejs = require('ejs'), - fs = require('node:fs'), - fsPromises = require('node:fs').promises, - iputils = require('./iputils.js'), - manifestFormat = require('@cloudron/manifest-format'), - os = require('node:os'), - path = require('node:path'), - paths = require('./paths.js'), - promiseRetry = require('./promise-retry.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - services = require('./services.js'), - shell = require('./shell.js')('apptask'), - _ = require('./underscore.js'); - -const LOGROTATE_CONFIG_EJS = fs.readFileSync(__dirname + '/logrotate.ejs', { encoding: 'utf8' }), - CONFIGURE_LOGROTATE_CMD = path.join(__dirname, 'scripts/configurelogrotate.sh'); +const LOGROTATE_CONFIG_EJS = fs.readFileSync(import.meta.dirname + '/logrotate.ejs', { encoding: 'utf8' }), + CONFIGURE_LOGROTATE_CMD = path.join(import.meta.dirname, 'scripts/configurelogrotate.sh'); function makeTaskError(error, app) { assert(error instanceof BoxError); @@ -293,7 +296,6 @@ async function updateChecklist(app, newChecks, acknowledged = false) { await updateApp(app, { checklist }); } - async function startApp(app) { debug('startApp: starting container'); diff --git a/src/apptaskmanager.js b/src/apptaskmanager.js index d2df5cd1c..285c8453c 100644 --- a/src/apptaskmanager.js +++ b/src/apptaskmanager.js @@ -1,21 +1,21 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import debugModule from 'debug'; +import fs from 'node:fs'; +import locks from './locks.js'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import * as scheduler from './scheduler.js'; +import tasks from './tasks.js'; -exports = module.exports = { +const debug = debugModule('box:apptaskmanager'); + +export { start, scheduleTask }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - debug = require('debug')('box:apptaskmanager'), - fs = require('node:fs'), - locks = require('./locks.js'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - scheduler = require('./scheduler.js'), - tasks = require('./tasks.js'); - const gActiveTasks = {}; // indexed by app id const gPendingTasks = []; let gStarted = false; diff --git a/src/archives.js b/src/archives.js index 283e1e9ac..b580a3059 100644 --- a/src/archives.js +++ b/src/archives.js @@ -1,6 +1,11 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import eventlog from './eventlog.js'; +import safe from 'safetydance'; -exports = module.exports = { +export { get, getIcons, getIcon, @@ -10,13 +15,6 @@ exports = module.exports = { del, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - database = require('./database.js'), - eventlog = require('./eventlog.js'), - safe = require('safetydance'); - const ARCHIVE_FIELDS = [ 'archives.id', 'backupId', 'archives.creationTime', 'backups.remotePath', 'backups.siteId', 'backups.manifestJson', 'backups.appConfigJson', '(archives.icon IS NOT NULL) AS hasIcon', '(archives.packageIcon IS NOT NULL) AS hasPackageIcon' ]; function postProcess(result) { diff --git a/src/asynctask.js b/src/asynctask.js index c99a22c65..72ea344f7 100644 --- a/src/asynctask.js +++ b/src/asynctask.js @@ -1,8 +1,8 @@ -'use strict'; +import debugModule from 'debug'; +import EventEmitter from 'node:events'; +import safe from 'safetydance'; -const debug = require('debug')('box:asynctask'), - EventEmitter = require('node:events'), - safe = require('safetydance'); +const debug = debugModule('box:asynctask'); // this runs in-process class AsyncTask extends EventEmitter { @@ -42,6 +42,6 @@ class AsyncTask extends EventEmitter { } } -exports = module.exports = { +export { AsyncTask }; diff --git a/src/auditsource.js b/src/auditsource.js index 97027e654..eb57759b3 100644 --- a/src/auditsource.js +++ b/src/auditsource.js @@ -1,5 +1,3 @@ -'use strict'; - class AuditSource { constructor(username, userId, ip) { this.username = username || null; // this can be a real user or a module like cron/apptask/platform @@ -33,4 +31,4 @@ AuditSource.LDAP = new AuditSource('ldap'); AuditSource.MAIL = new AuditSource('mail'); AuditSource.PLATFORM = new AuditSource('platform'); -exports = module.exports = AuditSource; +export default AuditSource; diff --git a/src/backupcleaner.js b/src/backupcleaner.js index 4bd4225e7..b2182bce9 100644 --- a/src/backupcleaner.js +++ b/src/backupcleaner.js @@ -1,25 +1,25 @@ -'use strict'; +import BoxError from './boxerror.js'; +import apps from './apps.js'; +import * as archives from './archives.js'; +import assert from 'node:assert'; +import backups from './backups.js'; +import * as backupFormats from './backupformats.js'; +import * as backupSites from './backupsites.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import moment from 'moment'; +import path from 'node:path'; +import safe from 'safetydance'; -const BoxError = require('./boxerror.js'); +const debug = debugModule('box:backupcleaner'); -exports = module.exports = { +const _applyBackupRetention = applyBackupRetention; + +export { run, - - _applyBackupRetention: applyBackupRetention + _applyBackupRetention, }; -const apps = require('./apps.js'), - archives = require('./archives.js'), - assert = require('node:assert'), - backups = require('./backups.js'), - backupFormats = require('./backupformats.js'), - backupSites = require('./backupsites.js'), - constants = require('./constants.js'), - debug = require('debug')('box:backupcleaner'), - moment = require('moment'), - path = require('node:path'), - safe = require('safetydance'); - function applyBackupRetention(allBackups, retention, referencedBackupIds) { assert(Array.isArray(allBackups)); assert.strictEqual(typeof retention, 'object'); diff --git a/src/backupformat/rsync.js b/src/backupformat/rsync.js index 335b52e4a..1d0b5ecb8 100644 --- a/src/backupformat/rsync.js +++ b/src/backupformat/rsync.js @@ -1,37 +1,40 @@ -'use strict'; +import assert from 'node:assert'; +import async from 'async'; +import * as backupSites from '../backupsites.js'; +import BoxError from '../boxerror.js'; +import DataLayout from '../datalayout.js'; +import { DecryptStream } from '../hush.js'; +import debugModule from 'debug'; +import { EncryptStream } from '../hush.js'; +import fs from 'node:fs'; +import HashStream from '../hash-stream.js'; +import * as hush from '../hush.js'; +import path from 'node:path'; +import paths from '../paths.js'; +import ProgressStream from '../progress-stream.js'; +import promiseRetry from '../promise-retry.js'; +import safe from 'safetydance'; +import shellModule from '../shell.js'; +import stream from 'stream/promises'; +import * as syncer from '../syncer.js'; +import util from 'node:util'; -exports = module.exports = { +const debug = debugModule('box:backupformat/rsync'); +const shell = shellModule('backupformat/rsync'); + +const _saveFsMetadata = saveFsMetadata; +const _restoreFsMetadata = restoreFsMetadata; + +export { download, upload, verify, getFileExtension, copy, - - _saveFsMetadata: saveFsMetadata, - _restoreFsMetadata: restoreFsMetadata + _saveFsMetadata, + _restoreFsMetadata, }; -const assert = require('node:assert'), - async = require('async'), - backupSites = require('../backupsites.js'), - BoxError = require('../boxerror.js'), - DataLayout = require('../datalayout.js'), - { DecryptStream } = require('../hush.js'), - debug = require('debug')('box:backupformat/rsync'), - { EncryptStream } = require('../hush.js'), - fs = require('node:fs'), - HashStream = require('../hash-stream.js'), - hush = require('../hush.js'), - path = require('node:path'), - paths = require('../paths.js'), - ProgressStream = require('../progress-stream.js'), - promiseRetry = require('../promise-retry.js'), - safe = require('safetydance'), - shell = require('../shell.js')('backupformat/rsync'), - stream = require('stream/promises'), - syncer = require('../syncer.js'), - util = require('node:util'); - async function addFile(sourceFile, encryption, uploader, progressCallback) { assert.strictEqual(typeof sourceFile, 'string'); assert.strictEqual(typeof encryption, 'object'); diff --git a/src/backupformat/tgz.js b/src/backupformat/tgz.js index 76884840c..d8994a320 100644 --- a/src/backupformat/tgz.js +++ b/src/backupformat/tgz.js @@ -1,22 +1,22 @@ -'use strict'; +import assert from 'node:assert'; +import * as backupSites from '../backupsites.js'; +import BoxError from '../boxerror.js'; +import DataLayout from '../datalayout.js'; +import debugModule from 'debug'; +import { DecryptStream, EncryptStream } from '../hush.js'; +import fs from 'node:fs'; +import HashStream from '../hash-stream.js'; +import path from 'node:path'; +import ProgressStream from '../progress-stream.js'; +import promiseRetry from '../promise-retry.js'; +import safe from 'safetydance'; +import stream from 'stream/promises'; +import { Transform } from 'node:stream'; +import tar from 'tar-stream'; +import util from 'node:util'; +import zlib from 'node:zlib'; -const assert = require('node:assert'), - backupSites = require('../backupsites.js'), - BoxError = require('../boxerror.js'), - DataLayout = require('../datalayout.js'), - debug = require('debug')('box:backupformat/tgz'), - { DecryptStream, EncryptStream } = require('../hush.js'), - fs = require('node:fs'), - HashStream = require('../hash-stream.js'), - path = require('node:path'), - ProgressStream = require('../progress-stream.js'), - promiseRetry = require('../promise-retry.js'), - safe = require('safetydance'), - stream = require('stream/promises'), - { Transform } = require('node:stream'), - tar = require('tar-stream'), - util = require('node:util'), - zlib = require('node:zlib'); +const debug = debugModule('box:backupformat/tgz'); // In tar, the entry header contains the file size. If we don't provide it those many bytes, the tar will become corrupt // Linux provides no guarantee of how many bytes can be read from a file. This is the case with sqlite and log files @@ -345,13 +345,13 @@ function getFileExtension(encryption) { return encryption ? '.tar.gz.enc' : '.tar.gz'; } -exports = module.exports = { +const _EnsureFileSizeStream = EnsureFileSizeStream; + +export { download, upload, verify, getFileExtension, copy, - - // exported for testing - _EnsureFileSizeStream: EnsureFileSizeStream + _EnsureFileSizeStream, }; diff --git a/src/backupformats.js b/src/backupformats.js index 1674b0176..194e267b8 100644 --- a/src/backupformats.js +++ b/src/backupformats.js @@ -1,20 +1,19 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as tgzFormat from './backupformat/tgz.js'; +import * as rsyncFormat from './backupformat/rsync.js'; -exports = module.exports = { +export { api, validateFormat, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'); +const FORMATS = { tgz: tgzFormat, rsync: rsyncFormat }; function api(format) { assert.strictEqual(typeof format, 'string'); - switch (format) { - case 'tgz': return require('./backupformat/tgz.js'); - case 'rsync': return require('./backupformat/rsync.js'); - } + if (FORMATS[format]) return FORMATS[format]; throw new BoxError(BoxError.INTERNAL_ERROR, `Undefined format ${format}`); } diff --git a/src/backupintegrity.js b/src/backupintegrity.js index d472241dc..90b40f7bb 100644 --- a/src/backupintegrity.js +++ b/src/backupintegrity.js @@ -1,19 +1,19 @@ -'use strict'; +import assert from 'assert'; +import backups from './backups.js'; +import * as backupFormats from './backupformats.js'; +import * as backupSites from './backupsites.js'; +import BoxError from './boxerror.js'; +import consumers from 'node:stream/consumers'; +import crypto from 'node:crypto'; +import debugModule from 'debug'; +import safe from 'safetydance'; -exports = module.exports = { +const debug = debugModule('box:backupintegrity'); + +export { check }; -const assert = require('assert'), - backups = require('./backups.js'), - backupFormats = require('./backupformats.js'), - backupSites = require('./backupsites.js'), - BoxError = require('./boxerror'), - consumers = require('node:stream/consumers'), - crypto = require('node:crypto'), - debug = require('debug')('box:backupintegrity'), - safe = require('safetydance'); - async function downloadBackupInfo(backupSite, backup) { const stream = await backupSites.storageApi(backupSite).download(backupSite.config, `${backup.remotePath}.backupinfo`); const buffer = await consumers.buffer(stream); diff --git a/src/backups.js b/src/backups.js index 728482462..1f1fe0618 100644 --- a/src/backups.js +++ b/src/backups.js @@ -1,6 +1,18 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import hat from './hat.js'; +import safe from 'safetydance'; +import tasks from './tasks.js'; -exports = module.exports = { +const debug = debugModule('box:backups'); + +const BACKUP_TYPE_APP = 'app'; +const BACKUP_STATE_NORMAL = 'normal'; + +export default { get, getByIdentifierAndStatePaged, getLatestInTargetByIdentifier, // brutal function name @@ -19,24 +31,15 @@ exports = module.exports = { BACKUP_IDENTIFIER_BOX: 'box', BACKUP_IDENTIFIER_MAIL: 'mail', - BACKUP_TYPE_APP: 'app', + BACKUP_TYPE_APP, BACKUP_TYPE_BOX: 'box', BACKUP_TYPE_MAIL: 'mail', - BACKUP_STATE_NORMAL: 'normal', // should rename to created to avoid listing in UI? + BACKUP_STATE_NORMAL, BACKUP_STATE_CREATING: 'creating', BACKUP_STATE_ERROR: 'error', }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - database = require('./database.js'), - debug = require('debug')('box:backups'), - eventlog = require('./eventlog.js'), - hat = require('./hat.js'), - safe = require('safetydance'), - tasks = require('./tasks.js'); - const BACKUPS_FIELDS = [ 'id', 'remotePath', 'label', 'identifier', 'creationTime', 'packageVersion', 'type', 'integrityJson', 'statsJson', 'dependsOnJson', 'state', 'manifestJson', 'preserveSecs', 'encryptionVersion', 'appConfigJson', 'siteId', 'integrityCheckTaskId', 'lastIntegrityCheckTime', 'integrityCheckStatus', 'integrityCheckResultJson' ].join(','); @@ -85,7 +88,7 @@ async function add(data) { const creationTime = data.creationTime || new Date(); // allow tests to set the time const manifestJson = JSON.stringify(data.manifest); - const prefixId = data.type === exports.BACKUP_TYPE_APP ? `${data.type}_${data.identifier}` : data.type; // type and identifier are same for other types + const prefixId = data.type === BACKUP_TYPE_APP ? `${data.type}_${data.identifier}` : data.type; // type and identifier are same for other types const id = `${prefixId}_v${data.packageVersion}_${hat(32)}`; // id is used by the UI to derive dependent packages. making this a UUID will require a lot of db querying const appConfigJson = data.appConfig ? JSON.stringify(data.appConfig) : null; const statsJson = data.stats ? JSON.stringify(data.stats) : null; @@ -117,7 +120,7 @@ async function getLatestInTargetByIdentifier(identifier, siteId) { assert.strictEqual(typeof identifier, 'string'); assert.strictEqual(typeof siteId, 'string'); - const results = await database.query(`SELECT ${BACKUPS_FIELDS} FROM backups WHERE identifier = ? AND state = ? AND siteId = ? LIMIT 1`, [ identifier, exports.BACKUP_STATE_NORMAL, siteId ]); + const results = await database.query(`SELECT ${BACKUPS_FIELDS} FROM backups WHERE identifier = ? AND state = ? AND siteId = ? LIMIT 1`, [ identifier, BACKUP_STATE_NORMAL, siteId ]); if (!results.length) return null; return postProcess(results[0]); } diff --git a/src/backupsites.js b/src/backupsites.js index 9ab001298..1a4b00431 100644 --- a/src/backupsites.js +++ b/src/backupsites.js @@ -1,6 +1,26 @@ -'use strict'; +import assert from 'node:assert'; +import * as backupFormats from './backupformats.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as cron from './cron.js'; +import { CronTime } from 'cron'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import * as hush from './hush.js'; +import locks from './locks.js'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import tasks from './tasks.js'; +import * as storageFilesystem from './storage/filesystem.js'; +import * as storageS3 from './storage/s3.js'; +import * as storageGcs from './storage/gcs.js'; -exports = module.exports = { +const debug = debugModule('box:backups'); + +export { get, list, listByContentForUpdates, @@ -39,23 +59,6 @@ exports = module.exports = { reinitAll }; -const assert = require('node:assert'), - backupFormats = require('./backupformats.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - cron = require('./cron.js'), - { CronTime } = require('cron'), - crypto = require('node:crypto'), - database = require('./database.js'), - debug = require('debug')('box:backups'), - eventlog = require('./eventlog.js'), - hush = require('./hush.js'), - locks = require('./locks.js'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - tasks = require('./tasks.js'); - // format: rsync or tgz // provider: used to determine the api provider // config: depends on the 'provider' field. 'provider' is not stored in config object. but it is injected when calling the api backends @@ -68,39 +71,27 @@ const assert = require('node:assert'), // encryption: 'encryptionPassword' and 'encryptedFilenames' is converted into an 'encryption' object using hush.js. Password is lost forever after conversion. const BACKUP_TARGET_FIELDS = [ 'id', 'name', 'provider', 'configJson', 'limitsJson', 'retentionJson', 'schedule', 'encryptionJson', 'format', 'enableForUpdates', 'contentsJson', 'creationTime', 'ts', 'integrityKeyPairJson' ].join(','); +const STORAGE_PROVIDERS = { + nfs: storageFilesystem, cifs: storageFilesystem, sshfs: storageFilesystem, + mountpoint: storageFilesystem, disk: storageFilesystem, ext4: storageFilesystem, + xfs: storageFilesystem, filesystem: storageFilesystem, + s3: storageS3, minio: storageS3, 's3-v4-compat': storageS3, + 'digitalocean-spaces': storageS3, 'exoscale-sos': storageS3, wasabi: storageS3, + 'scaleway-objectstorage': storageS3, 'backblaze-b2': storageS3, 'cloudflare-r2': storageS3, + 'linode-objectstorage': storageS3, 'ovh-objectstorage': storageS3, + 'ionos-objectstorage': storageS3, 'idrive-e2': storageS3, + 'vultr-objectstorage': storageS3, 'upcloud-objectstorage': storageS3, + 'contabo-objectstorage': storageS3, 'hetzner-objectstorage': storageS3, + 'synology-c2-objectstorage': storageS3, + gcs: storageGcs +}; + function storageApi(backupSite) { assert.strictEqual(typeof backupSite, 'object'); - switch (backupSite.provider) { - case 'nfs': return require('./storage/filesystem.js'); - case 'cifs': return require('./storage/filesystem.js'); - case 'sshfs': return require('./storage/filesystem.js'); - case 'mountpoint': return require('./storage/filesystem.js'); - case 'disk': return require('./storage/filesystem.js'); - case 'ext4': return require('./storage/filesystem.js'); - case 'xfs': return require('./storage/filesystem.js'); - case 's3': return require('./storage/s3.js'); - case 'gcs': return require('./storage/gcs.js'); - case 'filesystem': return require('./storage/filesystem.js'); - case 'minio': return require('./storage/s3.js'); - case 's3-v4-compat': return require('./storage/s3.js'); - case 'digitalocean-spaces': return require('./storage/s3.js'); - case 'exoscale-sos': return require('./storage/s3.js'); - case 'wasabi': return require('./storage/s3.js'); - case 'scaleway-objectstorage': return require('./storage/s3.js'); - case 'backblaze-b2': return require('./storage/s3.js'); - case 'cloudflare-r2': return require('./storage/s3.js'); - case 'linode-objectstorage': return require('./storage/s3.js'); - case 'ovh-objectstorage': return require('./storage/s3.js'); - case 'ionos-objectstorage': return require('./storage/s3.js'); - case 'idrive-e2': return require('./storage/s3.js'); - case 'vultr-objectstorage': return require('./storage/s3.js'); - case 'upcloud-objectstorage': return require('./storage/s3.js'); - case 'contabo-objectstorage': return require('./storage/s3.js'); - case 'hetzner-objectstorage': return require('./storage/s3.js'); - case 'synology-c2-objectstorage': return require('./storage/s3.js'); - default: throw new BoxError(BoxError.BAD_FIELD, `Unknown provider: ${backupSite.provider}`); - } + const provider = STORAGE_PROVIDERS[backupSite.provider]; + if (!provider) throw new BoxError(BoxError.BAD_FIELD, `Unknown provider: ${backupSite.provider}`); + return provider; } function postProcess(result) { diff --git a/src/backuptask.js b/src/backuptask.js index e7667fb1f..a314bdb14 100644 --- a/src/backuptask.js +++ b/src/backuptask.js @@ -1,6 +1,29 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import * as backupFormats from './backupformats.js'; +import backups from './backups.js'; +import * as backupSites from './backupsites.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import crypto from 'node:crypto'; +import DataLayout from './datalayout.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import * as df from './df.js'; +import locks from './locks.js'; +import path from 'node:path'; +import paths from './paths.js'; +import { Readable } from 'node:stream'; +import safe from 'safetydance'; +import services from './services.js'; +import shellModule from './shell.js'; +import stream from 'stream/promises'; +import util from 'util'; -exports = module.exports = { +const debug = debugModule('box:backuptask'); +const shell = shellModule('backuptask'); + +export { fullBackup, appBackup, @@ -14,29 +37,7 @@ exports = module.exports = { upload, }; -const apps = require('./apps.js'), - assert = require('node:assert'), - backupFormats = require('./backupformats.js'), - backups = require('./backups.js'), - backupSites = require('./backupsites.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - crypto = require('node:crypto'), - DataLayout = require('./datalayout.js'), - database = require('./database.js'), - debug = require('debug')('box:backuptask'), - df = require('./df.js'), - locks = require('./locks.js'), - path = require('node:path'), - paths = require('./paths.js'), - { Readable } = require('node:stream'), - safe = require('safetydance'), - services = require('./services.js'), - shell = require('./shell.js')('backuptask'), - stream = require('stream/promises'), - util = require('util'); - -const BACKUP_UPLOAD_CMD = path.join(__dirname, 'scripts/backupupload.js'); +const BACKUP_UPLOAD_CMD = path.join(import.meta.dirname, 'scripts/backupupload.js'); function addFileExtension(backupSite, remotePath) { assert.strictEqual(typeof backupSite, 'object'); diff --git a/src/blobs.js b/src/blobs.js index d72e966bb..eef3c841b 100644 --- a/src/blobs.js +++ b/src/blobs.js @@ -1,8 +1,12 @@ /* jslint node:true */ -'use strict'; +import assert from 'node:assert'; +import * as database from './database.js'; -exports = module.exports = { +const CERT_PREFIX = 'cert'; +const CERT_SUFFIX = 'cert'; + +export default { get, getString, set, @@ -25,15 +29,11 @@ exports = module.exports = { OIDC_KEY_EDDSA: 'oidc_key_eddsa', // this is only JWT private key, the public key will be derived OIDC_KEY_RS256: 'oidc_key_rs256', - CERT_PREFIX: 'cert', - CERT_SUFFIX: 'cert', - + CERT_PREFIX, + CERT_SUFFIX, _clear: clear }; -const assert = require('node:assert'), - database = require('./database.js'); - const BLOBS_FIELDS = [ 'id', 'value' ].join(','); async function get(id) { @@ -75,6 +75,6 @@ async function clear() { } async function listCertIds() { - const result = await database.query('SELECT id FROM blobs WHERE id LIKE ?', [ `${exports.CERT_PREFIX}-%.${exports.CERT_SUFFIX}` ]); + const result = await database.query('SELECT id FROM blobs WHERE id LIKE ?', [ `${CERT_PREFIX}-%.${CERT_SUFFIX}` ]); return result.map(r => r.id); } diff --git a/src/boxerror.js b/src/boxerror.js index dafd25531..785a5d404 100644 --- a/src/boxerror.js +++ b/src/boxerror.js @@ -1,12 +1,10 @@ /* jslint node:true */ -'use strict'; +import assert from 'node:assert'; +import { HttpError } from '@cloudron/connect-lastmile'; +import util from 'node:util'; -const assert = require('node:assert'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - util = require('node:util'); - -exports = module.exports = BoxError; +export default BoxError; function BoxError(reason, errorOrMessage, extra = {}) { assert.strictEqual(typeof reason, 'string'); diff --git a/src/branding.js b/src/branding.js index 4bb73b4d6..0d87092f6 100644 --- a/src/branding.js +++ b/src/branding.js @@ -1,6 +1,16 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import paths from './paths.js'; +import safe from 'safetydance'; +import * as settings from './settings.js'; -exports = module.exports = { +const debug = debugModule('box:branding'); + +export { getCloudronName, setCloudronName, @@ -16,16 +26,6 @@ exports = module.exports = { renderFooter }; -const apps = require('./apps.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:branding'), - eventlog = require('./eventlog.js'), - paths = require('./paths.js'), - safe = require('safetydance'), - settings = require('./settings.js'); - async function getCloudronName() { const name = await settings.get(settings.CLOUDRON_NAME_KEY); return name || 'Cloudron'; diff --git a/src/changelog.js b/src/changelog.js index 3bd4b897c..8d05c6f51 100644 --- a/src/changelog.js +++ b/src/changelog.js @@ -1,18 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import fs from 'node:fs'; +import path from 'node:path'; -exports = module.exports = { +export { getChanges }; -const assert = require('node:assert'), - fs = require('node:fs'), - path = require('node:path'); - function getChanges(version) { assert.strictEqual(typeof version, 'string'); const changelog = []; - const lines = fs.readFileSync(path.join(__dirname, '../CHANGES'), 'utf8').split('\n'); + const lines = fs.readFileSync(path.join(import.meta.dirname, '../CHANGES'), 'utf8').split('\n'); version = version.replace(/[+-].*/, ''); // strip prerelease diff --git a/src/cloudron.js b/src/cloudron.js index 6f61de535..2016d1181 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as cron from './cron.js'; +import moment from 'moment-timezone'; +import * as settings from './settings.js'; +import * as translations from './translations.js'; -exports = module.exports = { +export { getStatus, getTimeZone, @@ -10,14 +16,6 @@ exports = module.exports = { setLanguage, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - cron = require('./cron.js'), - moment = require('moment-timezone'), - settings = require('./settings.js'), - translations = require('./translations.js'); - async function getStatus() { return { version: constants.VERSION, diff --git a/src/community.js b/src/community.js index 8b0c8541a..8f1a68c5b 100644 --- a/src/community.js +++ b/src/community.js @@ -1,20 +1,20 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import debugModule from 'debug'; +import manifestFormat from '@cloudron/manifest-format'; +import promiseRetry from './promise-retry.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; -exports = module.exports = { +const debug = debugModule('box:community'); + +export { getAppVersion, downloadManifest, getAppUpdate, downloadIcon }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - debug = require('debug')('box:community'), - manifestFormat = require('@cloudron/manifest-format'), - promiseRetry = require('./promise-retry.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - async function getAppVersion(url, version) { assert.strictEqual(typeof url, 'string'); assert.strictEqual(typeof version, 'string'); diff --git a/src/constants.js b/src/constants.js index 7d31dff15..224b84302 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,12 +1,10 @@ -'use strict'; - -const fs = require('node:fs'), - path = require('node:path'); +import fs from 'node:fs'; +import path from 'node:path'; const CLOUDRON = process.env.BOX_ENV === 'cloudron', TEST = process.env.BOX_ENV === 'test'; -exports = module.exports = { +export default { SMTP_SUBDOMAIN: 'smtp', IMAP_SUBDOMAIN: 'imap', @@ -90,6 +88,6 @@ exports = module.exports = { FOOTER: '© %YEAR% [Cloudron](https://cloudron.io)', - VERSION: process.env.BOX_ENV === 'cloudron' ? fs.readFileSync(path.join(__dirname, '../VERSION'), 'utf8').trim() : '8.0.0-test' + VERSION: process.env.BOX_ENV === 'cloudron' ? fs.readFileSync(path.join(import.meta.dirname, '../VERSION'), 'utf8').trim() : '8.0.0-test' }; diff --git a/src/cron.js b/src/cron.js index d6e2f65f6..952b015fd 100644 --- a/src/cron.js +++ b/src/cron.js @@ -1,4 +1,30 @@ -'use strict'; +import * as appHealthMonitor from './apphealthmonitor.js'; +import assert from 'node:assert'; +import * as appstore from './appstore.js'; +import AuditSource from './auditsource.js'; +import * as backupSites from './backupsites.js'; +import * as cloudron from './cloudron.js'; +import constants from './constants.js'; +import { CronJob } from 'cron'; +import debugModule from 'debug'; +import * as domains from './domains.js'; +import * as dyndns from './dyndns.js'; +import * as externalLdap from './externalldap.js'; +import eventlog from './eventlog.js'; +import * as janitor from './janitor.js'; +import * as mail from './mail.js'; +import * as metrics from './metrics.js'; +import * as network from './network.js'; +import * as oidcServer from './oidcserver.js'; +import paths from './paths.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import * as scheduler from './scheduler.js'; +import * as system from './system.js'; +import * as updater from './updater.js'; +import util from 'node:util'; + +const debug = debugModule('box:cron'); // IMPORTANT: These patterns are together because they spin tasks which acquire a lock // If the patterns overlap all the time, then the task may not ever get a chance to run! @@ -6,7 +32,7 @@ const DEFAULT_CLEANUP_BACKUPS_PATTERN = '00 30 1,3,5,23 * * *', DEFAULT_AUTOUPDATE_PATTERN = '00 00 1,3,5,23 * * *'; -exports = module.exports = { +export { startJobs, stopJobs, @@ -20,32 +46,6 @@ exports = module.exports = { DEFAULT_AUTOUPDATE_PATTERN, }; -const appHealthMonitor = require('./apphealthmonitor.js'), - assert = require('node:assert'), - appstore = require('./appstore.js'), - AuditSource = require('./auditsource.js'), - backupSites = require('./backupsites.js'), - cloudron = require('./cloudron.js'), - constants = require('./constants.js'), - { CronJob } = require('cron'), - debug = require('debug')('box:cron'), - domains = require('./domains.js'), - dyndns = require('./dyndns.js'), - externalLdap = require('./externalldap.js'), - eventlog = require('./eventlog.js'), - janitor = require('./janitor.js'), - mail = require('./mail.js'), - metrics = require('./metrics.js'), - network = require('./network.js'), - oidcServer = require('./oidcserver.js'), - paths = require('./paths.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - scheduler = require('./scheduler.js'), - system = require('./system.js'), - updater = require('./updater.js'), - util = require('node:util'); - const gJobs = { autoUpdater: null, backups: new Map(), diff --git a/src/dashboard.js b/src/dashboard.js index 831a46f79..84db4e894 100644 --- a/src/dashboard.js +++ b/src/dashboard.js @@ -1,39 +1,38 @@ -'use strict'; +import apps from './apps.js'; +import * as appstore from './appstore.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as branding from './branding.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import * as externalLdap from './externalldap.js'; +import eventlog from './eventlog.js'; +import Location from './location.js'; +import * as mailServer from './mailserver.js'; +import * as platform from './platform.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import * as system from './system.js'; +import tasks from './tasks.js'; +import * as userDirectory from './user-directory.js'; -exports = module.exports = { +const debug = debugModule('box:dashboard'); + +const _setLocation = setLocation; + +export { getLocation, clearLocation, - - startPrepareLocation, // starts the task - prepareLocation, // the task to setup dns and cert - setupLocation, // initial setup from setup/restore - changeLocation, // only on dashboard change (post setup/restore) - + startPrepareLocation, // starts the task, + prepareLocation, // the task to setup dns and cert, + setupLocation, // initial setup from setup/restore, + changeLocation, // only on dashboard change (post setup/restore), getConfig, - - _setLocation: setLocation, + _setLocation, }; -const apps = require('./apps.js'), - appstore = require('./appstore.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - branding = require('./branding.js'), - constants = require('./constants.js'), - debug = require('debug')('box:dashboard'), - dns = require('./dns.js'), - externalLdap = require('./externalldap.js'), - eventlog = require('./eventlog.js'), - Location = require('./location.js'), - mailServer = require('./mailserver.js'), - platform = require('./platform.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - settings = require('./settings.js'), - system = require('./system.js'), - tasks = require('./tasks.js'), - userDirectory = require('./user-directory.js'); - async function getLocation() { const domain = await settings.get(settings.DASHBOARD_DOMAIN_KEY); const subdomain = await settings.get(settings.DASHBOARD_SUBDOMAIN_KEY); diff --git a/src/database.js b/src/database.js index b3545c22c..82c7abe06 100644 --- a/src/database.js +++ b/src/database.js @@ -1,27 +1,28 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import { execSync } from 'node:child_process'; +import mysql from 'mysql2/promise'; +import safe from 'safetydance'; +import shellModule from './shell.js'; -exports = module.exports = { +const debug = debugModule('box:database'); +const shell = shellModule('database'); + +const _clear = clear; + +export { initialize, uninitialize, query, transaction, runInTransaction, - importFromFile, exportToFile, - - _clear: clear + _clear, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:database'), - execSync = require('node:child_process').execSync, - mysql = require('mysql2/promise'), - safe = require('safetydance'), - shell = require('./shell.js')('database'); - let gConnectionPool = null; const gDatabase = { diff --git a/src/datalayout.js b/src/datalayout.js index ed1d78d08..14dbde764 100644 --- a/src/datalayout.js +++ b/src/datalayout.js @@ -1,7 +1,5 @@ -'use strict'; - -const assert = require('node:assert'), - path = require('node:path'); +import assert from 'node:assert'; +import path from 'node:path'; class DataLayout { constructor(localRoot, dirMap) { @@ -54,4 +52,4 @@ class DataLayout { } } -exports = module.exports = DataLayout; +export default DataLayout; diff --git a/src/df.js b/src/df.js index 45a06cd45..ccfccdb43 100644 --- a/src/df.js +++ b/src/df.js @@ -1,17 +1,18 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import debugModule from 'debug'; +import safe from 'safetydance'; +import shellModule from './shell.js'; -exports = module.exports = { +const debug = debugModule('box:df'); +const shell = shellModule('df'); + +export { filesystems, file, prettyBytes }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - debug = require('debug')('box:df'), - safe = require('safetydance'), - shell = require('./shell.js')('df'); - // binary units (non SI) 1024 based function prettyBytes(bytes) { assert.strictEqual(typeof bytes, 'number'); diff --git a/src/dig.js b/src/dig.js index 9cde53db6..31c1403e8 100644 --- a/src/dig.js +++ b/src/dig.js @@ -1,13 +1,23 @@ -'use strict'; +import assert from 'node:assert'; +import constants from './constants.js'; +import dns from 'node:dns'; -exports = module.exports = { - resolve, +let _mockResolve = null; + +function _setMockResolve(fn) { + _mockResolve = fn; +} + +function resolveWrapper(hostname, rrtype, options) { + if (_mockResolve) return _mockResolve(hostname, rrtype, options); + return resolve(hostname, rrtype, options); +} + +export { + resolveWrapper as resolve, + _setMockResolve, }; -const assert = require('node:assert'), - constants = require('./constants.js'), - dns = require('node:dns'); - // a note on TXT records. It doesn't have quotes ("") at the DNS level. Those quotes // are added for DNS server software to enclose spaces. Such quotes may also be returned // by the DNS REST API of some providers diff --git a/src/directoryserver.js b/src/directoryserver.js index 19608546a..53257ac33 100644 --- a/src/directoryserver.js +++ b/src/directoryserver.js @@ -1,6 +1,25 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import * as ipaddr from './ipaddr.js'; +import * as groups from './groups.js'; +import ldap from 'ldapjs'; +import path from 'node:path'; +import paths from './paths.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import shellModule from './shell.js'; +import * as users from './users.js'; +import util from 'node:util'; -exports = module.exports = { +const debug = debugModule('box:directoryserver'); +const shell = shellModule('directoryserver'); + +export { getConfig, setConfig, @@ -10,29 +29,11 @@ exports = module.exports = { checkCertificate, }; -const assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:directoryserver'), - eventlog = require('./eventlog.js'), - ipaddr = require('./ipaddr.js'), - groups = require('./groups.js'), - ldap = require('ldapjs'), - path = require('node:path'), - paths = require('./paths.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - settings = require('./settings.js'), - shell = require('./shell.js')('directoryserver'), - users = require('./users.js'), - util = require('node:util'); - let gServer = null, gCertificate = null; const NOOP = function () {}; -const SET_LDAP_ALLOWLIST_CMD = path.join(__dirname, 'scripts/setldapallowlist.sh'); +const SET_LDAP_ALLOWLIST_CMD = path.join(import.meta.dirname, 'scripts/setldapallowlist.sh'); async function getConfig() { const value = await settings.get(settings.DIRECTORY_SERVER_KEY); diff --git a/src/dns.js b/src/dns.js index a0dc101c2..195a91279 100644 --- a/src/dns.js +++ b/src/dns.js @@ -1,6 +1,44 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import * as domains from './domains.js'; +import * as ipaddr from './ipaddr.js'; +import * as mail from './mail.js'; +import * as mailServer from './mailserver.js'; +import * as network from './network.js'; +import promiseRetry from './promise-retry.js'; +import safe from 'safetydance'; +import tasks from './tasks.js'; +import tld from 'tldjs'; +import * as dnsBunny from './dns/bunny.js'; +import * as dnsCloudflare from './dns/cloudflare.js'; +import * as dnsDesec from './dns/desec.js'; +import * as dnsDnsimple from './dns/dnsimple.js'; +import * as dnsRoute53 from './dns/route53.js'; +import * as dnsGcdns from './dns/gcdns.js'; +import * as dnsDigitalocean from './dns/digitalocean.js'; +import * as dnsGandi from './dns/gandi.js'; +import * as dnsGodaddy from './dns/godaddy.js'; +import * as dnsInwx from './dns/inwx.js'; +import * as dnsLinode from './dns/linode.js'; +import * as dnsVultr from './dns/vultr.js'; +import * as dnsNamecom from './dns/namecom.js'; +import * as dnsNamecheap from './dns/namecheap.js'; +import * as dnsNetcup from './dns/netcup.js'; +import * as dnsHetzner from './dns/hetzner.js'; +import * as dnsHetznercloud from './dns/hetznercloud.js'; +import * as dnsNoop from './dns/noop.js'; +import * as dnsManual from './dns/manual.js'; +import * as dnsOvh from './dns/ovh.js'; +import * as dnsPorkbun from './dns/porkbun.js'; +import * as dnsWildcard from './dns/wildcard.js'; -exports = module.exports = { +const debug = debugModule('box:dns'); + +export { fqdn, getName, @@ -23,51 +61,20 @@ exports = module.exports = { startSyncDnsRecords }; -const apps = require('./apps.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:dns'), - domains = require('./domains.js'), - ipaddr = require('./ipaddr.js'), - mail = require('./mail.js'), - mailServer = require('./mailserver.js'), - network = require('./network.js'), - promiseRetry = require('./promise-retry.js'), - safe = require('safetydance'), - tasks = require('./tasks.js'), - tld = require('tldjs'); +const DNS_PROVIDERS = { + bunny: dnsBunny, cloudflare: dnsCloudflare, desec: dnsDesec, dnsimple: dnsDnsimple, + route53: dnsRoute53, gcdns: dnsGcdns, digitalocean: dnsDigitalocean, gandi: dnsGandi, + godaddy: dnsGodaddy, inwx: dnsInwx, linode: dnsLinode, vultr: dnsVultr, + namecom: dnsNamecom, namecheap: dnsNamecheap, netcup: dnsNetcup, hetzner: dnsHetzner, + hetznercloud: dnsHetznercloud, noop: dnsNoop, manual: dnsManual, ovh: dnsOvh, + porkbun: dnsPorkbun, wildcard: dnsWildcard +}; // choose which subdomain backend we use for test purpose we use route53 function api(provider) { assert.strictEqual(typeof provider, 'string'); - switch (provider) { - case 'bunny': return require('./dns/bunny.js'); - case 'cloudflare': return require('./dns/cloudflare.js'); - case 'desec': return require('./dns/desec.js'); - case 'dnsimple': return require('./dns/dnsimple.js'); - case 'route53': return require('./dns/route53.js'); - case 'gcdns': return require('./dns/gcdns.js'); - case 'digitalocean': return require('./dns/digitalocean.js'); - case 'gandi': return require('./dns/gandi.js'); - case 'godaddy': return require('./dns/godaddy.js'); - case 'inwx': return require('./dns/inwx.js'); - case 'linode': return require('./dns/linode.js'); - case 'vultr': return require('./dns/vultr.js'); - case 'namecom': return require('./dns/namecom.js'); - case 'namecheap': return require('./dns/namecheap.js'); - case 'netcup': return require('./dns/netcup.js'); - case 'hetzner': return require('./dns/hetzner.js'); - case 'hetznercloud': return require('./dns/hetznercloud.js'); - case 'noop': return require('./dns/noop.js'); - case 'manual': return require('./dns/manual.js'); - case 'ovh': return require('./dns/ovh.js'); - case 'porkbun': return require('./dns/porkbun.js'); - case 'wildcard': return require('./dns/wildcard.js'); - default: return null; - } + return DNS_PROVIDERS[provider] || null; } function fqdn(subdomain, domain) { @@ -373,7 +380,6 @@ async function syncDnsRecords(options, progressCallback) { return { errors }; } - async function startSyncDnsRecords(options) { assert.strictEqual(typeof options, 'object'); diff --git a/src/dns/bunny.js b/src/dns/bunny.js index 35a5b2fbc..e62b41cea 100644 --- a/src/dns/bunny.js +++ b/src/dns/bunny.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/bunny'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/bunny'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const BUNNY_API = 'https://api.bunny.net'; const RECORD_TYPES = [ 'A', 'AAAA', 'CNAME', 'TXT', 'MX', 'RDR', '???', 'PZ', 'SRV', 'CAA', 'PTR', 'SCR', 'NS' ]; diff --git a/src/dns/cloudflare.js b/src/dns/cloudflare.js index eba49e3df..3bc53a506 100644 --- a/src/dns/cloudflare.js +++ b/src/dns/cloudflare.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; +import * as _ from '../underscore.js'; -exports = module.exports = { +const debug = debugModule('box:dns/cloudflare'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,17 +21,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/cloudflare'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'), - _ = require('../underscore.js'); - // we are using latest v4 stable API https://api.cloudflare.com/#getting-started-endpoints const CLOUDFLARE_ENDPOINT = 'https://api.cloudflare.com/client/v4'; diff --git a/src/dns/desec.js b/src/dns/desec.js index d8e929a1c..260dc801b 100644 --- a/src/dns/desec.js +++ b/src/dns/desec.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import constants from '../constants.js'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import timers from 'timers/promises'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/desec'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,17 +21,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - constants = require('../constants.js'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:dns/desec'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - timers = require('timers/promises'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const DESEC_ENDPOINT = 'https://desec.io/api/v1'; function formatError(response) { diff --git a/src/dns/digitalocean.js b/src/dns/digitalocean.js index d857905d6..f5522e34c 100644 --- a/src/dns/digitalocean.js +++ b/src/dns/digitalocean.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/digitalocean'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/digitalocean'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const DIGITALOCEAN_ENDPOINT = 'https://api.digitalocean.com'; function formatError(response) { diff --git a/src/dns/dnsimple.js b/src/dns/dnsimple.js index 2630c8c09..63f94f049 100644 --- a/src/dns/dnsimple.js +++ b/src/dns/dnsimple.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/dnsimple'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/dnsimple'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const DNSIMPLE_API = 'https://api.dnsimple.com/v2'; function formatError(response) { diff --git a/src/dns/gandi.js b/src/dns/gandi.js index dce45586a..fa966d205 100644 --- a/src/dns/gandi.js +++ b/src/dns/gandi.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/gandi'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/gandi'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const GANDI_API = 'https://dns.api.gandi.net/api/v5'; function formatError(response) { diff --git a/src/dns/gcdns.js b/src/dns/gcdns.js index 52473e7ef..04d32c12c 100644 --- a/src/dns/gcdns.js +++ b/src/dns/gcdns.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import { DNS as GCDNS } from '@google-cloud/dns'; +import safe from 'safetydance'; +import waitForDns from './waitfordns.js'; +import * as _ from '../underscore.js'; -exports = module.exports = { +const debug = debugModule('box:dns/gcdns'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,17 +21,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/gcdns'), - dig = require('../dig.js'), - dns = require('../dns.js'), - GCDNS = require('@google-cloud/dns').DNS, - safe = require('safetydance'), - waitForDns = require('./waitfordns.js'), - _ = require('../underscore.js'); - function removePrivateFields(domainObject) { delete domainObject.config.credentials.private_key; return domainObject; diff --git a/src/dns/godaddy.js b/src/dns/godaddy.js index c0e8fa08c..2939943f4 100644 --- a/src/dns/godaddy.js +++ b/src/dns/godaddy.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/godaddy'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/godaddy'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - // const GODADDY_API_OTE = 'https://api.ote-godaddy.com/v1/domains'; const GODADDY_API = 'https://api.godaddy.com/v1/domains'; diff --git a/src/dns/hetzner.js b/src/dns/hetzner.js index e3afa2805..e1b1c3421 100644 --- a/src/dns/hetzner.js +++ b/src/dns/hetzner.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/hetzner'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/hetzner'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const ENDPOINT = 'https://dns.hetzner.com/api/v1'; function formatError(response) { diff --git a/src/dns/hetznercloud.js b/src/dns/hetznercloud.js index 4b47bf898..03a0f3795 100644 --- a/src/dns/hetznercloud.js +++ b/src/dns/hetznercloud.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import promiseRetry from '../promise-retry.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/hetznercloud'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,17 +21,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/hetznercloud'), - dig = require('../dig.js'), - dns = require('../dns.js'), - promiseRetry = require('../promise-retry.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - // https://docs.hetzner.cloud/reference/cloud const ENDPOINT = 'https://api.hetzner.cloud/v1'; diff --git a/src/dns/interface.js b/src/dns/interface.js index 99014ffb0..35b29f29f 100644 --- a/src/dns/interface.js +++ b/src/dns/interface.js @@ -1,4 +1,5 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; // ------------------------------------------- // This file just describes the interface @@ -6,7 +7,7 @@ // New backends can start from here // ------------------------------------------- -exports = module.exports = { +export { removePrivateFields, injectPrivateFields, upsert, @@ -16,9 +17,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'); - function removePrivateFields(domainObject) { // in-place removal of tokens and api keys return domainObject; diff --git a/src/dns/inwx.js b/src/dns/inwx.js index aa8a20d31..66beeb5c6 100644 --- a/src/dns/inwx.js +++ b/src/dns/inwx.js @@ -1,6 +1,16 @@ -'use strict'; +import { ApiClient, Language } from 'domrobot-client'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/inwx'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const { ApiClient, Language } = require('domrobot-client'), - assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/inwx'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - waitForDns = require('./waitfordns.js'); - function formatError(response) { return `INWX Api error error [Code: [${response.code}] Message: ${response.msg}`; } diff --git a/src/dns/linode.js b/src/dns/linode.js index 94f344d43..39d5f853b 100644 --- a/src/dns/linode.js +++ b/src/dns/linode.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import constants from '../constants.js'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/linode'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - constants = require('../constants.js'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:dns/linode'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const LINODE_ENDPOINT = 'https://api.linode.com/v4'; function formatError(response) { diff --git a/src/dns/manual.js b/src/dns/manual.js index 20269fef6..7008c2d3c 100644 --- a/src/dns/manual.js +++ b/src/dns/manual.js @@ -1,6 +1,14 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/manual'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,14 +18,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:dns/manual'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - waitForDns = require('./waitfordns.js'); - function removePrivateFields(domainObject) { return domainObject; } diff --git a/src/dns/namecheap.js b/src/dns/namecheap.js index 212172417..0697de588 100644 --- a/src/dns/namecheap.js +++ b/src/dns/namecheap.js @@ -1,6 +1,20 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import * as network from '../network.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import timers from 'timers/promises'; +import util from 'node:util'; +import waitForDns from './waitfordns.js'; +import xml2js from 'xml2js'; -exports = module.exports = { +const debug = debugModule('box:dns/namecheap'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,20 +24,6 @@ exports = module.exports = { wait }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/namecheap'), - dig = require('../dig.js'), - dns = require('../dns.js'), - network = require('../network.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - timers = require('timers/promises'), - util = require('node:util'), - waitForDns = require('./waitfordns.js'), - xml2js = require('xml2js'); - const ENDPOINT = 'https://api.namecheap.com/xml.response'; function removePrivateFields(domainObject) { diff --git a/src/dns/namecom.js b/src/dns/namecom.js index 44b797348..366ea22ca 100644 --- a/src/dns/namecom.js +++ b/src/dns/namecom.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/namecom'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/namecom'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const NAMECOM_API = 'https://api.name.com/v4'; function formatError(response) { diff --git a/src/dns/netcup.js b/src/dns/netcup.js index 11bce2572..6b6c38aca 100644 --- a/src/dns/netcup.js +++ b/src/dns/netcup.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/netcup'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/netcup'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const API_ENDPOINT = 'https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON'; function formatError(response) { diff --git a/src/dns/noop.js b/src/dns/noop.js index dbc4f1d5b..5c13fda90 100644 --- a/src/dns/noop.js +++ b/src/dns/noop.js @@ -1,6 +1,9 @@ -'use strict'; +import assert from 'node:assert'; +import debugModule from 'debug'; -exports = module.exports = { +const debug = debugModule('box:dns/noop'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,9 +13,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - debug = require('debug')('box:dns/noop'); - function removePrivateFields(domainObject) { return domainObject; } diff --git a/src/dns/ovh.js b/src/dns/ovh.js index fb1b06754..ddf9a9126 100644 --- a/src/dns/ovh.js +++ b/src/dns/ovh.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import ovhClient from 'ovh'; +import safe from 'safetydance'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/ovh'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/ovh'), - dig = require('../dig.js'), - dns = require('../dns.js'), - ovhClient = require('ovh'), - safe = require('safetydance'), - waitForDns = require('./waitfordns.js'); - function formatError(error) { return `OVH DNS error ${error.error} ${error.message}`; // error.error is the status } diff --git a/src/dns/porkbun.js b/src/dns/porkbun.js index 53863292b..9550c089b 100644 --- a/src/dns/porkbun.js +++ b/src/dns/porkbun.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import timers from 'timers/promises'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/porkbun'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,17 +21,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/porkbun'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - timers = require('timers/promises'), - waitForDns = require('./waitfordns.js'); - // Rate limit note: Porkbun return 503 when it hits rate limits. It's as low as 1 req/second // https://github.com/cullenmcdermott/terraform-provider-porkbun/issues/23#issuecomment-1366859999 const PORKBUN_API = 'https://api.porkbun.com/api/json/v3/dns'; diff --git a/src/dns/route53.js b/src/dns/route53.js index 2b4876f86..ca3b5665b 100644 --- a/src/dns/route53.js +++ b/src/dns/route53.js @@ -1,6 +1,18 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import { ConfiguredRetryStrategy } from '@smithy/util-retry'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import { Route53 } from '@aws-sdk/client-route-53'; +import safe from 'safetydance'; +import waitForDns from './waitfordns.js'; +import * as _ from '../underscore.js'; -exports = module.exports = { +const debug = debugModule('box:dns/route53'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,18 +22,6 @@ exports = module.exports = { verifyDomainConfig, }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - { ConfiguredRetryStrategy } = require('@smithy/util-retry'), - constants = require('../constants.js'), - debug = require('debug')('box:dns/route53'), - dig = require('../dig.js'), - dns = require('../dns.js'), - { Route53 } = require('@aws-sdk/client-route-53'), - safe = require('safetydance'), - waitForDns = require('./waitfordns.js'), - _ = require('../underscore.js'); - function removePrivateFields(domainObject) { delete domainObject.config.secretAccessKey; return domainObject; diff --git a/src/dns/vultr.js b/src/dns/vultr.js index a5640209f..21b352665 100644 --- a/src/dns/vultr.js +++ b/src/dns/vultr.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import constants from '../constants.js'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/vultr'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,16 +20,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - constants = require('../constants.js'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:dns/vultr'), - dig = require('../dig.js'), - dns = require('../dns.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - waitForDns = require('./waitfordns.js'); - const VULTR_ENDPOINT = 'https://api.vultr.com/v2'; function formatError(response) { diff --git a/src/dns/waitfordns.js b/src/dns/waitfordns.js index dbead6c4f..1b964895f 100644 --- a/src/dns/waitfordns.js +++ b/src/dns/waitfordns.js @@ -1,15 +1,15 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import dns from 'node:dns'; +import promiseRetry from '../promise-retry.js'; +import safe from 'safetydance'; +import * as _ from '../underscore.js'; -exports = module.exports = waitForDns; +const debug = debugModule('box:dns/waitfordns'); -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:dns/waitfordns'), - dig = require('../dig.js'), - dns = require('node:dns'), - promiseRetry = require('../promise-retry.js'), - safe = require('safetydance'), - _ = require('../underscore.js'); +export default waitForDns; async function resolveIp(hostname, type, options) { assert.strictEqual(typeof hostname, 'string'); diff --git a/src/dns/wildcard.js b/src/dns/wildcard.js index dac38eac1..0653a5778 100644 --- a/src/dns/wildcard.js +++ b/src/dns/wildcard.js @@ -1,6 +1,15 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import * as dig from '../dig.js'; +import * as dns from '../dns.js'; +import * as network from '../network.js'; +import safe from 'safetydance'; +import waitForDns from './waitfordns.js'; -exports = module.exports = { +const debug = debugModule('box:dns/manual'); + +export { removePrivateFields, injectPrivateFields, upsert, @@ -10,15 +19,6 @@ exports = module.exports = { verifyDomainConfig }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:dns/manual'), - dig = require('../dig.js'), - dns = require('../dns.js'), - network = require('../network.js'), - safe = require('safetydance'), - waitForDns = require('./waitfordns.js'); - function removePrivateFields(domainObject) { return domainObject; } diff --git a/src/docker.js b/src/docker.js index e5240bd88..c1ecca9d9 100644 --- a/src/docker.js +++ b/src/docker.js @@ -1,6 +1,26 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import Docker from 'dockerode'; +import * as dockerRegistries from './dockerregistries.js'; +import fs from 'node:fs'; +import * as mailServer from './mailserver.js'; +import os from 'node:os'; +import paths from './paths.js'; +import promiseRetry from './promise-retry.js'; +import services from './services.js'; +import shellModule from './shell.js'; +import safe from 'safetydance'; +import timers from 'timers/promises'; +import * as volumes from './volumes.js'; -exports = module.exports = { +const debug = debugModule('box:docker'); +const shell = shellModule('docker'); + +export { ping, info, @@ -31,25 +51,6 @@ exports = module.exports = { resizeExec }; -const apps = require('./apps.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:docker'), - Docker = require('dockerode'), - dockerRegistries = require('./dockerregistries.js'), - fs = require('node:fs'), - mailServer = require('./mailserver.js'), - os = require('node:os'), - paths = require('./paths.js'), - promiseRetry = require('./promise-retry.js'), - services = require('./services.js'), - shell = require('./shell.js')('docker'), - safe = require('safetydance'), - timers = require('timers/promises'), - volumes = require('./volumes.js'); - const gConnection = new Docker({ socketPath: paths.DOCKER_SOCKET_PATH }); const CLOUDRON_REGISTRIES = [ 'registry.docker.com', 'registry.ipv4.docker.com', 'quay.io' ]; // order determines priority and is important! diff --git a/src/dockerproxy.js b/src/dockerproxy.js index 4c8c248e7..4d7361caa 100644 --- a/src/dockerproxy.js +++ b/src/dockerproxy.js @@ -1,25 +1,25 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import constants from './constants.js'; +import express from 'express'; +import debugModule from 'debug'; +import http from 'node:http'; +import { HttpError } from '@cloudron/connect-lastmile'; +import * as middleware from './middleware/index.js'; +import net from 'node:net'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import util from 'node:util'; +import * as volumes from './volumes.js'; -exports = module.exports = { +const debug = debugModule('box:dockerproxy'); + +export { start, stop }; -const apps = require('./apps.js'), - assert = require('node:assert'), - constants = require('./constants.js'), - express = require('express'), - debug = require('debug')('box:dockerproxy'), - http = require('node:http'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - middleware = require('./middleware'), - net = require('node:net'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - util = require('node:util'), - volumes = require('./volumes.js'); - let gHttpServer = null; async function authorizeApp(req, res, next) { diff --git a/src/dockerregistries.js b/src/dockerregistries.js index c451d2ee3..71f5e8443 100644 --- a/src/dockerregistries.js +++ b/src/dockerregistries.js @@ -1,6 +1,15 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import Docker from 'dockerode'; +import eventlog from './eventlog.js'; +import paths from './paths.js'; +import safe from 'safetydance'; +import tld from 'tldjs'; -exports = module.exports = { +export { removePrivateFields, list, @@ -10,17 +19,6 @@ exports = module.exports = { update, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - crypto = require('node:crypto'), - database = require('./database.js'), - Docker = require('dockerode'), - eventlog = require('./eventlog.js'), - paths = require('./paths.js'), - safe = require('safetydance'), - tld = require('tldjs'); - const REGISTRY_FIELDS = [ 'id', 'provider', 'serverAddress', 'username', 'email', 'password' ].join(','); function removePrivateFields(registryConfig) { diff --git a/src/domains.js b/src/domains.js index d8aa7e640..2508dfd66 100644 --- a/src/domains.js +++ b/src/domains.js @@ -1,6 +1,44 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import crypto from 'node:crypto'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import * as mailServer from './mailserver.js'; +import * as notifications from './notifications.js'; +import * as openssl from './openssl.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import tld from 'tldjs'; +import * as _ from './underscore.js'; +import * as dnsBunny from './dns/bunny.js'; +import * as dnsCloudflare from './dns/cloudflare.js'; +import * as dnsDesec from './dns/desec.js'; +import * as dnsDnsimple from './dns/dnsimple.js'; +import * as dnsRoute53 from './dns/route53.js'; +import * as dnsGcdns from './dns/gcdns.js'; +import * as dnsDigitalocean from './dns/digitalocean.js'; +import * as dnsGandi from './dns/gandi.js'; +import * as dnsGodaddy from './dns/godaddy.js'; +import * as dnsHetzner from './dns/hetzner.js'; +import * as dnsHetznercloud from './dns/hetznercloud.js'; +import * as dnsInwx from './dns/inwx.js'; +import * as dnsLinode from './dns/linode.js'; +import * as dnsVultr from './dns/vultr.js'; +import * as dnsNamecom from './dns/namecom.js'; +import * as dnsNamecheap from './dns/namecheap.js'; +import * as dnsNetcup from './dns/netcup.js'; +import * as dnsNoop from './dns/noop.js'; +import * as dnsOvh from './dns/ovh.js'; +import * as dnsManual from './dns/manual.js'; +import * as dnsPorkbun from './dns/porkbun.js'; +import * as dnsWildcard from './dns/wildcard.js'; -exports = module.exports = { +const debug = debugModule('box:domains'); + +export { add, get, list, @@ -17,22 +55,6 @@ exports = module.exports = { checkConfigs }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - crypto = require('node:crypto'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - debug = require('debug')('box:domains'), - eventlog = require('./eventlog.js'), - mailServer = require('./mailserver.js'), - notifications = require('./notifications.js'), - openssl = require('./openssl.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - tld = require('tldjs'), - _ = require('./underscore.js'); - const DOMAINS_FIELDS = [ 'domain', 'zoneName', 'provider', 'configJson', 'tlsConfigJson', 'wellKnownJson', 'fallbackCertificateJson' ].join(','); function postProcess(data) { @@ -51,35 +73,20 @@ function postProcess(data) { return data; } +const DNS_PROVIDERS = { + bunny: dnsBunny, cloudflare: dnsCloudflare, desec: dnsDesec, dnsimple: dnsDnsimple, + route53: dnsRoute53, gcdns: dnsGcdns, digitalocean: dnsDigitalocean, gandi: dnsGandi, + godaddy: dnsGodaddy, hetzner: dnsHetzner, hetznercloud: dnsHetznercloud, inwx: dnsInwx, + linode: dnsLinode, vultr: dnsVultr, namecom: dnsNamecom, namecheap: dnsNamecheap, + netcup: dnsNetcup, noop: dnsNoop, ovh: dnsOvh, manual: dnsManual, + porkbun: dnsPorkbun, wildcard: dnsWildcard +}; + // choose which subdomain backend we use for test purpose we use route53 function api(provider) { assert.strictEqual(typeof provider, 'string'); - switch (provider) { - case 'bunny': return require('./dns/bunny.js'); - case 'cloudflare': return require('./dns/cloudflare.js'); - case 'desec': return require('./dns/desec.js'); - case 'dnsimple': return require('./dns/dnsimple.js'); - case 'route53': return require('./dns/route53.js'); - case 'gcdns': return require('./dns/gcdns.js'); - case 'digitalocean': return require('./dns/digitalocean.js'); - case 'gandi': return require('./dns/gandi.js'); - case 'godaddy': return require('./dns/godaddy.js'); - case 'hetzner': return require('./dns/hetzner.js'); - case 'hetznercloud': return require('./dns/hetznercloud.js'); - case 'inwx': return require('./dns/inwx.js'); - case 'linode': return require('./dns/linode.js'); - case 'vultr': return require('./dns/vultr.js'); - case 'namecom': return require('./dns/namecom.js'); - case 'namecheap': return require('./dns/namecheap.js'); - case 'netcup': return require('./dns/netcup.js'); - case 'noop': return require('./dns/noop.js'); - case 'ovh': return require('./dns/ovh.js'); - case 'manual': return require('./dns/manual.js'); - case 'porkbun': return require('./dns/porkbun.js'); - case 'wildcard': return require('./dns/wildcard.js'); - default: return null; - } + return DNS_PROVIDERS[provider] || null; } async function verifyDomainConfig(domainConfig, domain, zoneName, provider) { diff --git a/src/dyndns.js b/src/dyndns.js index d822e576f..4a2c8f2a5 100644 --- a/src/dyndns.js +++ b/src/dyndns.js @@ -1,23 +1,23 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import * as mailServer from './mailserver.js'; +import * as network from './network.js'; +import paths from './paths.js'; +import safe from 'safetydance'; +import tasks from './tasks.js'; -exports = module.exports = { +const debug = debugModule('box:dyndns'); + +export { refreshDns, sync }; -const apps = require('./apps.js'), - assert = require('node:assert'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:dyndns'), - dns = require('./dns.js'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - mailServer = require('./mailserver.js'), - network = require('./network.js'), - paths = require('./paths.js'), - safe = require('safetydance'), - tasks = require('./tasks.js'); - // FIXME: this races with apptask. can result in a conflict if apptask is doing some dns operation and this code changes entries async function refreshDns(auditSource) { assert.strictEqual(typeof auditSource, 'object'); diff --git a/src/eventlog.js b/src/eventlog.js index 9f845edcc..9b21436be 100644 --- a/src/eventlog.js +++ b/src/eventlog.js @@ -1,6 +1,19 @@ -'use strict'; +import assert from 'node:assert'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import debugModule from 'debug'; +import mysql from 'mysql2'; +import * as notifications from './notifications.js'; +import safe from 'safetydance'; -exports = module.exports = { +const debug = debugModule('box:eventlog'); + +const ACTION_ACTIVATE = 'cloudron.activate'; +const ACTION_USER_LOGIN = 'user.login'; +const ACTION_USER_LOGIN_GHOST = 'user.login.ghost'; +const ACTION_USER_LOGOUT = 'user.logout'; + +export default { add, upsertLoginEvent, get, @@ -10,7 +23,7 @@ exports = module.exports = { _clear: clear, // keep in sync with webadmin index.js filter - ACTION_ACTIVATE: 'cloudron.activate', + ACTION_ACTIVATE, ACTION_APP_CLONE: 'app.clone', ACTION_APP_CONFIGURE: 'app.configure', ACTION_APP_REPAIR: 'app.repair', @@ -96,9 +109,9 @@ exports = module.exports = { ACTION_UPDATE_FINISH: 'cloudron.update.finish', ACTION_USER_ADD: 'user.add', - ACTION_USER_LOGIN: 'user.login', - ACTION_USER_LOGIN_GHOST: 'user.login.ghost', - ACTION_USER_LOGOUT: 'user.logout', + ACTION_USER_LOGIN, + ACTION_USER_LOGIN_GHOST, + ACTION_USER_LOGOUT, ACTION_USER_REMOVE: 'user.remove', ACTION_USER_UPDATE: 'user.update', ACTION_USER_TRANSFER: 'user.transfer', @@ -118,14 +131,6 @@ exports = module.exports = { ACTION_PROCESS_CRASH: 'system.crash' // obsolete }; -const assert = require('node:assert'), - crypto = require('node:crypto'), - database = require('./database.js'), - debug = require('debug')('box:eventlog'), - mysql = require('mysql2'), - notifications = require('./notifications.js'), - safe = require('safetydance'); - const EVENTLOG_FIELDS = [ 'id', 'action', 'sourceJson', 'dataJson', 'creationTime' ].join(','); function postProcess(record) { @@ -182,7 +187,7 @@ async function get(id) { } async function getActivationEvent() { - const result = await database.query(`SELECT ${EVENTLOG_FIELDS} FROM eventlog WHERE action = ? ORDER BY creationTime`, [ exports.ACTION_ACTIVATE ]); + const result = await database.query(`SELECT ${EVENTLOG_FIELDS} FROM eventlog WHERE action = ? ORDER BY creationTime`, [ ACTION_ACTIVATE ]); if (result.length === 0) return null; return postProcess(result[0]); @@ -225,9 +230,9 @@ async function cleanup(options) { // only these actions are pruned const actions = [ - exports.ACTION_USER_LOGIN, - exports.ACTION_USER_LOGIN_GHOST, - exports.ACTION_USER_LOGOUT, + ACTION_USER_LOGIN, + ACTION_USER_LOGIN_GHOST, + ACTION_USER_LOGOUT, ]; let query = `SELECT ${EVENTLOG_FIELDS} FROM eventlog WHERE creationTime <= ? AND (`; diff --git a/src/externalldap.js b/src/externalldap.js index 8bf3fe86a..5ebbc2fca 100644 --- a/src/externalldap.js +++ b/src/externalldap.js @@ -1,6 +1,21 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as cron from './cron.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import * as groups from './groups.js'; +import ldap from 'ldapjs'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import tasks from './tasks.js'; +import * as users from './users.js'; +import util from 'node:util'; -exports = module.exports = { +const debug = debugModule('box:externalldap'); + +export { getConfig, setConfig, @@ -16,28 +31,12 @@ exports = module.exports = { sync }; -const assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - cron = require('./cron.js'), - debug = require('debug')('box:externalldap'), - eventlog = require('./eventlog.js'), - groups = require('./groups.js'), - ldap = require('ldapjs'), - safe = require('safetydance'), - settings = require('./settings.js'), - tasks = require('./tasks.js'), - users = require('./users.js'), - util = require('node:util'); - function removePrivateFields(ldapConfig) { assert.strictEqual(typeof ldapConfig, 'object'); delete ldapConfig.bindPassword; return ldapConfig; } - function injectPrivateFields(newConfig, currentConfig) { if (!Object.hasOwn(newConfig, 'bindPassword')) newConfig.bindPassword = currentConfig.bindPassword; } diff --git a/src/groups.js b/src/groups.js index f0564fc1d..3ff359fde 100644 --- a/src/groups.js +++ b/src/groups.js @@ -1,40 +1,33 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import eventlog from './eventlog.js'; +import safe from 'safetydance'; -exports = module.exports = { +const _getMembership = getMembership; + +export { add, del, get, getByName, - update, setName, - getWithMembers, list, listWithMembers, - getMemberIds, setMembers, isMember, - setLocalMembership, resetSources, - setAllowedApps, - - // exported for testing - _getMembership: getMembership + _getMembership, }; -const apps = require('./apps.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - crypto = require('node:crypto'), - database = require('./database.js'), - eventlog = require('./eventlog.js'), - safe = require('safetydance'); - const GROUPS_FIELDS = [ 'id', 'name', 'source' ].join(','); // keep this in sync with validateUsername diff --git a/src/hash-stream.js b/src/hash-stream.js index 46f5b6e90..254c91c5c 100644 --- a/src/hash-stream.js +++ b/src/hash-stream.js @@ -1,8 +1,7 @@ -'use strict'; +import crypto from 'node:crypto'; +import stream from 'node:stream'; -const crypto = require('node:crypto'), - stream = require('node:stream'), - TransformStream = stream.Transform; +const TransformStream = stream.Transform; class HashStream extends TransformStream { #hasher; @@ -36,4 +35,4 @@ class HashStream extends TransformStream { } } -exports = module.exports = HashStream; \ No newline at end of file +export default HashStream; diff --git a/src/hat.js b/src/hat.js index 7c4aa18dd..e4b03cca3 100644 --- a/src/hat.js +++ b/src/hat.js @@ -1,8 +1,6 @@ -'use strict'; +import crypto from 'node:crypto'; -exports = module.exports = hat; - -const crypto = require('node:crypto'); +export default hat; function hat(bits) { return crypto.randomBytes(bits / 8).toString('hex'); diff --git a/src/hush.js b/src/hush.js index 58cc15945..ff4990d74 100644 --- a/src/hush.js +++ b/src/hush.js @@ -1,10 +1,10 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import debugModule from 'debug'; +import { Transform as TransformStream } from 'node:stream'; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - debug = require('debug')('box:hush'), - TransformStream = require('node:stream').Transform; +const debug = debugModule('box:hush'); class EncryptStream extends TransformStream { constructor(encryption) { @@ -165,7 +165,7 @@ function generateEncryptionKeysSync(password) { }; } -exports = module.exports = { +export { EncryptStream, DecryptStream, diff --git a/src/infra_version.js b/src/infra_version.js index 8e14e0f8d..c63e2a545 100644 --- a/src/infra_version.js +++ b/src/infra_version.js @@ -1,10 +1,8 @@ -'use strict'; - // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING // These constants are used in the installer script as well // Do not require anything here! -exports = module.exports = { +export default { // a version change recreates all containers with latest docker config 'version': '49.9.0', diff --git a/src/ipaddr.js b/src/ipaddr.js index 1e3370484..402d94744 100644 --- a/src/ipaddr.js +++ b/src/ipaddr.js @@ -1,15 +1,13 @@ -'use strict'; +import assert from 'node:assert'; +import net from 'node:net'; -exports = module.exports = { +export { isValid, isValidCIDR, isEqual, includes, }; -const assert = require('node:assert'), - net = require('node:net'); - function isValid(ip) { assert.strictEqual(typeof ip, 'string'); diff --git a/src/iputils.js b/src/iputils.js index f1cf9fcd9..d3be348ec 100644 --- a/src/iputils.js +++ b/src/iputils.js @@ -1,12 +1,10 @@ -'use strict'; +import assert from 'node:assert'; -exports = module.exports = { +export { ipFromInt, intFromIp }; -const assert = require('node:assert'); - // this code is used in migrations - 20201120212726-apps-add-containerIp.js function intFromIp(address) { assert.strictEqual(typeof address, 'string'); @@ -25,7 +23,7 @@ function intFromIp(address) { function ipFromInt(input) { assert.strictEqual(typeof input, 'number'); - let output = []; + const output = []; for (let i = 3; i >= 0; --i) { const octet = (input >> (i*8)) & 0x000000FF; diff --git a/src/janitor.js b/src/janitor.js index 47d2fe259..da2d8789c 100644 --- a/src/janitor.js +++ b/src/janitor.js @@ -1,17 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import debugModule from 'debug'; +import Docker from 'dockerode'; +import safe from 'safetydance'; +import * as tokens from './tokens.js'; -exports = module.exports = { +const debug = debugModule('box:janitor'); + +export { cleanupTokens, cleanupDockerVolumes }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - debug = require('debug')('box:janitor'), - Docker = require('dockerode'), - safe = require('safetydance'), - tokens = require('./tokens.js'); - const gConnection = new Docker({ socketPath: '/var/run/docker.sock' }); async function cleanupTokens() { diff --git a/src/ldapserver.js b/src/ldapserver.js index 3406d1650..0c30c63d0 100644 --- a/src/ldapserver.js +++ b/src/ldapserver.js @@ -1,27 +1,33 @@ -'use strict'; +import * as addonConfigs from './addonconfigs.js'; +import assert from 'node:assert'; +import apps from './apps.js'; +import AuditSource from './auditsource.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import * as groups from './groups.js'; +import ldap from 'ldapjs'; +import * as mail from './mail.js'; +import safe from 'safetydance'; +import * as users from './users.js'; +import util from 'node:util'; -exports = module.exports = { +const debug = debugModule('box:ldapserver'); + +let _MOCK_APP = null; + +function _setMockApp(app) { + _MOCK_APP = app; +} + +export { start, stop, - - _MOCK_APP: null + _MOCK_APP, + _setMockApp, }; -const addonConfigs = require('./addonconfigs.js'), - assert = require('node:assert'), - apps = require('./apps.js'), - AuditSource = require('./auditsource.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:ldapserver'), - eventlog = require('./eventlog.js'), - groups = require('./groups.js'), - ldap = require('ldapjs'), - mail = require('./mail.js'), - safe = require('safetydance'), - users = require('./users.js'), - util = require('node:util'); - let gServer = null; const NOOP = function () {}; @@ -33,7 +39,7 @@ async function authenticateApp(req, res, next) { // this is only used by the ldap test. the apps tests still uses proper docker if (constants.TEST && sourceIp === '127.0.0.1') { - req.app = exports._MOCK_APP; + req.app = _MOCK_APP; return next(); } diff --git a/src/location.js b/src/location.js index 104e80cd1..70a88aae8 100644 --- a/src/location.js +++ b/src/location.js @@ -1,6 +1,4 @@ -'use strict'; - -const assert = require('node:assert'); +import assert from 'node:assert'; class Location { constructor(subdomain, domain, type, certificate) { @@ -25,4 +23,4 @@ Location.TYPE_DASHBOARD = 'dashboard'; Location.TYPE_MAIL = 'mail'; Location.TYPE_DIRECTORY_SERVER = 'directoryserver'; -exports = module.exports = Location; +export default Location; diff --git a/src/locks.js b/src/locks.js index 5ab503fc9..7acfe434a 100644 --- a/src/locks.js +++ b/src/locks.js @@ -1,6 +1,18 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import promiseRetry from './promise-retry.js'; -exports = module.exports = { +const debug = debugModule('box:locks'); + +const TYPE_APP_TASK_PREFIX = 'app_task_'; +const TYPE_APP_BACKUP_PREFIX = 'app_backup_'; +const TYPE_BOX_UPDATE = 'box_update'; +const TYPE_BOX_UPDATE_TASK = 'box_update_task'; +const TYPE_FULL_BACKUP_TASK_PREFIX = 'full_backup_task_'; + +export default { setTaskId, acquire, @@ -10,21 +22,15 @@ exports = module.exports = { releaseAll, releaseByTaskId, - TYPE_APP_TASK_PREFIX: 'app_task_', - TYPE_APP_BACKUP_PREFIX: 'app_backup_', - TYPE_BOX_UPDATE: 'box_update', // for the actual update and after the backup. this allows the backup before update do not block - TYPE_BOX_UPDATE_TASK: 'box_update_task', // for scheduling the update task - TYPE_FULL_BACKUP_TASK_PREFIX: 'full_backup_task_', // for scheduling the backup task + TYPE_APP_TASK_PREFIX, + TYPE_APP_BACKUP_PREFIX, + TYPE_BOX_UPDATE, + TYPE_BOX_UPDATE_TASK, + TYPE_FULL_BACKUP_TASK_PREFIX, TYPE_MAIL_SERVER_RESTART: 'mail_restart', }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - database = require('./database.js'), - debug = require('debug')('box:locks'), - promiseRetry = require('./promise-retry.js'); - let gTaskId = null; function setTaskId(taskId) { @@ -52,15 +58,15 @@ function canAcquire(data, type) { if (type in data) return new BoxError(BoxError.BAD_STATE, `Locked by ${data[type]}`); - if (type === exports.TYPE_BOX_UPDATE) { - if (Object.keys(data).some(k => k.startsWith(exports.TYPE_APP_TASK_PREFIX))) return new BoxError(BoxError.BAD_STATE, 'One or more app tasks are active'); - if (Object.keys(data).some(k => k.startsWith(exports.TYPE_APP_BACKUP_PREFIX))) return new BoxError(BoxError.BAD_STATE, 'One or more app backups are active'); - } else if (type.startsWith(exports.TYPE_APP_TASK_PREFIX)) { - if (exports.TYPE_BOX_UPDATE in data) return new BoxError(BoxError.BAD_STATE, 'Update is active'); - } else if (type.startsWith(exports.TYPE_FULL_BACKUP_TASK_PREFIX)) { - if (exports.TYPE_BOX_UPDATE_TASK in data) return new BoxError(BoxError.BAD_STATE, 'Update task is active'); - } else if (type === exports.TYPE_BOX_UPDATE_TASK) { - if (Object.keys(data).some(k => k.startsWith(exports.TYPE_FULL_BACKUP_TASK_PREFIX))) return new BoxError(BoxError.BAD_STATE, 'One or more backup tasks is active'); + if (type === TYPE_BOX_UPDATE) { + if (Object.keys(data).some(k => k.startsWith(TYPE_APP_TASK_PREFIX))) return new BoxError(BoxError.BAD_STATE, 'One or more app tasks are active'); + if (Object.keys(data).some(k => k.startsWith(TYPE_APP_BACKUP_PREFIX))) return new BoxError(BoxError.BAD_STATE, 'One or more app backups are active'); + } else if (type.startsWith(TYPE_APP_TASK_PREFIX)) { + if (TYPE_BOX_UPDATE in data) return new BoxError(BoxError.BAD_STATE, 'Update is active'); + } else if (type.startsWith(TYPE_FULL_BACKUP_TASK_PREFIX)) { + if (TYPE_BOX_UPDATE_TASK in data) return new BoxError(BoxError.BAD_STATE, 'Update task is active'); + } else if (type === TYPE_BOX_UPDATE_TASK) { + if (Object.keys(data).some(k => k.startsWith(TYPE_FULL_BACKUP_TASK_PREFIX))) return new BoxError(BoxError.BAD_STATE, 'One or more backup tasks is active'); } // TYPE_APP_BACKUP_PREFIX , TYPE_MAIL_SERVER_RESTART can co-run with everything except themselves diff --git a/src/logs.js b/src/logs.js index 3e8f2744c..793c2b50b 100644 --- a/src/logs.js +++ b/src/logs.js @@ -1,15 +1,15 @@ -'use strict'; +import assert from 'node:assert'; +import child_process from 'node:child_process'; +import debugModule from 'debug'; +import path from 'node:path'; +import stream from 'node:stream'; +import { StringDecoder } from 'node:string_decoder'; -const assert = require('node:assert'), - child_process = require('node:child_process'), - debug = require('debug')('box:logs'), - path = require('node:path'), - stream = require('node:stream'), - { StringDecoder } = require('node:string_decoder'), - TransformStream = stream.Transform; +const debug = debugModule('box:logs'); +const TransformStream = stream.Transform; -const LOGTAIL_CMD = path.join(__dirname, 'scripts/logtail.sh'); -const KILL_CHILD_CMD = path.join(__dirname, 'scripts/kill-child.sh'); +const LOGTAIL_CMD = path.join(import.meta.dirname, 'scripts/logtail.sh'); +const KILL_CHILD_CMD = path.join(import.meta.dirname, 'scripts/kill-child.sh'); class LogStream extends TransformStream { constructor(options) { @@ -99,7 +99,7 @@ function journalctl(unit, options) { return cp; } -exports = module.exports = { +export { tail, journalctl, LogStream diff --git a/src/mail.js b/src/mail.js index b81ccb7fb..d660a1cb2 100644 --- a/src/mail.js +++ b/src/mail.js @@ -1,90 +1,85 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import * as dig from './dig.js'; +import * as dns from './dns.js'; +import eventlog from './eventlog.js'; +import * as mailer from './mailer.js'; +import * as mailServer from './mailserver.js'; +import net from 'node:net'; +import * as network from './network.js'; +import nodemailer from 'nodemailer'; +import * as notifications from './notifications.js'; +import path from 'node:path'; +import * as platform from './platform.js'; +import safe from 'safetydance'; +import services from './services.js'; +import shellModule from './shell.js'; +import superagent from '@cloudron/superagent'; +import * as validator from './validator.js'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:mail'); +const shell = shellModule('mail'); + +const OWNERTYPE_USER = 'user'; +const OWNERTYPE_GROUP = 'group'; +const OWNERTYPE_APP = 'app'; +const TYPE_MAILBOX = 'mailbox'; +const TYPE_LIST = 'list'; +const TYPE_ALIAS = 'alias'; +const _delByDomain = delByDomain; +const _updateDomain = updateDomain; + +export { getStatus, checkConfiguration, - listDomains, - getDomain, clearDomains, - removePrivateFields, - setDnsRecords, upsertDnsRecords, - validateName, validateDisplayName, - setMailFromValidation, setCatchAllAddress, setMailRelay, setMailEnabled, setBanner, - sendTestMail, - listMailboxesByDomain, listMailboxes, getMailbox, addMailbox, updateMailbox, delMailbox, - getAlias, getAliases, setAliases, searchAlias, - listMailingListsByDomain, getMailingList, addMailingList, updateMailingList, delMailingList, resolveMailingList, - getStats, - checkStatus, - - OWNERTYPE_USER: 'user', - OWNERTYPE_GROUP: 'group', - OWNERTYPE_APP: 'app', - - TYPE_MAILBOX: 'mailbox', - TYPE_LIST: 'list', - TYPE_ALIAS: 'alias', - - _delByDomain: delByDomain, - _updateDomain: updateDomain + OWNERTYPE_USER, + OWNERTYPE_GROUP, + OWNERTYPE_APP, + TYPE_MAILBOX, + TYPE_LIST, + TYPE_ALIAS, + _delByDomain, + _updateDomain, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - database = require('./database.js'), - debug = require('debug')('box:mail'), - dig = require('./dig.js'), - dns = require('./dns.js'), - eventlog = require('./eventlog.js'), - mailer = require('./mailer.js'), - mailServer = require('./mailserver.js'), - net = require('node:net'), - network = require('./network.js'), - nodemailer = require('nodemailer'), - notifications = require('./notifications.js'), - path = require('node:path'), - platform = require('./platform.js'), - safe = require('safetydance'), - services = require('./services.js'), - shell = require('./shell.js')('mail'), - superagent = require('@cloudron/superagent'), - validator = require('./validator.js'), - _ = require('./underscore.js'); - const DNS_OPTIONS = { timeout: 20000, tries: 4 }; -const REMOVE_MAILBOX_CMD = path.join(__dirname, 'scripts/rmmailbox.sh'); +const REMOVE_MAILBOX_CMD = path.join(import.meta.dirname, 'scripts/rmmailbox.sh'); // if you add a field here, listMailboxes* has to be updated const MAILBOX_FIELDS = [ 'name', 'type', 'ownerId', 'ownerType', 'aliasName', 'aliasDomain', 'creationTime', 'membersJson', 'membersOnly', 'domain', 'active', 'enablePop3', 'storageQuota', 'messagesQuota' ].join(','); @@ -169,7 +164,7 @@ function validateDisplayName(name) { } function validateOwnerType(type) { - const OWNERTYPES = [ exports.OWNERTYPE_USER, exports.OWNERTYPE_GROUP, exports.OWNERTYPE_APP ]; + const OWNERTYPES = [ OWNERTYPE_USER, OWNERTYPE_GROUP, OWNERTYPE_APP ]; return OWNERTYPES.includes(type); } @@ -845,8 +840,8 @@ async function listMailboxesByDomain(domain, page, perPage) { // const searchQuery = search ? ` HAVING (name LIKE ${escapedSearch} OR aliasNames LIKE ${escapedSearch} OR aliasDomains LIKE ${escapedSearch})` : ''; // having instead of where because of aggregated columns use const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, m1.active as active, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains, m1.enablePop3 AS enablePop3, m1.storageQuota AS storageQuota, m1.messagesQuota AS messagesQuota ' - + ` FROM (SELECT * FROM mailboxes WHERE type='${exports.TYPE_MAILBOX}') AS m1` - + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${exports.TYPE_ALIAS}') AS m2` + + ` FROM (SELECT * FROM mailboxes WHERE type='${TYPE_MAILBOX}') AS m1` + + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${TYPE_ALIAS}') AS m2` + ' ON m1.name=m2.aliasName AND m1.domain=m2.aliasDomain AND m1.ownerId=m2.ownerId' + ' WHERE m1.domain = ?' + ' GROUP BY m1.name, m1.domain, m1.ownerId' @@ -866,8 +861,8 @@ async function listMailboxes(page, perPage) { assert.strictEqual(typeof perPage, 'number'); const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, m1.active as active, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains, m1.enablePop3 AS enablePop3, m1.storageQuota AS storageQuota, m1.messagesQuota AS messagesQuota ' - + ` FROM (SELECT * FROM mailboxes WHERE type='${exports.TYPE_MAILBOX}') AS m1` - + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${exports.TYPE_ALIAS}') AS m2` + + ` FROM (SELECT * FROM mailboxes WHERE type='${TYPE_MAILBOX}') AS m1` + + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${TYPE_ALIAS}') AS m2` + ' ON m1.name=m2.aliasName AND m1.domain=m2.aliasDomain AND m1.ownerId=m2.ownerId' + ' GROUP BY m1.name, m1.domain, m1.ownerId' + ' ORDER BY name LIMIT ?,?'; @@ -914,7 +909,7 @@ async function getMailbox(name, domain) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE name = ? AND type = ? AND domain = ?`, [ name, exports.TYPE_MAILBOX, domain ]); + const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE name = ? AND type = ? AND domain = ?`, [ name, TYPE_MAILBOX, domain ]); if (results.length === 0) return null; return postProcessMailbox(results[0]); } @@ -941,7 +936,7 @@ async function addMailbox(name, domain, data, auditSource) { if (!validateOwnerType(ownerType)) throw new BoxError(BoxError.BAD_FIELD, 'bad owner type'); [error] = await safe(database.query('INSERT INTO mailboxes (name, type, domain, ownerId, ownerType, active, storageQuota, messagesQuota, enablePop3) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', - [ name, exports.TYPE_MAILBOX, domain, ownerId, ownerType, active, storageQuota, messagesQuota, enablePop3 ])); + [ name, TYPE_MAILBOX, domain, ownerId, ownerType, active, storageQuota, messagesQuota, enablePop3 ])); if (error && error.sqlCode === 'ER_DUP_ENTRY') throw new BoxError(BoxError.ALREADY_EXISTS, 'mailbox already exists'); if (error && error.sqlCode === 'ER_NO_REFERENCED_ROW_2' && error.sqlMessage.includes('mailboxes_domain_constraint')) throw new BoxError(BoxError.NOT_FOUND, `no such domain '${domain}'`); if (error) throw error; @@ -1022,7 +1017,7 @@ async function getAlias(name, domain) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE name = ? AND type = ? AND domain = ?`, [ name, exports.TYPE_ALIAS, domain ]); + const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE name = ? AND type = ? AND domain = ?`, [ name, TYPE_ALIAS, domain ]); if (results.length === 0) return null; results.forEach(function (result) { postProcessMailbox(result); }); @@ -1034,7 +1029,7 @@ async function searchAlias(name, domain) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE ? LIKE REPLACE(REPLACE(name, '*', '%'), '_', '\\_') AND type = ? AND domain = ?`, [ name, exports.TYPE_ALIAS, domain ]); + const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE ? LIKE REPLACE(REPLACE(name, '*', '%'), '_', '\\_') AND type = ? AND domain = ?`, [ name, TYPE_ALIAS, domain ]); if (results.length === 0) return null; results.forEach(function (result) { postProcessMailbox(result); }); @@ -1048,7 +1043,7 @@ async function getAliases(name, domain) { const result = await getMailbox(name, domain); // check if mailbox exists if (result === null) throw new BoxError(BoxError.NOT_FOUND, 'No such mailbox'); - return await database.query('SELECT name, domain FROM mailboxes WHERE type = ? AND aliasName = ? AND aliasDomain = ? ORDER BY name', [ exports.TYPE_ALIAS, name, domain ]); + return await database.query('SELECT name, domain FROM mailboxes WHERE type = ? AND aliasName = ? AND aliasDomain = ? ORDER BY name', [ TYPE_ALIAS, name, domain ]); } async function setAliases(name, domain, aliases, auditSource) { @@ -1075,10 +1070,10 @@ async function setAliases(name, domain, aliases, auditSource) { const queries = []; // clear existing aliases - queries.push({ query: 'DELETE FROM mailboxes WHERE aliasName = ? AND aliasDomain = ? AND type = ?', args: [ name, domain, exports.TYPE_ALIAS ] }); + queries.push({ query: 'DELETE FROM mailboxes WHERE aliasName = ? AND aliasDomain = ? AND type = ?', args: [ name, domain, TYPE_ALIAS ] }); for (const alias of aliases) { queries.push({ query: 'INSERT INTO mailboxes (name, domain, type, aliasName, aliasDomain, ownerId, ownerType) VALUES (?, ?, ?, ?, ?, ?, ?)', - args: [ alias.name, alias.domain, exports.TYPE_ALIAS, name, domain, results[0].ownerId, results[0].ownerType ] }); + args: [ alias.name, alias.domain, TYPE_ALIAS, name, domain, results[0].ownerId, results[0].ownerType ] }); } const [error] = await safe(database.transaction(queries)); @@ -1103,7 +1098,7 @@ async function listMailingListsByDomain(domain, page, perPage) { query += 'ORDER BY name LIMIT ?,?'; - const results = await database.query(query, [ exports.TYPE_LIST, domain, (page-1)*perPage, perPage ]); + const results = await database.query(query, [ TYPE_LIST, domain, (page-1)*perPage, perPage ]); results.forEach(function (result) { postProcessMailbox(result); }); @@ -1114,7 +1109,7 @@ async function getMailingList(name, domain) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - const results = await database.query('SELECT ' + MAILBOX_FIELDS + ' FROM mailboxes WHERE type = ? AND name = ? AND domain = ?', [ exports.TYPE_LIST, name, domain ]); + const results = await database.query('SELECT ' + MAILBOX_FIELDS + ' FROM mailboxes WHERE type = ? AND name = ? AND domain = ?', [ TYPE_LIST, name, domain ]); if (results.length === 0) return null; return postProcessMailbox(results[0]); @@ -1140,7 +1135,7 @@ async function addMailingList(name, domain, data, auditSource) { if (!validator.isEmail(members[i])) throw new BoxError(BoxError.BAD_FIELD, 'Invalid mail member: ' + members[i]); } - [error] = await safe(database.query('INSERT INTO mailboxes (name, type, domain, ownerId, ownerType, membersJson, membersOnly, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [ name, exports.TYPE_LIST, domain, 'admin', 'user', JSON.stringify(members), membersOnly, active ])); + [error] = await safe(database.query('INSERT INTO mailboxes (name, type, domain, ownerId, ownerType, membersJson, membersOnly, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [ name, TYPE_LIST, domain, 'admin', 'user', JSON.stringify(members), membersOnly, active ])); if (error && error.sqlCode === 'ER_DUP_ENTRY') throw new BoxError(BoxError.ALREADY_EXISTS, 'mailbox already exists'); if (error) throw error; @@ -1223,9 +1218,9 @@ async function resolveMailingList(listName, listDomain) { continue; } - if (entry.type === exports.TYPE_MAILBOX) { // concrete mailbox + if (entry.type === TYPE_MAILBOX) { // concrete mailbox resolvedMembers.push(member); - } else if (entry.type === exports.TYPE_ALIAS) { // resolve aliases + } else if (entry.type === TYPE_ALIAS) { // resolve aliases toResolve = toResolve.concat(`${entry.aliasName}@${entry.aliasDomain}`); } else { // resolve list members toResolve = toResolve.concat(entry.members); diff --git a/src/mailer.js b/src/mailer.js index 90de0ef76..976558da7 100644 --- a/src/mailer.js +++ b/src/mailer.js @@ -1,11 +1,28 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as branding from './branding.js'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import ejs from 'ejs'; +import * as mailServer from './mailserver.js'; +import nodemailer from 'nodemailer'; +import path from 'node:path'; +import safe from 'safetydance'; +import * as translations from './translations.js'; -exports = module.exports = { +const debug = debugModule('box:mailer'); + +const _mailQueue = []; // accumulate mails in test mode; + +function clearMailQueue() { + _mailQueue.length = 0; +} + +export { passwordReset, - sendInvite, sendNewLoginLocation, - backupFailed, certificateRenewalError, appDown, @@ -14,26 +31,12 @@ exports = module.exports = { rebootRequired, boxUpdateError, lowDiskSpace, - sendTestMail, - - _mailQueue: [] // accumulate mails in test mode + _mailQueue, + clearMailQueue, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - branding = require('./branding.js'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:mailer'), - ejs = require('ejs'), - mailServer = require('./mailserver.js'), - nodemailer = require('nodemailer'), - path = require('node:path'), - safe = require('safetydance'), - translations = require('./translations.js'); - -const MAIL_TEMPLATES_DIR = path.join(__dirname, 'mail_templates'); +const MAIL_TEMPLATES_DIR = path.join(import.meta.dirname, 'mail_templates'); // This will collect the most common details required for notification emails async function getMailConfig() { @@ -53,7 +56,7 @@ async function sendMail(mailOptions) { assert.strictEqual(typeof mailOptions, 'object'); if (constants.TEST) { - exports._mailQueue.push(mailOptions); + _mailQueue.push(mailOptions); return; } diff --git a/src/mailserver.js b/src/mailserver.js index a26306876..282e95cee 100644 --- a/src/mailserver.js +++ b/src/mailserver.js @@ -1,49 +1,46 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import * as docker from './docker.js'; +import * as domains from './domains.js'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import hat from './hat.js'; +import infra from './infra_version.js'; +import Location from './location.js'; +import locks from './locks.js'; +import * as mail from './mail.js'; +import paths from './paths.js'; +import * as platform from './platform.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import services from './services.js'; +import * as settings from './settings.js'; +import shellModule from './shell.js'; +import tasks from './tasks.js'; +import * as users from './users.js'; -exports = module.exports = { +const debug = debugModule('box:mailserver'); +const shell = shellModule('mailserver'); + +const DEFAULT_MEMORY_LIMIT = 512 * 1024 * 1024; + +export { restart, start, - onDomainAdded, onDomainRemoved, - checkCertificate, - getMailAuth, - getLocation, startChangeLocation, changeLocation, - setLocation, - - DEFAULT_MEMORY_LIMIT: 512 * 1024 * 1024, + DEFAULT_MEMORY_LIMIT, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:mailserver'), - dns = require('./dns.js'), - docker = require('./docker.js'), - domains = require('./domains.js'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - hat = require('./hat.js'), - infra = require('./infra_version.js'), - Location = require('./location.js'), - locks = require('./locks.js'), - mail = require('./mail.js'), - paths = require('./paths.js'), - platform = require('./platform.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - services = require('./services.js'), - settings = require('./settings.js'), - shell = require('./shell.js')('mailserver'), - tasks = require('./tasks.js'), - users = require('./users.js'); - async function createMailConfig(mailFqdn) { assert.strictEqual(typeof mailFqdn, 'string'); @@ -121,7 +118,7 @@ async function configureMail(mailFqdn, mailDomain, serviceConfig) { // mail container uses /app/data for backed up data and /run for restart-able data const image = infra.images.mail; - const memoryLimit = serviceConfig.memoryLimit || exports.DEFAULT_MEMORY_LIMIT; + const memoryLimit = serviceConfig.memoryLimit || DEFAULT_MEMORY_LIMIT; const cloudronToken = hat(8 * 128), relayToken = hat(8 * 128); const certificate = await reverseProxy.getMailCertificate(); diff --git a/src/metrics.js b/src/metrics.js index 99286ede9..8cc9f670a 100644 --- a/src/metrics.js +++ b/src/metrics.js @@ -1,27 +1,28 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import * as docker from './docker.js'; +import fs from 'node:fs'; +import net from 'node:net'; +import * as network from './network.js'; +import os from 'node:os'; +import { Readable } from 'node:stream'; +import safe from 'safetydance'; +import shellModule from './shell.js'; +import superagent from '@cloudron/superagent'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:metrics'); +const shell = shellModule('metrics'); + +export { get, getStream, sendToGraphite }; -const apps = require('./apps.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:metrics'), - docker = require('./docker.js'), - fs = require('node:fs'), - net = require('node:net'), - network = require('./network.js'), - os = require('node:os'), - { Readable } = require('node:stream'), - safe = require('safetydance'), - shell = require('./shell.js')('metrics'), - superagent = require('@cloudron/superagent'), - _ = require('./underscore.js'); - function translateContainerStatsSync(stats) { assert.strictEqual(typeof stats, 'object'); @@ -218,7 +219,7 @@ async function sendToGraphite() { // the datapoint is (value, timestamp) https://graphite.readthedocs.io/en/latest/ async function getGraphiteUrl() { const [error, result] = await safe(docker.inspect('graphite')); - if (error && error.reason === BoxError.NOT_FOUND) return { status: exports.SERVICE_STATUS_STOPPED }; + if (error && error.reason === BoxError.NOT_FOUND) return { status: SERVICE_STATUS_STOPPED }; if (error) throw error; const ip = safe.query(result, 'NetworkSettings.Networks.cloudron.IPAddress', null); diff --git a/src/middleware/cors.js b/src/middleware/cors.js index e915018a8..9d62d1fa1 100644 --- a/src/middleware/cors.js +++ b/src/middleware/cors.js @@ -1,15 +1,13 @@ /* jshint node:true */ -'use strict'; - -const url = require('node:url'); +import url from 'node:url'; /* * CORS middleware * * options can contains a list of origins */ -module.exports = function cors(options) { +export default function cors(options) { options = options || { }; const maxAge = options.maxAge || 60 * 60 * 25 * 5; // 5 days const origins = options.origins || [ '*' ]; diff --git a/src/middleware/index.js b/src/middleware/index.js index 1039252d8..79ad36c27 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -1,10 +1,15 @@ -'use strict'; +import cookieParser from 'cookie-parser'; +import cors from './cors.js'; +import json from './json.js'; +import lastMile from '@cloudron/connect-lastmile'; +import multipart from './multipart.js'; +import timeout from 'connect-timeout'; -exports = module.exports = { - cookieParser: require('cookie-parser'), - cors: require('./cors.js'), - json: require('./json.js'), - lastMile: require('@cloudron/connect-lastmile'), - multipart: require('./multipart.js'), - timeout: require('connect-timeout') +export { + cookieParser, + cors, + json, + lastMile, + multipart, + timeout }; diff --git a/src/middleware/json.js b/src/middleware/json.js index 71512cc83..eb7b9ca12 100644 --- a/src/middleware/json.js +++ b/src/middleware/json.js @@ -1,13 +1,11 @@ -'use strict'; - -const express = require('express'); +import express from 'express'; function _mime(req) { const str = req.headers['content-type'] || ''; return str.split(';')[0]; } -exports = module.exports = function (options, forceContentType = true) { +export default function (options, forceContentType = true) { const json = express.json(options); return function (req, res, next) { diff --git a/src/middleware/multipart.js b/src/middleware/multipart.js index a9264a484..dbf753abf 100644 --- a/src/middleware/multipart.js +++ b/src/middleware/multipart.js @@ -1,17 +1,15 @@ /* jshint node:true */ -'use strict'; - -const multiparty = require('multiparty'), - safe = require('safetydance'), - timeout = require('connect-timeout'); +import multiparty from 'multiparty'; +import safe from 'safetydance'; +import timeout from 'connect-timeout'; function _mime(req) { const str = req.headers['content-type'] || ''; return str.split(';')[0]; } -module.exports = function multipart(options, forceContentType = true) { +export default function multipart(options, forceContentType = true) { return function (req, res, next) { if (_mime(req) !== 'multipart/form-data') { if (forceContentType) return res.status(400).send('Invalid content-type. Expecting multipart'); diff --git a/src/mounts.js b/src/mounts.js index 4ce2e344b..8827fb613 100644 --- a/src/mounts.js +++ b/src/mounts.js @@ -1,38 +1,47 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import ejs from 'ejs'; +import fs from 'node:fs'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import shellModule from './shell.js'; -exports = module.exports = { +const debug = debugModule('box:mounts'); +const shell = shellModule('mounts'); + +const MOUNT_TYPE_FILESYSTEM = 'filesystem'; +const MOUNT_TYPE_MOUNTPOINT = 'mountpoint'; +const MOUNT_TYPE_CIFS = 'cifs'; +const MOUNT_TYPE_NFS = 'nfs'; +const MOUNT_TYPE_SSHFS = 'sshfs'; +const MOUNT_TYPE_EXT4 = 'ext4'; // raw disk path +const MOUNT_TYPE_XFS = 'xfs'; // raw disk path +const MOUNT_TYPE_DISK = 'disk'; // this provides a selector of block devices + +export default { isManagedProvider, tryAddMount, removeMount, validateMountOptions, getStatus, remount, - - MOUNT_TYPE_FILESYSTEM: 'filesystem', - MOUNT_TYPE_MOUNTPOINT: 'mountpoint', - MOUNT_TYPE_CIFS: 'cifs', - MOUNT_TYPE_NFS: 'nfs', - MOUNT_TYPE_SSHFS: 'sshfs', - MOUNT_TYPE_EXT4: 'ext4', // raw disk path - MOUNT_TYPE_XFS: 'xfs', // raw disk path - MOUNT_TYPE_DISK: 'disk', // this provides a selector of block devices + MOUNT_TYPE_FILESYSTEM, + MOUNT_TYPE_MOUNTPOINT, + MOUNT_TYPE_CIFS, + MOUNT_TYPE_NFS, + MOUNT_TYPE_SSHFS, + MOUNT_TYPE_EXT4, + MOUNT_TYPE_XFS, + MOUNT_TYPE_DISK, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:mounts'), - ejs = require('ejs'), - fs = require('node:fs'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - shell = require('./shell.js')('mounts'); - -const ADD_MOUNT_CMD = path.join(__dirname, 'scripts/addmount.sh'); -const RM_MOUNT_CMD = path.join(__dirname, 'scripts/rmmount.sh'); -const REMOUNT_MOUNT_CMD = path.join(__dirname, 'scripts/remountmount.sh'); -const SYSTEMD_MOUNT_EJS = fs.readFileSync(path.join(__dirname, 'systemd-mount.ejs'), { encoding: 'utf8' }); +const ADD_MOUNT_CMD = path.join(import.meta.dirname, 'scripts/addmount.sh'); +const RM_MOUNT_CMD = path.join(import.meta.dirname, 'scripts/rmmount.sh'); +const REMOUNT_MOUNT_CMD = path.join(import.meta.dirname, 'scripts/remountmount.sh'); +const SYSTEMD_MOUNT_EJS = fs.readFileSync(path.join(import.meta.dirname, 'systemd-mount.ejs'), { encoding: 'utf8' }); // https://man7.org/linux/man-pages/man8/mount.8.html async function validateMountOptions(type, options) { @@ -40,31 +49,31 @@ async function validateMountOptions(type, options) { assert.strictEqual(typeof options, 'object'); switch (type) { - case exports.MOUNT_TYPE_FILESYSTEM: - case exports.MOUNT_TYPE_MOUNTPOINT: + case MOUNT_TYPE_FILESYSTEM: + case MOUNT_TYPE_MOUNTPOINT: if (typeof options.hostPath !== 'string') return new BoxError(BoxError.BAD_FIELD, 'hostPath is not a string'); return null; - case exports.MOUNT_TYPE_CIFS: + case MOUNT_TYPE_CIFS: if (typeof options.username !== 'string') return new BoxError(BoxError.BAD_FIELD, 'username is not a string'); if (typeof options.password !== 'string') return new BoxError(BoxError.BAD_FIELD, 'password is not a string'); if (typeof options.host !== 'string') return new BoxError(BoxError.BAD_FIELD, 'host is not a string'); if (typeof options.remoteDir !== 'string') return new BoxError(BoxError.BAD_FIELD, 'remoteDir is not a string'); if ('seal' in options && typeof options.seal !== 'boolean') return new BoxError(BoxError.BAD_FIELD, 'seal is not a boolean'); return null; - case exports.MOUNT_TYPE_NFS: + case MOUNT_TYPE_NFS: if (typeof options.host !== 'string') return new BoxError(BoxError.BAD_FIELD, 'host is not a string'); if (typeof options.remoteDir !== 'string') return new BoxError(BoxError.BAD_FIELD, 'remoteDir is not a string'); return null; - case exports.MOUNT_TYPE_SSHFS: + case MOUNT_TYPE_SSHFS: if (typeof options.user !== 'string') return new BoxError(BoxError.BAD_FIELD, 'user is not a string'); if (typeof options.privateKey !== 'string') return new BoxError(BoxError.BAD_FIELD, 'privateKey is not a string'); if (typeof options.port !== 'number') return new BoxError(BoxError.BAD_FIELD, 'port is not a number'); if (typeof options.host !== 'string') return new BoxError(BoxError.BAD_FIELD, 'host is not a string'); if (typeof options.remoteDir !== 'string') return new BoxError(BoxError.BAD_FIELD, 'remoteDir is not a string'); return null; - case exports.MOUNT_TYPE_EXT4: - case exports.MOUNT_TYPE_XFS: - case exports.MOUNT_TYPE_DISK: { + case MOUNT_TYPE_EXT4: + case MOUNT_TYPE_XFS: + case MOUNT_TYPE_DISK: { if (typeof options.diskPath !== 'string') return new BoxError(BoxError.BAD_FIELD, 'diskPath is not a string'); const [error, output] = await safe(shell.spawn('lsblk', ['--paths', '--json', '--list', '--fs', options.diskPath ], { encoding: 'utf8' })); if (error) return new BoxError(BoxError.BAD_FIELD, `Bad disk path: ${error.message}`); @@ -90,12 +99,12 @@ async function validateMountOptions(type, options) { // managed providers are those for which we setup systemd mount file under /mnt/volumes function isManagedProvider(provider) { switch (provider) { - case exports.MOUNT_TYPE_SSHFS: - case exports.MOUNT_TYPE_CIFS: - case exports.MOUNT_TYPE_NFS: - case exports.MOUNT_TYPE_EXT4: - case exports.MOUNT_TYPE_XFS: - case exports.MOUNT_TYPE_DISK: + case MOUNT_TYPE_SSHFS: + case MOUNT_TYPE_CIFS: + case MOUNT_TYPE_NFS: + case MOUNT_TYPE_EXT4: + case MOUNT_TYPE_XFS: + case MOUNT_TYPE_DISK: return true; default: return false; @@ -113,7 +122,7 @@ async function renderMountFile(mount) { let options, what, type, dependsOn; switch (mountType) { - case exports.MOUNT_TYPE_CIFS: { + case MOUNT_TYPE_CIFS: { const out = await shell.spawn('systemd-escape', [ '-p', hostPath ], { encoding: 'utf8' }); // this ensures uniqueness of creds file const credentialsFilePath = path.join(paths.CIFS_CREDENTIALS_DIR, `${out.trim()}.cred`); if (!safe.fs.writeFileSync(credentialsFilePath, `username=${mountOptions.username}\npassword=${mountOptions.password}\n`, { mode: 0o600 })) throw new BoxError(BoxError.FS_ERROR, `Could not write credentials file: ${safe.error.message}`); @@ -124,28 +133,28 @@ async function renderMountFile(mount) { dependsOn = 'network-online.target'; break; } - case exports.MOUNT_TYPE_NFS: + case MOUNT_TYPE_NFS: type = 'nfs'; what = `${mountOptions.host}:${mountOptions.remoteDir}`; options = 'noauto'; // noauto means it is not a blocker for local-fs.target. _netdev is implicit. rw,hard,tcp,rsize=8192,wsize=8192,timeo=14 dependsOn = 'network-online.target'; break; - case exports.MOUNT_TYPE_EXT4: + case MOUNT_TYPE_EXT4: type = 'ext4'; what = mountOptions.diskPath; // like /dev/disk/by-uuid/uuid or /dev/disk/by-id/scsi-id options = 'discard,defaults,noatime'; break; - case exports.MOUNT_TYPE_XFS: + case MOUNT_TYPE_XFS: type = 'xfs'; what = mountOptions.diskPath; // like /dev/disk/by-uuid/uuid or /dev/disk/by-id/scsi-id options = 'discard,defaults,noatime,pquota'; break; - case exports.MOUNT_TYPE_DISK: + case MOUNT_TYPE_DISK: type = 'auto'; what = mountOptions.diskPath; // like /dev/disk/by-uuid/uuid or /dev/disk/by-id/scsi-id options = 'discard,defaults,noatime'; break; - case exports.MOUNT_TYPE_SSHFS: { + case MOUNT_TYPE_SSHFS: { const keyFilePath = path.join(paths.SSHFS_KEYS_DIR, `identity_file_${path.basename(hostPath)}`); if (!safe.fs.writeFileSync(keyFilePath, `${mount.mountOptions.privateKey}\n`, { mode: 0o600 })) throw new BoxError(BoxError.FS_ERROR, `Could not write private key: ${safe.error.message}`); @@ -155,8 +164,8 @@ async function renderMountFile(mount) { dependsOn = 'network-online.target'; break; } - case exports.MOUNT_TYPE_FILESYSTEM: - case exports.MOUNT_TYPE_MOUNTPOINT: + case MOUNT_TYPE_FILESYSTEM: + case MOUNT_TYPE_MOUNTPOINT: return; } @@ -172,10 +181,10 @@ async function removeMount(mount) { await safe(shell.sudo([ RM_MOUNT_CMD, hostPath ], {}), { debug }); // ignore any error - if (mountType === exports.MOUNT_TYPE_SSHFS) { + if (mountType === MOUNT_TYPE_SSHFS) { const keyFilePath = path.join(paths.SSHFS_KEYS_DIR, `identity_file_${path.basename(hostPath)}`); safe.fs.unlinkSync(keyFilePath); - } else if (mountType === exports.MOUNT_TYPE_CIFS) { + } else if (mountType === MOUNT_TYPE_CIFS) { const out = await shell.spawn('systemd-escape', [ '-p', hostPath ], { encoding: 'utf8' }); const credentialsFilePath = path.join(paths.CIFS_CREDENTIALS_DIR, `${out.trim()}.cred`); safe.fs.unlinkSync(credentialsFilePath); @@ -186,7 +195,7 @@ async function getStatus(mountType, hostPath) { assert.strictEqual(typeof mountType, 'string'); assert.strictEqual(typeof hostPath, 'string'); - if (mountType === exports.MOUNT_TYPE_FILESYSTEM) { + if (mountType === MOUNT_TYPE_FILESYSTEM) { const exists = safe.fs.existsSync(hostPath); return { state: exists ? 'active' : 'inactive', message: exists ? '' : `${hostPath} not found` }; } diff --git a/src/network.js b/src/network.js index 5b246dbee..dab518370 100644 --- a/src/network.js +++ b/src/network.js @@ -1,6 +1,22 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as cron from './cron.js'; +import fs from 'node:fs'; +import * as ipaddr from './ipaddr.js'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import shellModule from './shell.js'; +import * as noopProvider from './network/noop.js'; +import * as fixedProvider from './network/fixed.js'; +import * as networkInterfaceProvider from './network/network-interface.js'; +import * as genericProvider from './network/generic.js'; -exports = module.exports = { +const shell = shellModule('network'); + +export { testIPv4Config, testIPv6Config, @@ -24,31 +40,20 @@ exports = module.exports = { getDefaultInterface }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - cron = require('./cron.js'), - fs = require('node:fs'), - ipaddr = require('./ipaddr.js'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - settings = require('./settings.js'), - shell = require('./shell.js')('network'); - -const SET_BLOCKLIST_CMD = path.join(__dirname, 'scripts/setblocklist.sh'); +const SET_BLOCKLIST_CMD = path.join(import.meta.dirname, 'scripts/setblocklist.sh'); let gDefaultIface = null; // cache +const NETWORK_PROVIDERS = { + 'noop': noopProvider, + 'fixed': fixedProvider, + 'network-interface': networkInterfaceProvider +}; + function api(provider) { assert.strictEqual(typeof provider, 'string'); - switch (provider) { - case 'noop': return require('./network/noop.js'); - case 'fixed': return require('./network/fixed.js'); - case 'network-interface': return require('./network/network-interface.js'); - default: return require('./network/generic.js'); - } + return NETWORK_PROVIDERS[provider] || genericProvider; } function hasIPv6() { @@ -165,7 +170,6 @@ async function getIPv6() { } async function detectIP() { - const genericProvider = require('./network/generic.js'); const [error4, ipv4] = await safe(genericProvider.getIPv4({})); const [error6, ipv6] = await safe(genericProvider.getIPv6({})); diff --git a/src/network/fixed.js b/src/network/fixed.js index 84a26fd22..42610f726 100644 --- a/src/network/fixed.js +++ b/src/network/fixed.js @@ -1,16 +1,14 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import net from 'node:net'; -exports = module.exports = { +export { getIPv4, getIPv6, testIPv4Config, testIPv6Config }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - net = require('node:net'); - async function getIPv4(config) { assert.strictEqual(typeof config, 'object'); diff --git a/src/network/generic.js b/src/network/generic.js index fc403c688..7038e2085 100644 --- a/src/network/generic.js +++ b/src/network/generic.js @@ -1,19 +1,19 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; -exports = module.exports = { +const debug = debugModule('box:network/generic'); + +export { getIPv4, getIPv6, testIPv4Config, testIPv6Config }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:network/generic'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - const gCache = { ipv4: {}, ipv6: {} }; // each has { timestamp, value, request } async function getIP(type) { diff --git a/src/network/interface.js b/src/network/interface.js index 8f9b2c09c..6e0900a94 100644 --- a/src/network/interface.js +++ b/src/network/interface.js @@ -1,4 +1,5 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; // ------------------------------------------- // This file just describes the interface @@ -6,16 +7,13 @@ // New backends can start from here // ------------------------------------------- -exports = module.exports = { +export { getIPv4, getIPv6, testIPv4Config, testIPv6Config }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'); - async function getIPv4(config) { assert.strictEqual(typeof config, 'object'); diff --git a/src/network/network-interface.js b/src/network/network-interface.js index 9edf699fb..aaf253584 100644 --- a/src/network/network-interface.js +++ b/src/network/network-interface.js @@ -1,18 +1,18 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import os from 'node:os'; +import safe from 'safetydance'; -exports = module.exports = { +const debug = debugModule('box:network/network-interface'); + +export { getIPv4, getIPv6, testIPv4Config, testIPv6Config }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:network/network-interface'), - os = require('node:os'), - safe = require('safetydance'); - async function getIPv4(config) { assert.strictEqual(typeof config, 'object'); diff --git a/src/network/noop.js b/src/network/noop.js index c3c33d4c3..a5c7f6531 100644 --- a/src/network/noop.js +++ b/src/network/noop.js @@ -1,14 +1,12 @@ -'use strict'; +import assert from 'node:assert'; -exports = module.exports = { +export { getIPv4, getIPv6, testIPv4Config, testIPv6Config }; -const assert = require('node:assert'); - async function getIPv4(config) { assert.strictEqual(typeof config, 'object'); diff --git a/src/notifications.js b/src/notifications.js index a72c4f7c4..7e97f53ae 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -1,55 +1,65 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import BoxError from './boxerror.js'; +import * as changelog from './changelog.js'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import * as mailer from './mailer.js'; +import safe from 'safetydance'; +import * as users from './users.js'; -exports = module.exports = { +const debug = debugModule('box:notifications'); + +const TYPE_CLOUDRON_INSTALLED = 'cloudronInstalled'; +const TYPE_CLOUDRON_UPDATED = 'cloudronUpdated'; +const TYPE_CLOUDRON_UPDATE_FAILED = 'cloudronUpdateFailed'; +const TYPE_CERTIFICATE_RENEWAL_FAILED = 'certificateRenewalFailed'; +const TYPE_BACKUP_CONFIG = 'backupConfig'; +const TYPE_APP_OOM = 'appOutOfMemory'; +const TYPE_APP_UPDATED = 'appUpdated'; +const TYPE_BACKUP_FAILED = 'backupFailed'; +const TYPE_APP_DOWN = 'appDown'; +const TYPE_APP_UP = 'appUp'; +const TYPE_DISK_SPACE = 'diskSpace'; +const TYPE_MAIL_STATUS = 'mailStatus'; +const TYPE_REBOOT = 'reboot'; +const TYPE_UPDATE_UBUNTU = 'ubuntuUpdate'; +const TYPE_BOX_UPDATE = 'boxUpdate'; +const TYPE_MANUAL_APP_UPDATE_NEEDED = 'manualAppUpdate'; +const TYPE_DOMAIN_CONFIG_CHECK_FAILED = 'domainConfigCheckFailed'; +const _add = add; + +export { get, update, list, del, - onEvent, - - // these are notification types, keep in sync with client.js - TYPE_CLOUDRON_INSTALLED: 'cloudronInstalled', - TYPE_CLOUDRON_UPDATED: 'cloudronUpdated', - TYPE_CLOUDRON_UPDATE_FAILED: 'cloudronUpdateFailed', - TYPE_CERTIFICATE_RENEWAL_FAILED: 'certificateRenewalFailed', - TYPE_BACKUP_CONFIG: 'backupConfig', - TYPE_APP_OOM: 'appOutOfMemory', - TYPE_APP_UPDATED: 'appUpdated', - TYPE_BACKUP_FAILED: 'backupFailed', - TYPE_APP_DOWN: 'appDown', - TYPE_APP_UP: 'appUp', - - // these are singleton types allowed in pin() and unpin() - TYPE_DISK_SPACE: 'diskSpace', - TYPE_MAIL_STATUS: 'mailStatus', - TYPE_REBOOT: 'reboot', - TYPE_UPDATE_UBUNTU: 'ubuntuUpdate', - TYPE_BOX_UPDATE: 'boxUpdate', - TYPE_MANUAL_APP_UPDATE_NEEDED: 'manualAppUpdate', - TYPE_DOMAIN_CONFIG_CHECK_FAILED: 'domainConfigCheckFailed', - - // these work off singleton types + TYPE_CLOUDRON_INSTALLED, + TYPE_CLOUDRON_UPDATED, + TYPE_CLOUDRON_UPDATE_FAILED, + TYPE_CERTIFICATE_RENEWAL_FAILED, + TYPE_BACKUP_CONFIG, + TYPE_APP_OOM, + TYPE_APP_UPDATED, + TYPE_BACKUP_FAILED, + TYPE_APP_DOWN, + TYPE_APP_UP, + TYPE_DISK_SPACE, + TYPE_MAIL_STATUS, + TYPE_REBOOT, + TYPE_UPDATE_UBUNTU, + TYPE_BOX_UPDATE, + TYPE_MANUAL_APP_UPDATE_NEEDED, + TYPE_DOMAIN_CONFIG_CHECK_FAILED, pin, unpin, - - // exported for testing - _add: add + _add, }; -const assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - BoxError = require('./boxerror.js'), - changelog = require('./changelog.js'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - debug = require('debug')('box:notifications'), - eventlog = require('./eventlog.js'), - mailer = require('./mailer.js'), - safe = require('safetydance'), - users = require('./users.js'); - const NOTIFICATION_FIELDS = [ 'id', 'eventId', 'type', 'title', 'message', 'creationTime', 'acknowledged', 'context' ]; function postProcess(result) { @@ -163,11 +173,11 @@ async function oomEvent(eventId, containerId, app, addonName, event) { message = `The app has been restarted automatically. If you see this notification often, consider increasing the [memory limit](https://${dashboardFqdn}/#/app/${app.id}/resources)`; } - await add(exports.TYPE_APP_OOM, title, message, { eventId }); + await add(TYPE_APP_OOM, title, message, { eventId }); const admins = await users.getAdmins(); for (const admin of admins) { - if (admin.notificationConfig.includes(exports.TYPE_APP_OOM)) { + if (admin.notificationConfig.includes(TYPE_APP_OOM)) { await safe(mailer.oomEvent(admin.email, containerId, app, addonName, event), { debug }); } } @@ -179,7 +189,7 @@ async function appUp(eventId, app) { const admins = await users.getAdmins(); for (const admin of admins) { - if (admin.notificationConfig.includes(exports.TYPE_APP_UP)) { + if (admin.notificationConfig.includes(TYPE_APP_UP)) { await safe(mailer.appUp(admin.email, app), { debug }); } } @@ -191,7 +201,7 @@ async function appDown(eventId, app) { const admins = await users.getAdmins(); for (const admin of admins) { - if (admin.notificationConfig.includes(exports.TYPE_APP_DOWN)) { + if (admin.notificationConfig.includes(TYPE_APP_DOWN)) { await safe(mailer.appDown(admin.email, app), { debug }); } } @@ -210,7 +220,7 @@ async function appUpdated(eventId, app, fromManifest, toManifest) { const title = upstreamVersion ? `${toManifest.title} at ${app.fqdn} updated to ${upstreamVersion} (package version ${toManifest.version})` : `${toManifest.title} at ${app.fqdn} updated to package version ${toManifest.version}`; - await add(exports.TYPE_APP_UPDATED, title, `The application installed at https://${app.fqdn} was updated.\n\nChangelog:\n${toManifest.changelog}\n`, { eventId }); + await add(TYPE_APP_UPDATED, title, `The application installed at https://${app.fqdn} was updated.\n\nChangelog:\n${toManifest.changelog}\n`, { eventId }); } async function boxUpdated(eventId, oldVersion, newVersion) { @@ -221,7 +231,7 @@ async function boxUpdated(eventId, oldVersion, newVersion) { const changes = changelog.getChanges(newVersion); const changelogMarkdown = changes.map((m) => `* ${m}\n`).join(''); - await add(exports.TYPE_CLOUDRON_UPDATED, `Cloudron updated to v${newVersion}`, `Cloudron was updated from v${oldVersion} to v${newVersion}.\n\nChangelog:\n${changelogMarkdown}\n`, { eventId }); + await add(TYPE_CLOUDRON_UPDATED, `Cloudron updated to v${newVersion}`, `Cloudron was updated from v${oldVersion} to v${newVersion}.\n\nChangelog:\n${changelogMarkdown}\n`, { eventId }); } async function boxInstalled(eventId, version) { @@ -231,18 +241,18 @@ async function boxInstalled(eventId, version) { const changes = changelog.getChanges(version.replace(/\.([^.]*)$/, '.0')); // last .0 release const changelogMarkdown = changes.map((m) => `* ${m}\n`).join(''); - await add(exports.TYPE_CLOUDRON_INSTALLED, `Cloudron v${version} installed`, `Cloudron v${version} was installed.\n\nChangelog:\n${changelogMarkdown}\n`, { eventId }); + await add(TYPE_CLOUDRON_INSTALLED, `Cloudron v${version} installed`, `Cloudron v${version} was installed.\n\nChangelog:\n${changelogMarkdown}\n`, { eventId }); } async function boxUpdateError(eventId, errorMessage) { assert.strictEqual(typeof eventId, 'string'); assert.strictEqual(typeof errorMessage, 'string'); - await add(exports.TYPE_CLOUDRON_UPDATE_FAILED, 'Cloudron update failed', `Failed to update Cloudron: ${errorMessage}.`, { eventId }); + await add(TYPE_CLOUDRON_UPDATE_FAILED, 'Cloudron update failed', `Failed to update Cloudron: ${errorMessage}.`, { eventId }); const admins = await users.getAdmins(); for (const admin of admins) { - if (admin.notificationConfig.includes(exports.TYPE_CLOUDRON_UPDATE_FAILED)) { + if (admin.notificationConfig.includes(TYPE_CLOUDRON_UPDATE_FAILED)) { await safe(mailer.boxUpdateError(admin.email, errorMessage), { debug }); } } @@ -253,11 +263,11 @@ async function certificateRenewalError(eventId, fqdn, errorMessage) { assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof errorMessage, 'string'); - await add(exports.TYPE_CERTIFICATE_RENEWAL_FAILED, `Certificate renewal of ${fqdn} failed`, `Failed to renew certs of ${fqdn}: ${errorMessage}. Renewal will be retried in 12 hours.`, { eventId }); + await add(TYPE_CERTIFICATE_RENEWAL_FAILED, `Certificate renewal of ${fqdn} failed`, `Failed to renew certs of ${fqdn}: ${errorMessage}. Renewal will be retried in 12 hours.`, { eventId }); const admins = await users.getAdmins(); for (const admin of admins) { - if (admin.notificationConfig.includes(exports.TYPE_CERTIFICATE_RENEWAL_FAILED)) { + if (admin.notificationConfig.includes(TYPE_CERTIFICATE_RENEWAL_FAILED)) { await safe(mailer.certificateRenewalError(admin.email, fqdn, errorMessage), { debug }); } } @@ -268,12 +278,12 @@ async function backupFailed(eventId, taskId, errorMessage) { assert.strictEqual(typeof taskId, 'string'); assert.strictEqual(typeof errorMessage, 'string'); - await add(exports.TYPE_BACKUP_FAILED, 'Backup failed', `Backup failed: ${errorMessage}. Logs are available [here](/logs.html?taskId=${taskId}).`, { eventId }); + await add(TYPE_BACKUP_FAILED, 'Backup failed', `Backup failed: ${errorMessage}. Logs are available [here](/logs.html?taskId=${taskId}).`, { eventId }); const { fqdn:dashboardFqdn } = await dashboard.getLocation(); const superadmins = await users.getSuperadmins(); for (const superadmin of superadmins) { - if (superadmin.notificationConfig.includes(exports.TYPE_BACKUP_FAILED)) { + if (superadmin.notificationConfig.includes(TYPE_BACKUP_FAILED)) { await safe(mailer.backupFailed(superadmin.email, errorMessage, `https://${dashboardFqdn}/logs.html?taskId=${taskId}`), { debug }); } } @@ -282,7 +292,7 @@ async function backupFailed(eventId, taskId, errorMessage) { async function rebootRequired() { const admins = await users.getAdmins(); for (const admin of admins) { - if (admin.notificationConfig.includes(exports.TYPE_REBOOT)) { + if (admin.notificationConfig.includes(TYPE_REBOOT)) { await safe(mailer.rebootRequired(admin.email), { debug }); } } @@ -293,7 +303,7 @@ async function lowDiskSpace(message) { const admins = await users.getAdmins(); for (const admin of admins) { - if (admin.notificationConfig.includes(exports.TYPE_DISK_SPACE)) { + if (admin.notificationConfig.includes(TYPE_DISK_SPACE)) { await safe(mailer.lowDiskSpace(admin.email, message), { debug }); } } @@ -304,9 +314,9 @@ async function onPin(type, message) { assert.strictEqual(typeof message, 'string'); switch (type) { - case exports.TYPE_REBOOT: + case TYPE_REBOOT: return await rebootRequired(); - case exports.TYPE_DISK_SPACE: + case TYPE_DISK_SPACE: return await lowDiskSpace(message); default: break; @@ -326,7 +336,7 @@ async function pin(type, title, message, options) { } // do not reset the ack state if user has already seen the update notification - const isUpdateType = type === exports.TYPE_BOX_UPDATE || type === exports.TYPE_MANUAL_APP_UPDATE_NEEDED; + const isUpdateType = type === TYPE_BOX_UPDATE || type === TYPE_MANUAL_APP_UPDATE_NEEDED; const acknowledged = (isUpdateType && result.message === message) ? result.acknowledged : false; if (result.acknowledged && !acknowledged) await onPin(type, message); diff --git a/src/oidcclients.js b/src/oidcclients.js index 183ac2b09..0ab0e5bb5 100644 --- a/src/oidcclients.js +++ b/src/oidcclients.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import hat from './hat.js'; +import safe from 'safetydance'; -exports = module.exports = { +const ID_WEBADMIN = 'cid-webadmin'; +const ID_DEVELOPMENT = 'cid-development'; +const ID_CLI = 'cid-cli'; +const ID_SDK = 'cid-sdk'; + +export default { add, get, del, @@ -10,19 +20,12 @@ exports = module.exports = { validateId, // token client ids. we categorize them so we can have different restrictions based on the client - ID_WEBADMIN: 'cid-webadmin', // dashboard - ID_DEVELOPMENT: 'cid-development', // dashboard development - ID_CLI: 'cid-cli', // cloudron cli - ID_SDK: 'cid-sdk', // created by user via dashboard + ID_WEBADMIN, + ID_DEVELOPMENT, + ID_CLI, + ID_SDK, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - hat = require('./hat.js'), - safe = require('safetydance'); - const OIDC_CLIENTS_TABLE_NAME = 'oidcClients'; const OIDC_CLIENTS_FIELDS = [ 'id', 'secret', 'name', 'appId', 'loginRedirectUri', 'tokenSignatureAlgorithm' ]; const DEFAULT_TOKEN_SIGNATURE_ALGORITHM='RS256'; @@ -30,7 +33,7 @@ const DEFAULT_TOKEN_SIGNATURE_ALGORITHM='RS256'; function validateId(type) { assert.strictEqual(typeof type, 'string'); - const types = [ exports.ID_WEBADMIN, exports.ID_SDK, exports.ID_DEVELOPMENT, exports.ID_CLI ]; + const types = [ ID_WEBADMIN, ID_SDK, ID_DEVELOPMENT, ID_CLI ]; if (types.indexOf(type) === -1) return new BoxError(BoxError.BAD_FIELD, `type must be one of ${types.join(',')}`); return null; @@ -66,19 +69,19 @@ async function add(data) { async function get(id) { assert.strictEqual(typeof id, 'string'); - if (id === exports.ID_WEBADMIN) { + if (id === ID_WEBADMIN) { const { fqdn:dashboardFqdn } = await dashboard.getLocation(); return { - id: exports.ID_WEBADMIN, + id: ID_WEBADMIN, secret: 'notused', application_type: 'web', response_types: ['code', 'code token'], grant_types: ['authorization_code', 'implicit'], loginRedirectUri: `https://${dashboardFqdn}/authcallback.html` }; - } else if (id === exports.ID_DEVELOPMENT) { + } else if (id === ID_DEVELOPMENT) { return { - id: exports.ID_DEVELOPMENT, + id: ID_DEVELOPMENT, secret: 'notused', application_type: 'native', // have to use native here to support plaintext http, this however makes it impossible to skip consent screen response_types: ['code', 'code token'], diff --git a/src/oidcserver.js b/src/oidcserver.js index b912e3700..a9307c0d5 100644 --- a/src/oidcserver.js +++ b/src/oidcserver.js @@ -1,6 +1,39 @@ -'use strict'; +import assert from 'node:assert'; +import apps from './apps.js'; +import AuditSource from './auditsource.js'; +import BoxError from './boxerror.js'; +import blobs from './blobs.js'; +import * as branding from './branding.js'; +import constants from './constants.js'; +import crypto from 'node:crypto'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import ejs from 'ejs'; +import express from 'express'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import * as marked from 'marked'; +import * as middleware from './middleware/index.js'; +import oidcClients from './oidcclients.js'; +import * as passkeys from './passkeys.js'; +import path from 'node:path'; +import paths from './paths.js'; +import http from 'node:http'; +import { HttpError } from '@cloudron/connect-lastmile'; +import * as jose from 'jose'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import superagent from '@cloudron/superagent'; +import * as tokens from './tokens.js'; +import url from 'node:url'; +import * as users from './users.js'; +import * as groups from './groups.js'; +import util from 'node:util'; -exports = module.exports = { +const debug = debugModule('box:oidcserver'); + +export { start, stop, revokeByUsername, @@ -9,39 +42,6 @@ exports = module.exports = { cleanupExpired, }; -const assert = require('node:assert'), - apps = require('./apps.js'), - AuditSource = require('./auditsource.js'), - BoxError = require('./boxerror.js'), - blobs = require('./blobs.js'), - branding = require('./branding.js'), - constants = require('./constants.js'), - crypto = require('node:crypto'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:oidcserver'), - dns = require('./dns.js'), - ejs = require('ejs'), - express = require('express'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - marked = require('marked'), - middleware = require('./middleware'), - oidcClients = require('./oidcclients.js'), - passkeys = require('./passkeys.js'), - path = require('node:path'), - paths = require('./paths.js'), - http = require('node:http'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - jose = require('jose'), - safe = require('safetydance'), - settings = require('./settings.js'), - superagent = require('@cloudron/superagent'), - tokens = require('./tokens.js'), - url = require('node:url'), - users = require('./users.js'), - groups = require('./groups.js'), - util = require('node:util'); - // 1. Index.vue starts the OIDC flow by navigating to /openid/auth. Webadmin sets callback url to authcallback.html + implicit flow // 2. oidcserver starts an interaction and redirects to oidc_login.html // 3. oidc_login.html is rendered by renderInteractionPage() with the form submit url /interaction/:uid/login diff --git a/src/once.js b/src/once.js index ccbc666e1..cb5718d65 100644 --- a/src/once.js +++ b/src/once.js @@ -1,8 +1,8 @@ -'use strict'; +import debugModule from 'debug'; -exports = module.exports = once; +const debug = debugModule('box:once'); -const debug = require('debug')('box:once'); +export default once; // https://github.com/isaacs/once/blob/main/LICENSE (ISC) function once (fn) { diff --git a/src/openssl.js b/src/openssl.js index ea8f3362b..efad96fd2 100644 --- a/src/openssl.js +++ b/src/openssl.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import debugModule from 'debug'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import safe from 'safetydance'; +import shellModule from './shell.js'; -exports = module.exports = { +const debug = debugModule('box:openssl'); +const shell = shellModule('openssl'); + +export { createCsr, generateKey, getModulus, @@ -18,16 +29,6 @@ exports = module.exports = { getAuthorityKeyId }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - debug = require('debug')('box:openssl'), - fs = require('node:fs'), - os = require('node:os'), - path = require('node:path'), - safe = require('safetydance'), - shell = require('./shell.js')('openssl'); - async function generateKey(type) { debug(`generateKey: generating new key for${type}`); diff --git a/src/passkeys.js b/src/passkeys.js index 93014c685..ea8aed85e 100644 --- a/src/passkeys.js +++ b/src/passkeys.js @@ -1,6 +1,21 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import safe from 'safetydance'; +import { + generateRegistrationOptions as generateWebAuthnRegistrationOptions, + verifyRegistrationResponse, + generateAuthenticationOptions as generateWebAuthnAuthenticationOptions, + verifyAuthenticationResponse +} from '@simplewebauthn/server'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:passkeys'); + +export { list, get, getByCredentialId, @@ -16,21 +31,6 @@ exports = module.exports = { removePrivateFields }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - debug = require('debug')('box:passkeys'), - safe = require('safetydance'), - { - generateRegistrationOptions: generateWebAuthnRegistrationOptions, - verifyRegistrationResponse, - generateAuthenticationOptions: generateWebAuthnAuthenticationOptions, - verifyAuthenticationResponse - } = require('@simplewebauthn/server'), - _ = require('./underscore.js'); - const PASSKEY_FIELDS = [ 'id', 'userId', 'credentialId', 'publicKey', 'counter', 'transports', 'name', 'creationTime', 'lastUsedTime' ].join(','); // In-memory challenge store with expiration (challenges are short-lived) diff --git a/src/paths.js b/src/paths.js index 07cdd5bc1..89e3437c6 100644 --- a/src/paths.js +++ b/src/paths.js @@ -1,7 +1,5 @@ -'use strict'; - -const constants = require('./constants.js'), - path = require('node:path'); +import constants from './constants.js'; +import path from 'node:path'; function baseDir() { const homeDir = process.env.HOME; @@ -11,14 +9,14 @@ function baseDir() { } function dashboardDir() { - return constants.TEST ? path.resolve(__dirname, '../dashboard/dist') : path.join(baseDir(), 'box/dashboard/dist'); + return constants.TEST ? path.resolve(import.meta.dirname, '../dashboard/dist') : path.join(baseDir(), 'box/dashboard/dist'); } // keep these values in sync with start.sh -exports = module.exports = { +export default { baseDir, - CLOUDRON_DEFAULT_AVATAR_FILE: path.join(__dirname, 'avatar.png'), + CLOUDRON_DEFAULT_AVATAR_FILE: path.join(import.meta.dirname, 'avatar.png'), INFRA_VERSION_FILE: path.join(baseDir(), 'platformdata/INFRA_VERSION'), CRON_SEED_FILE: path.join(baseDir(), 'platformdata/CRON_SEED'), TRANSLATIONS_DIR: path.join(dashboardDir(), 'translation'), diff --git a/src/platform.js b/src/platform.js index d53754ccf..11096cc48 100644 --- a/src/platform.js +++ b/src/platform.js @@ -1,6 +1,35 @@ -'use strict'; +import apps from './apps.js'; +import * as appTaskManager from './apptaskmanager.js'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import backups from './backups.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as cron from './cron.js'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import * as dockerProxy from './dockerproxy.js'; +import fs from 'node:fs'; +import infra from './infra_version.js'; +import locks from './locks.js'; +import * as oidcServer from './oidcserver.js'; +import paths from './paths.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import services from './services.js'; +import shellModule from './shell.js'; +import tasks from './tasks.js'; +import timers from 'timers/promises'; +import * as updater from './updater.js'; +import * as users from './users.js'; +import * as volumes from './volumes.js'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:platform'); +const shell = shellModule('platform'); + +export { initialize, uninitialize, @@ -13,34 +42,6 @@ exports = module.exports = { getStatus }; -const apps = require('./apps.js'), - appTaskManager = require('./apptaskmanager.js'), - assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - backups = require('./backups.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - cron = require('./cron.js'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - debug = require('debug')('box:platform'), - dockerProxy = require('./dockerproxy.js'), - fs = require('node:fs'), - infra = require('./infra_version.js'), - locks = require('./locks.js'), - oidcServer = require('./oidcserver.js'), - paths = require('./paths.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - services = require('./services.js'), - shell = require('./shell.js')('platform'), - tasks = require('./tasks.js'), - timers = require('timers/promises'), - updater = require('./updater.js'), - users = require('./users.js'), - volumes = require('./volumes.js'), - _ = require('./underscore.js'); - const gStatus = { message: 'Initializing', state: 'starting' }; // starting, failed, ready are the states function getStatus() { diff --git a/src/progress-stream.js b/src/progress-stream.js index 71b432851..758891bf1 100644 --- a/src/progress-stream.js +++ b/src/progress-stream.js @@ -1,7 +1,6 @@ -'use strict'; +import stream from 'node:stream'; -const stream = require('node:stream'), - TransformStream = stream.Transform; +const TransformStream = stream.Transform; class ProgressStream extends TransformStream { #options; @@ -58,4 +57,4 @@ class ProgressStream extends TransformStream { } } -exports = module.exports = ProgressStream; +export default ProgressStream; diff --git a/src/promise-retry.js b/src/promise-retry.js index f1217264e..5a0dfc4c7 100644 --- a/src/promise-retry.js +++ b/src/promise-retry.js @@ -1,10 +1,8 @@ -'use strict'; +import assert from 'node:assert'; +import timers from 'timers/promises'; +import util from 'node:util'; -exports = module.exports = promiseRetry; - -const assert = require('node:assert'), - timers = require('timers/promises'), - util = require('node:util'); +export default promiseRetry; async function promiseRetry(options, asyncFunction) { assert.strictEqual(typeof options, 'object'); diff --git a/src/provision.js b/src/provision.js index 9873a851e..e02c5a23d 100644 --- a/src/provision.js +++ b/src/provision.js @@ -1,41 +1,41 @@ -'use strict'; +import * as appstore from './appstore.js'; +import assert from 'node:assert'; +import * as backupSites from './backupsites.js'; +import backups from './backups.js'; +import * as backuptask from './backuptask.js'; +import BoxError from './boxerror.js'; +import * as dashboard from './dashboard.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import * as domains from './domains.js'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import * as mail from './mail.js'; +import * as mailServer from './mailserver.js'; +import * as network from './network.js'; +import oidcClients from './oidcclients.js'; +import * as openssl from './openssl.js'; +import * as platform from './platform.js'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import semver from 'semver'; +import paths from './paths.js'; +import * as system from './system.js'; +import tasks from './tasks.js'; +import * as users from './users.js'; +import tld from 'tldjs'; +import * as tokens from './tokens.js'; -exports = module.exports = { +const debug = debugModule('box:provision'); + +export { setup, restore, activate, getStatus, }; -const appstore = require('./appstore.js'), - assert = require('node:assert'), - backupSites = require('./backupsites.js'), - backups = require('./backups.js'), - backuptask = require('./backuptask.js'), - BoxError = require('./boxerror.js'), - dashboard = require('./dashboard.js'), - constants = require('./constants.js'), - debug = require('debug')('box:provision'), - dns = require('./dns.js'), - domains = require('./domains.js'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - mail = require('./mail.js'), - mailServer = require('./mailserver.js'), - network = require('./network.js'), - oidcClients = require('./oidcclients.js'), - openssl = require('./openssl.js'), - platform = require('./platform.js'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - semver = require('semver'), - paths = require('./paths.js'), - system = require('./system.js'), - tasks = require('./tasks.js'), - users = require('./users.js'), - tld = require('tldjs'), - tokens = require('./tokens.js'); - // we cannot use tasks since the tasks table gets overwritten when db is imported const gStatus = { setup: { diff --git a/src/proxyauth.js b/src/proxyauth.js index 97ec7f87c..215dff4b6 100644 --- a/src/proxyauth.js +++ b/src/proxyauth.js @@ -1,35 +1,35 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import blobs from './blobs.js'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import ejs from 'ejs'; +import express from 'express'; +import fs from 'node:fs'; +import path from 'node:path'; +import paths from './paths.js'; +import hat from './hat.js'; +import http from 'node:http'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import jwt from 'jsonwebtoken'; +import * as middleware from './middleware/index.js'; +import * as oidcServer from './oidcserver.js'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import * as users from './users.js'; +import util from 'node:util'; + +const debug = debugModule('box:proxyAuth'); // heavily inspired from https://gock.net/blog/2020/nginx-subrequest-authentication-server/ and https://github.com/andygock/auth-server -exports = module.exports = { +export { start, stop }; -const apps = require('./apps.js'), - assert = require('node:assert'), - blobs = require('./blobs.js'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:proxyAuth'), - ejs = require('ejs'), - express = require('express'), - fs = require('node:fs'), - path = require('node:path'), - paths = require('./paths.js'), - hat = require('./hat.js'), - http = require('node:http'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - jwt = require('jsonwebtoken'), - middleware = require('./middleware'), - oidcServer = require('./oidcserver.js'), - safe = require('safetydance'), - settings = require('./settings.js'), - users = require('./users.js'), - util = require('node:util'); - let gHttpServer = null; let gTokenSecret = null; diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 3074e2d62..2e48f8c0b 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -1,6 +1,33 @@ -'use strict'; +import * as acme2 from './acme2.js'; +import apps from './apps.js'; +import assert from 'node:assert'; +import blobs from './blobs.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import * as dns from './dns.js'; +import * as docker from './docker.js'; +import * as domains from './domains.js'; +import ejs from 'ejs'; +import eventlog from './eventlog.js'; +import * as ipaddr from './ipaddr.js'; +import fs from 'node:fs'; +import Location from './location.js'; +import * as mailServer from './mailserver.js'; +import * as network from './network.js'; +import * as openssl from './openssl.js'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import shellModule from './shell.js'; +import tasks from './tasks.js'; -exports = module.exports = { +const debug = debugModule('box:reverseproxy'); +const shell = shellModule('reverseproxy'); + +export { setUserCertificate, // per location certificate setFallbackCertificate, // per domain certificate @@ -31,34 +58,8 @@ exports = module.exports = { setTrustedIps }; -const acme2 = require('./acme2.js'), - apps = require('./apps.js'), - assert = require('node:assert'), - blobs = require('./blobs.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:reverseproxy'), - dns = require('./dns.js'), - docker = require('./docker.js'), - domains = require('./domains.js'), - ejs = require('ejs'), - eventlog = require('./eventlog.js'), - ipaddr = require('./ipaddr.js'), - fs = require('node:fs'), - Location = require('./location.js'), - mailServer = require('./mailserver.js'), - network = require('./network.js'), - openssl = require('./openssl.js'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - settings = require('./settings.js'), - shell = require('./shell.js')('reverseproxy'), - tasks = require('./tasks.js'); - -const NGINX_APPCONFIG_EJS = fs.readFileSync(__dirname + '/nginxconfig.ejs', { encoding: 'utf8' }); -const RESTART_SERVICE_CMD = path.join(__dirname, 'scripts/restartservice.sh'); +const NGINX_APPCONFIG_EJS = fs.readFileSync(import.meta.dirname + '/nginxconfig.ejs', { encoding: 'utf8' }); +const RESTART_SERVICE_CMD = path.join(import.meta.dirname, 'scripts/restartservice.sh'); function nginxLocation(s) { if (!s.startsWith('!')) return s; @@ -393,7 +394,7 @@ async function writeDashboardNginxConfig(vhost, certificatePath) { assert.strictEqual(typeof certificatePath, 'object'); const data = { - sourceDir: path.resolve(__dirname, '..'), + sourceDir: path.resolve(import.meta.dirname, '..'), vhost, hasIPv6: network.hasIPv6(), endpoint: 'dashboard', @@ -451,7 +452,7 @@ async function writeAppLocationNginxConfig(app, location, certificatePath) { const { type, fqdn} = location; const data = { - sourceDir: path.resolve(__dirname, '..'), + sourceDir: path.resolve(import.meta.dirname, '..'), vhost: fqdn, hasIPv6: network.hasIPv6(), ip: null, @@ -694,7 +695,7 @@ async function writeDefaultConfig(options) { } const data = { - sourceDir: path.resolve(__dirname, '..'), + sourceDir: path.resolve(import.meta.dirname, '..'), vhost: '', hasIPv6: network.hasIPv6(), endpoint: options.activated ? 'ip' : 'setup', diff --git a/src/routes/accesscontrol.js b/src/routes/accesscontrol.js index b5ed58b4b..a95c65a30 100644 --- a/src/routes/accesscontrol.js +++ b/src/routes/accesscontrol.js @@ -1,6 +1,15 @@ -'use strict'; +import apps from '../apps.js'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import { HttpError } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import * as tokens from '../tokens.js'; +import * as users from '../users.js'; -exports = module.exports = { +const debug = debugModule('box:routes/accesscontrol'); + +export { passwordAuth, tokenAuth, @@ -8,15 +17,6 @@ exports = module.exports = { authorizeOperator, }; -const apps = require('../apps.js'), - assert = require('node:assert'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:routes/accesscontrol'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - safe = require('safetydance'), - tokens = require('../tokens.js'), - users = require('../users.js'); - async function passwordAuth(req, res, next) { assert.strictEqual(typeof req.body, 'object'); diff --git a/src/routes/applinks.js b/src/routes/applinks.js index 9d24d61ea..a5752003f 100644 --- a/src/routes/applinks.js +++ b/src/routes/applinks.js @@ -1,6 +1,11 @@ -'use strict'; +import assert from 'node:assert'; +import * as applinks from '../applinks.js'; +import BoxError from '../boxerror.js'; +import safe from 'safetydance'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; -exports = module.exports = { +export { listByUser, add, get, @@ -11,13 +16,6 @@ exports = module.exports = { load }; -const assert = require('node:assert'), - applinks = require('../applinks.js'), - BoxError = require('../boxerror.js'), - safe = require('safetydance'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess; - async function load(req, res, next) { assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/apppasswords.js b/src/routes/apppasswords.js index 4f67101ac..7ca64132b 100644 --- a/src/routes/apppasswords.js +++ b/src/routes/apppasswords.js @@ -1,19 +1,17 @@ -'use strict'; +import * as appPasswords from '../apppasswords.js'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { list, get, del, add }; -const appPasswords = require('../apppasswords.js'), - assert = require('node:assert'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function get(req, res, next) { assert.strictEqual(typeof req.user, 'object'); assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/apps.js b/src/routes/apps.js index 354c58f00..fde660db2 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -1,6 +1,23 @@ -'use strict'; +import apps from '../apps.js'; +import * as appstore from '../appstore.js'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import * as backupSites from '../backupsites.js'; +import BoxError from '../boxerror.js'; +import * as community from '../community.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as metrics from '../metrics.js'; +import safe from 'safetydance'; +import * as updater from '../updater.js'; +import * as users from '../users.js'; +import WebSocket from 'ws'; -exports = module.exports = { +const debug = debugModule('box:routes/apps'); + +export { getApp, listByUser, getAppIcon, @@ -72,23 +89,6 @@ exports = module.exports = { load }; -const apps = require('../apps.js'), - appstore = require('../appstore.js'), - assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - backupSites = require('../backupsites.js'), - BoxError = require('../boxerror.js'), - community = require('../community.js'), - constants = require('../constants.js'), - debug = require('debug')('box:routes/apps'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - metrics = require('../metrics.js'), - safe = require('safetydance'), - updater = require('../updater.js'), - users = require('../users.js'), - WebSocket = require('ws'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/appstore.js b/src/routes/appstore.js index 1a2b257f7..a8db86b5e 100644 --- a/src/routes/appstore.js +++ b/src/routes/appstore.js @@ -1,6 +1,12 @@ -'use strict'; +import * as appstore from '../appstore.js'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import * as users from '../users.js'; +import * as _ from '../underscore.js'; -exports = module.exports = { +export { getApps, getApp, getAppVersion, @@ -9,14 +15,6 @@ exports = module.exports = { getSubscription }; -const appstore = require('../appstore.js'), - assert = require('node:assert'), - BoxError = require('../boxerror.js'), - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'), - users = require('../users.js'), - _ = require('../underscore.js'); - async function getApps(req, res, next) { const [error, apps] = await safe(appstore.getApps()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/archives.js b/src/routes/archives.js index e9a9b76dd..40933ecb1 100644 --- a/src/routes/archives.js +++ b/src/routes/archives.js @@ -1,6 +1,13 @@ -'use strict'; +import apps from '../apps.js'; +import assert from 'node:assert'; +import * as archives from '../archives.js'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { load, list, @@ -10,15 +17,6 @@ exports = module.exports = { unarchive }; -const apps = require('../apps.js'), - assert = require('node:assert'), - archives = require('../archives.js'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/auth.js b/src/routes/auth.js index 45aca6f2a..ef674eea4 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,6 +1,20 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import eventlog from '../eventlog.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import oidcClients from '../oidcclients.js'; +import safe from 'safetydance'; +import speakeasy from 'speakeasy'; +import * as tokens from '../tokens.js'; +import * as users from '../users.js'; -exports = module.exports = { +const debug = debugModule('box:routes/cloudron'); + +export { login, logout, passwordResetRequest, @@ -8,20 +22,6 @@ exports = module.exports = { setupAccount, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:routes/cloudron'), - eventlog = require('../eventlog.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - oidcClients = require('../oidcclients.js'), - safe = require('safetydance'), - speakeasy = require('speakeasy'), - tokens = require('../tokens.js'), - users = require('../users.js'); - async function login(req, res, next) { assert.strictEqual(typeof req.user, 'object'); diff --git a/src/routes/backups.js b/src/routes/backups.js index e289049e0..e89835e48 100644 --- a/src/routes/backups.js +++ b/src/routes/backups.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import backups from '../backups.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { load, list, @@ -11,14 +17,6 @@ exports = module.exports = { stopIntegrityCheck }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - backups = require('../backups.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/backupsites.js b/src/routes/backupsites.js index a94e7c26b..86cc5508c 100644 --- a/src/routes/backupsites.js +++ b/src/routes/backupsites.js @@ -1,6 +1,13 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import backups from '../backups.js'; +import * as backupSites from '../backupsites.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { load, list, @@ -25,15 +32,6 @@ exports = module.exports = { getStatus, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - backups = require('../backups.js'), - backupSites = require('../backupsites.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/branding.js b/src/routes/branding.js index 1b1ed377a..a1132ffc1 100644 --- a/src/routes/branding.js +++ b/src/routes/branding.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as branding from '../branding.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { getCloudronName, setCloudronName, getCloudronAvatar, @@ -12,14 +18,6 @@ exports = module.exports = { setFooter, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - branding = require('../branding.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function getFooter(req, res, next) { const [error, footer] = await safe(branding.getFooter()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index 84265f73d..4288c006b 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import * as cloudron from '../cloudron.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import * as translations from '../translations.js'; -exports = module.exports = { +export { getStatus, listLanguages, @@ -11,14 +17,6 @@ exports = module.exports = { setTimeZone }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - cloudron = require('../cloudron.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'), - translations = require('../translations.js'); - async function getStatus(req, res, next) { const [error, status] = await safe(cloudron.getStatus()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/community.js b/src/routes/community.js index 115a52726..b126f1465 100644 --- a/src/routes/community.js +++ b/src/routes/community.js @@ -1,15 +1,13 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import * as community from '../community.js'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { getAppVersion }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - community = require('../community.js'), - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function getAppVersion(req, res, next) { assert.strictEqual(typeof req.query.url, 'string'); assert.strictEqual(typeof req.query.version, 'string'); diff --git a/src/routes/dashboard.js b/src/routes/dashboard.js index 64eb89a2f..3712ff560 100644 --- a/src/routes/dashboard.js +++ b/src/routes/dashboard.js @@ -1,6 +1,18 @@ -'use strict'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as branding from '../branding.js'; +import constants from '../constants.js'; +import * as dashboard from '../dashboard.js'; +import ejs from 'ejs'; +import fs from 'node:fs'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import path from 'node:path'; +import paths from '../paths.js'; +import safe from 'safetydance'; +import * as settings from '../settings.js'; -exports = module.exports = { +export { getConfig, startPrepareLocation, @@ -17,20 +29,6 @@ exports = module.exports = { renderActivation, }; -const AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - branding = require('../branding.js'), - constants = require('../constants.js'), - dashboard = require('../dashboard.js'), - ejs = require('ejs'), - fs = require('node:fs'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - path = require('node:path'), - paths = require('../paths.js'), - safe = require('safetydance'), - settings = require('../settings.js'); - async function getConfig(req, res, next) { const [error, cloudronConfig] = await safe(dashboard.getConfig()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/directoryserver.js b/src/routes/directoryserver.js index d2ae08777..3ab9386ff 100644 --- a/src/routes/directoryserver.js +++ b/src/routes/directoryserver.js @@ -1,18 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as directoryServer from '../directoryserver.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { getConfig, setConfig, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - directoryServer = require('../directoryserver.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function getConfig(req, res, next) { const [error, config] = await safe(directoryServer.getConfig()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/dockerregistries.js b/src/routes/dockerregistries.js index 8a27dc709..02bd7e072 100644 --- a/src/routes/dockerregistries.js +++ b/src/routes/dockerregistries.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as dockerRegistries from '../dockerregistries.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { list, add, get, @@ -9,14 +15,6 @@ exports = module.exports = { load }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - dockerRegistries = require('../dockerregistries.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/domains.js b/src/routes/domains.js index 7d542caac..0599ed2a5 100644 --- a/src/routes/domains.js +++ b/src/routes/domains.js @@ -1,6 +1,13 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as dns from '../dns.js'; +import * as domains from '../domains.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { add, get, list, @@ -12,15 +19,6 @@ exports = module.exports = { syncDnsRecords, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - dns = require('../dns.js'), - domains = require('../domains.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function add(req, res, next) { assert.strictEqual(typeof req.body, 'object'); diff --git a/src/routes/eventlog.js b/src/routes/eventlog.js index daeabb642..1801f805f 100644 --- a/src/routes/eventlog.js +++ b/src/routes/eventlog.js @@ -1,16 +1,14 @@ -'use strict'; +import BoxError from '../boxerror.js'; +import eventlog from '../eventlog.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { get, list }; -const BoxError = require('../boxerror.js'), - eventlog = require('../eventlog.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function get(req, res, next) { const [error, event] = await safe(eventlog.get(req.params.eventId)); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/externalldap.js b/src/routes/externalldap.js index 82f83fdc2..35f0ecc10 100644 --- a/src/routes/externalldap.js +++ b/src/routes/externalldap.js @@ -1,19 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as externalLdap from '../externalldap.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { getConfig, setConfig, sync }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - externalLdap = require('../externalldap.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function sync(req, res, next) { const [error, taskId] = await safe(externalLdap.startSyncer()); if (error) return next(new HttpError(500, error.message)); diff --git a/src/routes/filemanager.js b/src/routes/filemanager.js index 0fbfa42ea..2634208dc 100644 --- a/src/routes/filemanager.js +++ b/src/routes/filemanager.js @@ -1,16 +1,14 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import http from 'node:http'; +import { HttpError } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import services from '../services.js'; -exports = module.exports = { +export { proxy, }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - http = require('node:http'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - safe = require('safetydance'), - services = require('../services.js'); - function proxy(kind) { assert(kind === 'mail' || kind === 'volume' || kind === 'app'); diff --git a/src/routes/groups.js b/src/routes/groups.js index acdcc5c5c..b9f66e5e9 100644 --- a/src/routes/groups.js +++ b/src/routes/groups.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as groups from '../groups.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { load, get, @@ -12,14 +18,6 @@ exports = module.exports = { setAllowedApps }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - groups = require('../groups.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.groupId, 'string'); diff --git a/src/routes/index.js b/src/routes/index.js index cc8189d51..71724e82c 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,41 +1,77 @@ -'use strict'; +import * as accesscontrol from './accesscontrol.js'; +import * as appPasswords from './apppasswords.js'; +import * as apps from './apps.js'; +import * as applinks from './applinks.js'; +import * as appstore from './appstore.js'; +import * as archives from './archives.js'; +import * as auth from './auth.js'; +import * as backups from './backups.js'; +import * as backupSites from './backupsites.js'; +import * as branding from './branding.js'; +import * as cloudron from './cloudron.js'; +import * as community from './community.js'; +import * as dashboard from './dashboard.js'; +import * as directoryServer from './directoryserver.js'; +import * as dockerRegistries from './dockerregistries.js'; +import * as domains from './domains.js'; +import * as eventlog from './eventlog.js'; +import * as externalLdap from './externalldap.js'; +import * as filemanager from './filemanager.js'; +import * as groups from './groups.js'; +import * as mail from './mail.js'; +import * as mailserver from './mailserver.js'; +import * as network from './network.js'; +import * as notifications from './notifications.js'; +import * as oidcClients from './oidcclients.js'; +import * as profile from './profile.js'; +import * as provision from './provision.js'; +import * as reverseProxy from './reverseproxy.js'; +import * as services from './services.js'; +import * as system from './system.js'; +import * as tasks from './tasks.js'; +import * as tokens from './tokens.js'; +import * as updater from './updater.js'; +import * as userDirectory from './user-directory.js'; +import * as users from './users.js'; +import * as volumes from './volumes.js'; +import * as wellknown from './wellknown.js'; -exports = module.exports = { - accesscontrol: require('./accesscontrol.js'), - appPasswords: require('./apppasswords.js'), - apps: require('./apps.js'), - applinks: require('./applinks.js'), - appstore: require('./appstore.js'), - archives: require('./archives.js'), - auth: require('./auth.js'), - backups: require('./backups.js'), - backupSites: require('./backupsites.js'), - branding: require('./branding.js'), - cloudron: require('./cloudron.js'), - community: require('./community.js'), - dashboard: require('./dashboard.js'), - directoryServer: require('./directoryserver.js'), - dockerRegistries: require('./dockerregistries.js'), - domains: require('./domains.js'), - eventlog: require('./eventlog.js'), - externalLdap: require('./externalldap.js'), - filemanager: require('./filemanager.js'), - groups: require('./groups.js'), - mail: require('./mail.js'), - mailserver: require('./mailserver.js'), - network: require('./network.js'), - notifications: require('./notifications.js'), - oidcClients: require('./oidcclients.js'), - profile: require('./profile.js'), - provision: require('./provision.js'), - reverseProxy: require('./reverseproxy.js'), - services: require('./services.js'), - system: require('./system.js'), - tasks: require('./tasks.js'), - tokens: require('./tokens.js'), - updater: require('./updater.js'), - userDirectory: require('./user-directory.js'), - users: require('./users.js'), - volumes: require('./volumes.js'), - wellknown: require('./wellknown.js') +export { + accesscontrol, + appPasswords, + apps, + applinks, + appstore, + archives, + auth, + backups, + backupSites, + branding, + cloudron, + community, + dashboard, + directoryServer, + dockerRegistries, + domains, + eventlog, + externalLdap, + filemanager, + groups, + mail, + mailserver, + network, + notifications, + oidcClients, + profile, + provision, + reverseProxy, + services, + system, + tasks, + tokens, + updater, + userDirectory, + users, + volumes, + wellknown }; diff --git a/src/routes/mail.js b/src/routes/mail.js index 7b1f0a1ab..649d53cc1 100644 --- a/src/routes/mail.js +++ b/src/routes/mail.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as mail from '../mail.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { getDomain, listDomains, @@ -32,14 +38,6 @@ exports = module.exports = { getStats }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - mail = require('../mail.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function getDomain(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); diff --git a/src/routes/mailserver.js b/src/routes/mailserver.js index 1e846fcd0..12bc7c0cd 100644 --- a/src/routes/mailserver.js +++ b/src/routes/mailserver.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import debugModule from 'debug'; +import http from 'node:http'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as mailServer from '../mailserver.js'; +import safe from 'safetydance'; +import services from '../services.js'; -exports = module.exports = { +const debug = debugModule('box:routes/mailserver'); + +export { proxy, proxyAndRestart, queueProxy, @@ -9,17 +20,6 @@ exports = module.exports = { getLocation }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - debug = require('debug')('box:routes/mailserver'), - http = require('node:http'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - mailServer = require('../mailserver.js'), - safe = require('safetydance'), - services = require('../services.js'); - async function proxyToMailContainer(port, pathname, req, res, next) { req.clearTimeout(); diff --git a/src/routes/network.js b/src/routes/network.js index 8f3378b56..fd29e1eb5 100644 --- a/src/routes/network.js +++ b/src/routes/network.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as network from '../network.js'; +import safe from 'safetydance'; -exports = module.exports = { +export { getBlocklist, setBlocklist, @@ -17,14 +23,6 @@ exports = module.exports = { getIPv6, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - network = require('../network.js'), - safe = require('safetydance'); - async function getBlocklist(req, res, next) { const [error, blocklist] = await safe(network.getBlocklist()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/notifications.js b/src/routes/notifications.js index 4a408bf1a..26eec0f10 100644 --- a/src/routes/notifications.js +++ b/src/routes/notifications.js @@ -1,19 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as notifications from '../notifications.js'; +import safe from 'safetydance'; -exports = module.exports = { +export { load, get, list, update }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - notifications = require('../notifications.js'), - safe = require('safetydance'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.notificationId, 'string'); diff --git a/src/routes/oidcclients.js b/src/routes/oidcclients.js index ada35441a..c8076512d 100644 --- a/src/routes/oidcclients.js +++ b/src/routes/oidcclients.js @@ -1,6 +1,11 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import oidcClients from '../oidcclients.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { load, add, list, @@ -9,13 +14,6 @@ exports = module.exports = { del, }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - oidcClients = require('../oidcclients.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function add(req, res, next) { assert.strictEqual(typeof req.body, 'object'); diff --git a/src/routes/profile.js b/src/routes/profile.js index 2a2c209d9..a5af7c800 100644 --- a/src/routes/profile.js +++ b/src/routes/profile.js @@ -1,6 +1,17 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as oidcServer from '../oidcserver.js'; +import * as passkeys from '../passkeys.js'; +import safe from 'safetydance'; +import * as tokens from '../tokens.js'; +import * as userDirectory from '../user-directory.js'; +import * as users from '../users.js'; +import * as settings from '../settings.js'; -exports = module.exports = { +export { canEditProfile, get, setDisplayName, @@ -25,19 +36,6 @@ exports = module.exports = { deletePasskey }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - oidcServer = require('../oidcserver.js'), - passkeys = require('../passkeys.js'), - safe = require('safetydance'), - tokens = require('../tokens.js'), - userDirectory = require('../user-directory.js'), - users = require('../users.js'), - settings = require('../settings.js'); - async function canEditProfile(req, res, next) { assert.strictEqual(typeof req.user, 'object'); diff --git a/src/routes/provision.js b/src/routes/provision.js index 313fc2cef..4fa6e5e64 100644 --- a/src/routes/provision.js +++ b/src/routes/provision.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as network from '../network.js'; +import * as provision from '../provision.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import * as system from '../system.js'; +import * as users from '../users.js'; -exports = module.exports = { +export { providerTokenAuth, verifyUnprovisioned, setup, @@ -11,18 +21,6 @@ exports = module.exports = { detectIP }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - network = require('../network.js'), - provision = require('../provision.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - system = require('../system.js'), - users = require('../users.js'); - async function verifyUnprovisioned(req, res, next) { const activated = await users.isActivated(); if (activated) return next(new HttpError(405, 'route unavailable post activation')); @@ -36,7 +34,6 @@ async function providerTokenAuth(req, res, next) { if (system.getProvider() === 'ami') { if (typeof req.body.providerToken !== 'string' || !req.body.providerToken) return next(new HttpError(400, 'providerToken must be a non empty string')); - // IMDSv2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-options.html // https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/ const imdsIp = req.body.ipv4Config?.provider === 'noop' ? '[fd00:ec2::254]' : '169.254.169.254'; // use ipv4config carefully, it's not validated yet at this point diff --git a/src/routes/reverseproxy.js b/src/routes/reverseproxy.js index 74f8ccec0..acef8feb1 100644 --- a/src/routes/reverseproxy.js +++ b/src/routes/reverseproxy.js @@ -1,20 +1,18 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as reverseProxy from '../reverseproxy.js'; +import safe from 'safetydance'; -exports = module.exports = { +export { getTrustedIps, setTrustedIps, renewCerts, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - reverseProxy = require('../reverseproxy.js'), - safe = require('safetydance'); - async function getTrustedIps(req, res, next) { const [error, trustedIps] = await safe(reverseProxy.getTrustedIps()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/services.js b/src/routes/services.js index 9e365e373..e81d21377 100644 --- a/src/routes/services.js +++ b/src/routes/services.js @@ -1,6 +1,14 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as metrics from '../metrics.js'; +import * as platform from '../platform.js'; +import safe from 'safetydance'; +import services from '../services.js'; -exports = module.exports = { +export { list, get, configure, @@ -12,16 +20,6 @@ exports = module.exports = { getPlatformStatus }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - metrics = require('../metrics.js'), - platform = require('../platform.js'), - safe = require('safetydance'), - services = require('../services.js'); - async function list(req, res, next) { const [error, result] = await safe(services.listServices()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/system.js b/src/routes/system.js index af4226160..aa02920f9 100644 --- a/src/routes/system.js +++ b/src/routes/system.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import * as metrics from '../metrics.js'; +import safe from 'safetydance'; +import * as system from '../system.js'; -exports = module.exports = { +export { reboot, getInfo, getMemory, @@ -14,14 +20,6 @@ exports = module.exports = { getCpus, }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - metrics = require('../metrics.js'), - safe = require('safetydance'), - system = require('../system.js'); - async function reboot(req, res, next) { // Finish the request, to let the appstore know we triggered the reboot next(new HttpSuccess(202, {})); diff --git a/src/routes/tasks.js b/src/routes/tasks.js index 55ab36a39..2be50e5d3 100644 --- a/src/routes/tasks.js +++ b/src/routes/tasks.js @@ -1,6 +1,11 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import tasks from '../tasks.js'; -exports = module.exports = { +export { load, get, list, @@ -11,13 +16,6 @@ exports = module.exports = { getLogStream }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'), - tasks = require('../tasks.js'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.taskId, 'string'); diff --git a/src/routes/test/api-test.js b/src/routes/test/api-test.js index 64f6d3407..3c1c67427 100644 --- a/src/routes/test/api-test.js +++ b/src/routes/test/api-test.js @@ -1,17 +1,16 @@ /* jslint node:true */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; +import timers from 'timers/promises'; +import * as tokens from '../../tokens.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'), - timers = require('timers/promises'), - tokens = require('../../tokens.js'); - describe('API', function () { const { setup, cleanup, serverUrl, owner, user } = common; @@ -58,7 +57,6 @@ describe('API', function () { expect(response.status).to.equal(401); }); - it('can get userInfo with token in auth header', async function () { const response = await superagent.get(`${serverUrl}/api/v1/users/${user.id}`) .set('Authorization', 'Bearer ' + owner.token); diff --git a/src/routes/test/applinks-test.js b/src/routes/test/applinks-test.js index 385210d19..74c61585b 100644 --- a/src/routes/test/applinks-test.js +++ b/src/routes/test/applinks-test.js @@ -1,15 +1,14 @@ -'use strict'; - /* global it:false */ + +import * as applinks from '../../applinks.js'; +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const applinks = require('../../applinks.js'), - common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('AppLinks API', function () { const { setup, cleanup, serverUrl, owner } = common; diff --git a/src/routes/test/apppasswords-test.js b/src/routes/test/apppasswords-test.js index 95ab2b48c..3241defff 100644 --- a/src/routes/test/apppasswords-test.js +++ b/src/routes/test/apppasswords-test.js @@ -1,15 +1,14 @@ /* jslint node:true */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('App Passwords', function () { const { setup, cleanup, serverUrl, user } = common; diff --git a/src/routes/test/apps-test.js b/src/routes/test/apps-test.js index 490f416cc..00d3c5354 100644 --- a/src/routes/test/apps-test.js +++ b/src/routes/test/apps-test.js @@ -1,33 +1,32 @@ -'use strict'; - /* global it:false */ + +import apps from '../../apps.js'; +import async from 'async'; +import child_process from 'node:child_process'; +import constants from '../../constants.js'; +import crypto from 'node:crypto'; +import * as database from '../../database.js'; +import Docker from 'dockerode'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import hat from '../../hat.js'; +import http from 'node:http'; +import * as ldapServer from '../../ldapserver.js'; +import net from 'node:net'; +import nock from 'nock'; +import path from 'node:path'; +import paths from '../../paths.js'; +import * as platform from '../../platform.js'; +import safe from 'safetydance'; +import * as server from '../../server.js'; +import * as settings from '../../settings.js'; +import superagent from '@cloudron/superagent'; +import * as tokens from '../../tokens.js'; +import url from 'node:url'; + /* global describe:false */ /* global before:false */ -const apps = require('../../apps.js'), - async = require('async'), - child_process = require('node:child_process'), - constants = require('../../constants.js'), - crypto = require('node:crypto'), - database = require('../../database.js'), - Docker = require('dockerode'), - expect = require('expect.js'), - fs = require('node:fs'), - hat = require('../../hat.js'), - http = require('node:http'), - ldapServer = require('../../ldapserver.js'), - net = require('node:net'), - nock = require('nock'), - path = require('node:path'), - paths = require('../../paths.js'), - platform = require('../../platform.js'), - safe = require('safetydance'), - server = require('../../server.js'), - settings = require('../../settings.js'), - superagent = require('@cloudron/superagent'), - tokens = require('../../tokens.js'), - url = require('node:url'); - const SERVER_URL = 'http://localhost:' + constants.PORT; const docker = new Docker({ socketPath: '/var/run/docker.sock' }); @@ -52,7 +51,7 @@ let APP_ID; const APP_SUBDOMAIN = 'appssubdomain'; const APP_SUBDOMAIN_NEW = 'appssubdomainnew'; -const APP_MANIFEST = {}; // JSON.parse(fs.readFileSync(__dirname + '/../../../../test-app/CloudronManifest.json', 'utf8')); +const APP_MANIFEST = {}; // JSON.parse(fs.readFileSync(import.meta.dirname + '/../../../../test-app/CloudronManifest.json', 'utf8')); APP_MANIFEST.dockerImage = TEST_IMAGE; const USERNAME = 'superadmin'; @@ -189,7 +188,7 @@ function startBox(done) { function (callback) { appstoreIconServer .get('/api/v1/apps/' + APP_STORE_ID + '/versions/' + APP_MANIFEST.version + '/icon') - .replyWithFile(200, path.resolve(__dirname, '../../../assets/avatar.png')); + .replyWithFile(200, path.resolve(import.meta.dirname, '../../../assets/avatar.png')); const port = parseInt(url.parse(settings.apiServerOrigin()).port, 10); http.createServer(appstoreIconServer.handler).listen(port, callback); @@ -403,7 +402,7 @@ xdescribe('App API', function () { const res = await superagent.post(SERVER_URL + '/api/v1/apps') .query({ access_token: token }) - .send({ appStoreId: APP_STORE_ID, subdomain: APP_SUBDOMAIN, domain: DOMAIN_0.domain, portBindings: { ECHO_SERVER_PORT: 7171 }, accessRestriction: { users: [ 'someuser' ], groups: [] } }) + .send({ appStoreId: APP_STORE_ID, subdomain: APP_SUBDOMAIN, domain: DOMAIN_0.domain, portBindings: { ECHO_SERVER_PORT: 7171 }, accessRestriction: { users: [ 'someuser' ], groups: [] } }); expect(res.status).to.equal(202); expect(res.body.id).to.be.a('string'); @@ -524,7 +523,7 @@ xdescribe('App API', function () { it('volume created', function (done) { expect(fs.existsSync(paths.APPS_DATA_DIR + '/' + APP_ID)); - let volume = docker.getVolume(APP_ID + '-localstorage'); + const volume = docker.getVolume(APP_ID + '-localstorage'); volume.inspect(function (error, volume) { expect(error).to.be(null); expect(volume.Labels.appId).to.eql(APP_ID); @@ -1355,7 +1354,7 @@ xdescribe('App API', function () { }); xit('can set data dir', function (done) { - let dataDir = path.join(paths.baseDir(), 'apps-test-datadir-' + crypto.randomBytes(4).readUInt32LE(0)); + const dataDir = path.join(paths.baseDir(), 'apps-test-datadir-' + crypto.randomBytes(4).readUInt32LE(0)); fs.mkdirSync(dataDir); superagent.post(SERVER_URL + '/api/v1/apps/' + APP_ID + '/configure/data_dir') diff --git a/src/routes/test/appstore-test.js b/src/routes/test/appstore-test.js index 4a432f23b..93584c1ec 100644 --- a/src/routes/test/appstore-test.js +++ b/src/routes/test/appstore-test.js @@ -1,18 +1,17 @@ /* global it:false */ + +import * as appstore from '../../appstore.js'; +import common from './common.js'; +import constants from '../../constants.js'; +import expect from 'expect.js'; +import nock from 'nock'; +import * as settings from '../../settings.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const appstore = require('../../appstore.js'), - common = require('./common.js'), - constants = require('../../constants.js'), - expect = require('expect.js'), - nock = require('nock'), - settings = require('../../settings.js'), - superagent = require('@cloudron/superagent'); - const { setup, cleanup, serverUrl, owner, appstoreToken } = common; describe('Appstore Apps API', function () { diff --git a/src/routes/test/archives-test.js b/src/routes/test/archives-test.js index 26fb1b928..4f1566dc4 100644 --- a/src/routes/test/archives-test.js +++ b/src/routes/test/archives-test.js @@ -1,16 +1,15 @@ /* global it:false */ + +import * as archives from '../../archives.js'; +import backups from '../../backups.js'; +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const archives = require('../../archives.js'), - backups = require('../../backups.js'), - common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('Archives API', function () { const { setup, cleanup, serverUrl, owner, auditSource, getDefaultBackupSite } = common; diff --git a/src/routes/test/backups-test.js b/src/routes/test/backups-test.js index db5fc7242..1cd558128 100644 --- a/src/routes/test/backups-test.js +++ b/src/routes/test/backups-test.js @@ -1,10 +1,8 @@ /* global it, describe, before, after */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; describe('Backups API', function () { const { setup, cleanup, waitForTask, serverUrl, owner, admin, getDefaultBackupSite } = common; @@ -40,7 +38,6 @@ describe('Backups API', function () { expect(response.body.backups.length).to.be(1); // only box backups are listed }); - it('cannot get random id', async function () { const response = await superagent.get(`${serverUrl}/api/v1/backups/bad_id`) .query({ access_token: owner.token }) diff --git a/src/routes/test/backupsites-test.js b/src/routes/test/backupsites-test.js index c8739c7e4..5b90e1246 100644 --- a/src/routes/test/backupsites-test.js +++ b/src/routes/test/backupsites-test.js @@ -1,11 +1,9 @@ /* global it, describe, before, after */ -'use strict'; - -const backupSites = require('../../backupsites.js'), - common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); +import * as backupSites from '../../backupsites.js'; +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; describe('Backups API', function () { const { setup, cleanup, waitForTask, serverUrl, owner, admin, getDefaultBackupSite } = common; diff --git a/src/routes/test/branding-test.js b/src/routes/test/branding-test.js index 660c582f0..895b60e92 100644 --- a/src/routes/test/branding-test.js +++ b/src/routes/test/branding-test.js @@ -1,17 +1,16 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import constants from '../../constants.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import paths from '../../paths.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - constants = require('../../constants.js'), - expect = require('expect.js'), - fs = require('node:fs'), - paths = require('../../paths.js'), - superagent = require('@cloudron/superagent'); - describe('Branding API', function () { const { setup, cleanup, serverUrl, owner } = common; diff --git a/src/routes/test/cloudron-test.js b/src/routes/test/cloudron-test.js index 48f274240..4058de47f 100644 --- a/src/routes/test/cloudron-test.js +++ b/src/routes/test/cloudron-test.js @@ -1,16 +1,15 @@ -'use strict'; - /* global it:false */ + +import constants from '../../constants.js'; +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; +import url from 'node:url'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const constants = require('../../constants.js'), - common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'), - url = require('node:url'); - describe('Cloudron', function () { const { setup, cleanup, serverUrl, owner, user, dashboardFqdn } = common; diff --git a/src/routes/test/common.js b/src/routes/test/common.js index 2e34be50c..52c47a3c7 100644 --- a/src/routes/test/common.js +++ b/src/routes/test/common.js @@ -1,22 +1,22 @@ -'use strict'; +import apps from '../../apps.js'; +import * as appstore from '../../appstore.js'; +import * as backupSites from '../../backupsites.js'; +import debugModule from 'debug'; +import constants from '../../constants.js'; +import * as database from '../../database.js'; +import expect from 'expect.js'; +import * as mailer from '../../mailer.js'; +import nock from 'nock'; +import oidcClients from '../../oidcclients.js'; +import * as oidcServer from '../../oidcserver.js'; +import * as server from '../../server.js'; +import * as settings from '../../settings.js'; +import superagent from '@cloudron/superagent'; +import tasks from '../../tasks.js'; +import timers from 'timers/promises'; +import * as tokens from '../../tokens.js'; -const apps = require('../../apps.js'), - appstore = require('../../appstore.js'), - backupSites = require('../../backupsites.js'), - debug = require('debug')('box:test/common'), - constants = require('../../constants.js'), - database = require('../../database.js'), - expect = require('expect.js'), - mailer = require('../../mailer.js'), - nock = require('nock'), - oidcClients = require('../../oidcclients.js'), - oidcServer = require('../../oidcserver.js'), - server = require('../../server.js'), - settings = require('../../settings.js'), - superagent = require('@cloudron/superagent'), - tasks = require('../../tasks.js'), - timers = require('timers/promises'), - tokens = require('../../tokens.js'); +const debug = debugModule('box:test/common'); const manifest = { 'id': 'io.cloudron.test', @@ -46,7 +46,57 @@ const manifest = { } }; -exports = module.exports = { +const mockApiServerOrigin = 'http://localhost:6060'; +const dashboardDomain = 'test.example.com'; +const appstoreToken = 'toktok'; + +const owner = { + id: null, + username: 'superadmin', + password: 'Foobar?1337', + email: 'superadmin@cloudron.local', + displayName: 'Super Admin', + token: null +}; + +const admin = { + id: null, + username: 'administrator', + password: 'Foobar?1339', + email: 'admin@cloudron.local', + token: null +}; + +const user = { + id: null, + username: 'user', + password: 'Foobar?1338', + email: 'user@cloudron.local', + token: null +}; + +const app = { + id: 'appid', + appStoreId: 'appStoreId', + installationState: apps.ISTATE_PENDING_INSTALL, + runState: 'running', + subdomain: 'app', + domain: 'test.example.com', + fqdn: 'app.test.example.com', + manifest, + containerId: 'someid', + portBindings: {}, + accessRestriction: null, + memoryLimit: 0, + mailboxDomain: 'test.example.com', + secondaryDomains: [], + redirectDomains: [], + aliasDomains: [] +}; + +const serverUrl = `http://localhost:${constants.PORT}`; + +export default { setup, setupServer, cleanup, @@ -54,61 +104,17 @@ exports = module.exports = { checkMails, waitForTask, waitForAsyncTask, - - owner: { - id: null, - username: 'superadmin', - password: 'Foobar?1337', - email: 'superadmin@cloudron.local', - displayName: 'Super Admin', - token: null - }, - - admin: { - id: null, - username: 'administrator', - password: 'Foobar?1339', - email: 'admin@cloudron.local', - token: null - }, - - user: { - id: null, - username: 'user', - password: 'Foobar?1338', - email: 'user@cloudron.local', - token: null - }, - - app: { - id: 'appid', - appStoreId: 'appStoreId', - installationState: apps.ISTATE_PENDING_INSTALL, - runState: 'running', - subdomain: 'app', - domain: 'test.example.com', - fqdn: 'app.test.example.com', - manifest, - containerId: 'someid', - portBindings: {}, - accessRestriction: null, - memoryLimit: 0, - mailboxDomain: 'test.example.com', - secondaryDomains: [], - redirectDomains: [], - aliasDomains: [] - }, - + owner, + admin, + user, + app, getDefaultBackupSite, - - mockApiServerOrigin: 'http://localhost:6060', - dashboardDomain: 'test.example.com', + mockApiServerOrigin, + dashboardDomain, dashboardFqdn: 'my.test.example.com', - appstoreToken: 'toktok', + appstoreToken, mailFqdn: 'my.test.example.com', - - serverUrl: `http://localhost:${constants.PORT}`, - + serverUrl, auditSource: { ip: '5.6.7.8' } }; @@ -116,7 +122,7 @@ async function setupServer() { debug('Setting up server'); await database.initialize(); await database._clear(); - await appstore._setApiServerOrigin(exports.mockApiServerOrigin); + await appstore._setApiServerOrigin(mockApiServerOrigin); await oidcServer.stop(); await server.start(); debug('Set up server complete'); @@ -125,13 +131,11 @@ async function setupServer() { async function setup() { debug('Setting up'); - const owner = exports.owner, serverUrl = exports.serverUrl, user = exports.user, admin = exports.admin; - await setupServer(); // setup let response = await superagent.post(`${serverUrl}/api/v1/provision/setup`) - .send({ domainConfig: { provider: 'noop', domain: exports.dashboardDomain, config: {}, tlsConfig: { provider: 'fallback' } } }); + .send({ domainConfig: { provider: 'noop', domain: dashboardDomain, config: {}, tlsConfig: { provider: 'fallback' } } }); expect(response.status).to.eql(200); await timers.setTimeout(2000); @@ -178,9 +182,9 @@ async function setup() { user.token = token2.accessToken; // create app object - await apps.add(exports.app.id, exports.app.appStoreId, '', exports.app.manifest, exports.app.subdomain, exports.app.domain, exports.app.portBindings, exports.app); + await apps.add(app.id, app.appStoreId, '', app.manifest, app.subdomain, app.domain, app.portBindings, app); - await settings._set(settings.APPSTORE_API_TOKEN_KEY, exports.appstoreToken); // appstore token + await settings._set(settings.APPSTORE_API_TOKEN_KEY, appstoreToken); // appstore token debug('Setup complete'); } @@ -194,7 +198,7 @@ async function cleanup() { } function clearMailQueue() { - mailer._mailQueue = []; + mailer.clearMailQueue(); } async function checkMails(number) { diff --git a/src/routes/test/directoryserver-test.js b/src/routes/test/directoryserver-test.js index 371d13106..2c1a7710f 100644 --- a/src/routes/test/directoryserver-test.js +++ b/src/routes/test/directoryserver-test.js @@ -1,14 +1,13 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('Directory Server API', function () { const { setup, cleanup, serverUrl, owner } = common; @@ -17,7 +16,7 @@ describe('Directory Server API', function () { describe('directory_server config', function () { // keep in sync with defaults in settings.js - let defaultConfig = { + const defaultConfig = { enabled: false, secret: '', allowlist: '' @@ -32,7 +31,7 @@ describe('Directory Server API', function () { }); it('cannot set directory_server config without enabled boolean', async function () { - let tmp = JSON.parse(JSON.stringify(defaultConfig)); + const tmp = JSON.parse(JSON.stringify(defaultConfig)); delete tmp.enabled; const response = await superagent.post(`${serverUrl}/api/v1/directory_server/config`) @@ -44,7 +43,7 @@ describe('Directory Server API', function () { }); it('cannot set directory_server config without secret', async function () { - let tmp = JSON.parse(JSON.stringify(defaultConfig)); + const tmp = JSON.parse(JSON.stringify(defaultConfig)); tmp.enabled = true; delete tmp.secret; @@ -57,7 +56,7 @@ describe('Directory Server API', function () { }); it('cannot enable directory_server config with empty secret', async function () { - let tmp = JSON.parse(JSON.stringify(defaultConfig)); + const tmp = JSON.parse(JSON.stringify(defaultConfig)); tmp.enabled = true; const response = await superagent.post(`${serverUrl}/api/v1/directory_server/config`) @@ -69,7 +68,7 @@ describe('Directory Server API', function () { }); it('cannot enable directory_server config with empty allowlist', async function () { - let tmp = JSON.parse(JSON.stringify(defaultConfig)); + const tmp = JSON.parse(JSON.stringify(defaultConfig)); tmp.enabled = true; tmp.secret = 'ldapsecret'; @@ -82,7 +81,7 @@ describe('Directory Server API', function () { }); it('can enable directory_server config', async function () { - let tmp = JSON.parse(JSON.stringify(defaultConfig)); + const tmp = JSON.parse(JSON.stringify(defaultConfig)); tmp.enabled = true; tmp.secret = 'ldapsecret'; tmp.allowlist = '1.2.3.4'; @@ -95,7 +94,7 @@ describe('Directory Server API', function () { }); it('can get directory_server config', async function () { - let tmp = JSON.parse(JSON.stringify(defaultConfig)); + const tmp = JSON.parse(JSON.stringify(defaultConfig)); tmp.enabled = true; const response = await superagent.get(`${serverUrl}/api/v1/directory_server/config`) @@ -107,7 +106,7 @@ describe('Directory Server API', function () { // keep this last. this ensures directory server is stopped and the tests can exit it('can disable directory_server config', async function () { - let tmp = JSON.parse(JSON.stringify(defaultConfig)); + const tmp = JSON.parse(JSON.stringify(defaultConfig)); tmp.enabled = false; tmp.secret = 'ldapsecret'; diff --git a/src/routes/test/dockerregistries-test.js b/src/routes/test/dockerregistries-test.js index 1c1009b29..227332066 100644 --- a/src/routes/test/dockerregistries-test.js +++ b/src/routes/test/dockerregistries-test.js @@ -1,16 +1,15 @@ /* jslint node:true */ + +import common from './common.js'; +import constants from '../../constants.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - constants = require('../../constants.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('Docker Registries', function () { const { setup, cleanup, serverUrl, owner } = common; diff --git a/src/routes/test/domains-test.js b/src/routes/test/domains-test.js index 9e778407e..7c0bd2a5b 100644 --- a/src/routes/test/domains-test.js +++ b/src/routes/test/domains-test.js @@ -1,18 +1,17 @@ -'use strict'; - /* global it:false */ + +import child_process from 'node:child_process'; +import common from './common.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import path from 'node:path'; +import paths from '../../paths.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const child_process = require('node:child_process'), - common = require('./common.js'), - expect = require('expect.js'), - fs = require('node:fs'), - path = require('node:path'), - paths = require('../../paths.js'), - superagent = require('@cloudron/superagent'); - const DOMAIN_0 = { domain: 'domain0.com', zoneName: 'domain0.com', diff --git a/src/routes/test/eventlog-test.js b/src/routes/test/eventlog-test.js index be5c994b1..07c05edf3 100644 --- a/src/routes/test/eventlog-test.js +++ b/src/routes/test/eventlog-test.js @@ -1,17 +1,16 @@ /* jslint node:true */ + +import async from 'async'; +import common from './common.js'; +import eventlog from '../../eventlog.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const async = require('async'), - common = require('./common.js'), - eventlog = require('../../eventlog.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('Eventlog API', function () { const { setup, cleanup, serverUrl, owner, user } = common; diff --git a/src/routes/test/externalldap-test.js b/src/routes/test/externalldap-test.js index c7d422744..b472aef37 100644 --- a/src/routes/test/externalldap-test.js +++ b/src/routes/test/externalldap-test.js @@ -1,14 +1,13 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('External LDAP API', function () { const { setup, cleanup, serverUrl, owner } = common; @@ -17,7 +16,7 @@ describe('External LDAP API', function () { describe('external_ldap', function () { // keep in sync with defaults in settings.js - let defaultConfig = { provider: 'noop', autoCreate: false }; + const defaultConfig = { provider: 'noop', autoCreate: false }; it('can get external_ldap (default)', async function () { const response = await superagent.get(`${serverUrl}/api/v1/external_ldap/config`) diff --git a/src/routes/test/groups-test.js b/src/routes/test/groups-test.js index c0bcdf1fb..cc9e7f16b 100644 --- a/src/routes/test/groups-test.js +++ b/src/routes/test/groups-test.js @@ -1,15 +1,14 @@ /* jslint node:true */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - const GROUP_NAME = 'externals'; let group0Object, group1Object; diff --git a/src/routes/test/mail-test.js b/src/routes/test/mail-test.js index 4f7adf0c7..445a2e9bf 100644 --- a/src/routes/test/mail-test.js +++ b/src/routes/test/mail-test.js @@ -1,16 +1,16 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import * as mail from '../../mail.js'; +import superagent from '@cloudron/superagent'; +import * as _ from '../../underscore.js'; +import * as dig from '../../dig.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - mail = require('../../mail.js'), - superagent = require('@cloudron/superagent'), - _ = require('../../underscore.js'); - describe('Mail API', function () { const { setup, cleanup, serverUrl, owner, dashboardDomain, dashboardFqdn, mailFqdn } = common; @@ -48,16 +48,13 @@ describe('Mail API', function () { }); describe('status', function () { - let resolve = null; let dnsAnswerQueue = []; let dkimDomain, spfDomain, mxDomain, dmarcDomain; before(async function () { - const dig = require('../../dig.js'); // replace dns resolveTxt() - resolve = dig.resolve; - dig.resolve = async function (hostname, type/*, options*/) { + dig._setMockResolve(async function (hostname, type/*, options*/) { expect(hostname).to.be.a('string'); if (!dnsAnswerQueue[hostname] || !(type in dnsAnswerQueue[hostname])) throw new Error('no mock answer'); @@ -65,7 +62,7 @@ describe('Mail API', function () { if (dnsAnswerQueue[hostname][type] === null) throw new Error({ code: 'ENODATA'} ); return dnsAnswerQueue[hostname][type]; - }; + }); dkimDomain = `cloudron._domainkey.${dashboardDomain}`; // no suffix for provisioned domains spfDomain = dashboardDomain; @@ -80,9 +77,8 @@ describe('Mail API', function () { }); after(function (done) { - const dig = require('../../dig.js'); - dig.resolve = resolve; + dig._setMockResolve(null); done(); }); diff --git a/src/routes/test/network-test.js b/src/routes/test/network-test.js index 15c8547c0..b2fb074b0 100644 --- a/src/routes/test/network-test.js +++ b/src/routes/test/network-test.js @@ -1,14 +1,13 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('Network API', function () { const { setup, cleanup, serverUrl, owner } = common; diff --git a/src/routes/test/notifications-test.js b/src/routes/test/notifications-test.js index 73f1255cf..05209dbc2 100644 --- a/src/routes/test/notifications-test.js +++ b/src/routes/test/notifications-test.js @@ -1,15 +1,14 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import * as notifications from '../../notifications.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - notifications = require('../../notifications.js'), - superagent = require('@cloudron/superagent'); - describe('Notifications API', function () { const { setup, cleanup, serverUrl, owner } = common; diff --git a/src/routes/test/oidcclients-test.js b/src/routes/test/oidcclients-test.js index 31b8b05be..efc987724 100644 --- a/src/routes/test/oidcclients-test.js +++ b/src/routes/test/oidcclients-test.js @@ -1,15 +1,14 @@ /* jslint node:true */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - const CLIENT_0 = { id: 'client0', name: 'test client 0', diff --git a/src/routes/test/profile-test.js b/src/routes/test/profile-test.js index 90c6eaf6c..4fa723bce 100644 --- a/src/routes/test/profile-test.js +++ b/src/routes/test/profile-test.js @@ -1,17 +1,19 @@ /* jslint node:true */ + +import common from './common.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import speakeasy from 'speakeasy'; +import superagent from '@cloudron/superagent'; +import * as tokens from '../../tokens.js'; + +const customAvatarSize = fs.readFileSync('./logo.png').length; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - speakeasy = require('speakeasy'), - superagent = require('@cloudron/superagent'), - tokens = require('../../tokens.js'); - describe('Profile API', function () { const { setup, cleanup, serverUrl, owner, user } = common; @@ -316,7 +318,7 @@ describe('Profile API', function () { .query({ access_token: user.token }) .attach('avatar', './logo.png'); - customAvatarSize = require('node:fs').readFileSync('./logo.png').length; + customAvatarSize = fs.readFileSync('./logo.png').length; expect(response.status).to.be(204); }); @@ -363,7 +365,6 @@ describe('Profile API', function () { .query({ access_token: user.token }) .ok(() => true); - const customAvatarSize = require('node:fs').readFileSync('./logo.png').length; expect(parseInt(response2.headers['content-length'])).to.equal(customAvatarSize); expect(response2.status).to.equal(200); }); diff --git a/src/routes/test/provision-test.js b/src/routes/test/provision-test.js index 1174681bb..92d44c43a 100644 --- a/src/routes/test/provision-test.js +++ b/src/routes/test/provision-test.js @@ -1,18 +1,16 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import * as appstore from '../../appstore.js'; +import expect from 'expect.js'; +import nock from 'nock'; +import superagent from '@cloudron/superagent'; +import timers from 'timers/promises'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'); - -const appstore = require('../../appstore.js'), - expect = require('expect.js'), - nock = require('nock'), - superagent = require('@cloudron/superagent'), - timers = require('timers/promises'); - const DOMAIN = 'example-server-test.com'; describe('Provision', function () { diff --git a/src/routes/test/system-test.js b/src/routes/test/system-test.js index 4617f0309..c5f007660 100644 --- a/src/routes/test/system-test.js +++ b/src/routes/test/system-test.js @@ -1,22 +1,21 @@ -'use strict'; - /* global it:false */ + +import constants from '../../constants.js'; +import common from './common.js'; +import { EventSource } from 'eventsource'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import http from 'node:http'; +import nock from 'nock'; +import os from 'node:os'; +import paths from '../../paths.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const constants = require('../../constants.js'), - common = require('./common.js'), - { EventSource } = require('eventsource'), - expect = require('expect.js'), - fs = require('node:fs'), - http = require('node:http'), - nock = require('nock'), - os = require('node:os'), - paths = require('../../paths.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - describe('System', function () { const { setup, cleanup, serverUrl, owner, user, waitForAsyncTask } = common; diff --git a/src/routes/test/tasks-test.js b/src/routes/test/tasks-test.js index 88f32ee43..784be485f 100644 --- a/src/routes/test/tasks-test.js +++ b/src/routes/test/tasks-test.js @@ -1,16 +1,15 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; +import tasks from '../../tasks.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'), - tasks = require('../../tasks.js'); - describe('Tasks API', function () { const { setup, cleanup, serverUrl, owner } = common; @@ -56,7 +55,6 @@ describe('Tasks API', function () { if (response.status !== 409) throw Error('Expecting 409'); }); - it('can stop task', async function () { const taskId = await tasks.add(tasks._TASK_SLEEP, [ 10000 ]); diff --git a/src/routes/test/tokens-test.js b/src/routes/test/tokens-test.js index 291230e5d..5ea5c6967 100644 --- a/src/routes/test/tokens-test.js +++ b/src/routes/test/tokens-test.js @@ -1,14 +1,13 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('Tokens API', function () { const { setup, cleanup, serverUrl, owner } = common; diff --git a/src/routes/test/updater-test.js b/src/routes/test/updater-test.js index cba0a4377..ebea5e8fc 100644 --- a/src/routes/test/updater-test.js +++ b/src/routes/test/updater-test.js @@ -1,15 +1,14 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import constants from '../../constants.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - constants = require('../../constants.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('Updater API', function () { const { setup, cleanup, serverUrl, owner } = common; diff --git a/src/routes/test/user-directory-test.js b/src/routes/test/user-directory-test.js index 9065e63c5..a52a31e1f 100644 --- a/src/routes/test/user-directory-test.js +++ b/src/routes/test/user-directory-test.js @@ -1,15 +1,14 @@ /* jslint node:true */ + +import common from './common.js'; +import expect from 'expect.js'; +import superagent from '@cloudron/superagent'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'); - describe('User Directory API', function () { const { setup, cleanup, serverUrl, owner, user } = common; diff --git a/src/routes/test/users-test.js b/src/routes/test/users-test.js index 5890f90a4..9eecda346 100644 --- a/src/routes/test/users-test.js +++ b/src/routes/test/users-test.js @@ -1,15 +1,15 @@ /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import superagent from '@cloudron/superagent'; +import * as users from '../../users.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - superagent = require('@cloudron/superagent'), - users = require('../../users.js'); - describe('Users API', function () { const { setup, cleanup, serverUrl, owner, user, dashboardDomain } = common; @@ -389,7 +389,7 @@ describe('Users API', function () { .query({ access_token: owner.token }) .attach('avatar', './logo.png'); - customAvatarSize = require('node:fs').readFileSync('./logo.png').length; + customAvatarSize = fs.readFileSync('./logo.png').length; expect(response.status).to.equal(204); diff --git a/src/routes/test/volumes-test.js b/src/routes/test/volumes-test.js index c8361c3cb..f8f6644d5 100644 --- a/src/routes/test/volumes-test.js +++ b/src/routes/test/volumes-test.js @@ -1,15 +1,14 @@ -'use strict'; - /* global it:false */ + +import common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ /* global before:false */ /* global after:false */ -const common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - describe('Volumes API', function () { const { setup, cleanup, serverUrl, owner } = common; @@ -69,7 +68,7 @@ describe('Volumes API', function () { }); it('cannot update volume', async function () { - let [error, response] = await safe(superagent.post(`${serverUrl}/api/v1/volumes/${volumeId}`) + const [error, response] = await safe(superagent.post(`${serverUrl}/api/v1/volumes/${volumeId}`) .query({ access_token: owner.token }) .send({ mountOptions: { hostPath: '/media/cloudron-test-music-2' }}) .ok(() => true)); diff --git a/src/routes/tokens.js b/src/routes/tokens.js index 9ece731ef..5ba09a185 100644 --- a/src/routes/tokens.js +++ b/src/routes/tokens.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import oidcClients from '../oidcclients.js'; +import safe from 'safetydance'; +import * as tokens from '../tokens.js'; -exports = module.exports = { +export { verifyOwnership, list, get, @@ -8,14 +14,6 @@ exports = module.exports = { del }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - oidcClients = require('../oidcclients.js'), - safe = require('safetydance'), - tokens = require('../tokens.js'); - async function verifyOwnership(req, res, next) { assert.strictEqual(typeof req.user, 'object'); assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/updater.js b/src/routes/updater.js index ae0f942cb..61098b7db 100644 --- a/src/routes/updater.js +++ b/src/routes/updater.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import * as updater from '../updater.js'; -exports = module.exports = { +export { getAutoupdatePattern, setAutoupdatePattern, @@ -10,14 +16,6 @@ exports = module.exports = { updateBox, }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'), - updater = require('../updater.js'); - async function getAutoupdatePattern(req, res, next) { const [error, pattern] = await safe(updater.getAutoupdatePattern()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/user-directory.js b/src/routes/user-directory.js index f1bf37ed4..a85813614 100644 --- a/src/routes/user-directory.js +++ b/src/routes/user-directory.js @@ -1,18 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import * as userDirectory from '../user-directory.js'; -exports = module.exports = { +export { getProfileConfig, setProfileConfig }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'), - userDirectory = require('../user-directory.js'); - async function getProfileConfig(req, res, next) { const [error, directoryConfig] = await safe(userDirectory.getProfileConfig()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/routes/users.js b/src/routes/users.js index 5db6c36bf..057158c9b 100644 --- a/src/routes/users.js +++ b/src/routes/users.js @@ -1,6 +1,14 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as groups from '../groups.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import * as users from '../users.js'; +import * as _ from '../underscore.js'; -exports = module.exports = { +export { get, list, add, @@ -29,16 +37,6 @@ exports = module.exports = { load }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - groups = require('../groups.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'), - users = require('../users.js'), - _ = require('../underscore.js'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.userId, 'string'); diff --git a/src/routes/volumes.js b/src/routes/volumes.js index 4f9e14a1d..94dad6e00 100644 --- a/src/routes/volumes.js +++ b/src/routes/volumes.js @@ -1,6 +1,12 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as volumes from '../volumes.js'; +import { HttpError } from '@cloudron/connect-lastmile'; +import { HttpSuccess } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; -exports = module.exports = { +export { add, get, update, @@ -11,14 +17,6 @@ exports = module.exports = { getStatus }; -const assert = require('node:assert'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - volumes = require('../volumes.js'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess, - safe = require('safetydance'); - async function load(req, res, next) { assert.strictEqual(typeof req.params.id, 'string'); diff --git a/src/routes/wellknown.js b/src/routes/wellknown.js index 4af9fb4fa..780d39ffe 100644 --- a/src/routes/wellknown.js +++ b/src/routes/wellknown.js @@ -1,13 +1,11 @@ -'use strict'; +import { HttpError } from '@cloudron/connect-lastmile'; +import safe from 'safetydance'; +import * as wellknown from '../wellknown.js'; -exports = module.exports = { +export { get }; -const HttpError = require('@cloudron/connect-lastmile').HttpError, - safe = require('safetydance'), - wellknown = require('../wellknown.js'); - async function get(req, res, next) { const host = req.headers['host']; const location = req.params.location.join('/'); diff --git a/src/scheduler.js b/src/scheduler.js index 21c80abc2..cf0dd18a9 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -1,6 +1,17 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as cloudron from './cloudron.js'; +import constants from './constants.js'; +import { CronJob } from 'cron'; +import debugModule from 'debug'; +import * as docker from './docker.js'; +import safe from 'safetydance'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:scheduler'); + +export { sync, deleteJobs, @@ -8,17 +19,6 @@ exports = module.exports = { resumeAppJobs }; -const apps = require('./apps.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - cloudron = require('./cloudron.js'), - constants = require('./constants.js'), - { CronJob } = require('cron'), - debug = require('debug')('box:scheduler'), - docker = require('./docker.js'), - safe = require('safetydance'), - _ = require('./underscore.js'); - const gState = {}; // appId -> { containerId, schedulerConfig (manifest+crontab), cronjobs } const gSuspendedAppIds = new Set(); // suspended because some apptask is running diff --git a/src/scripts/backupupload.js b/src/scripts/backupupload.js index 54ad62223..0a23f4dba 100755 --- a/src/scripts/backupupload.js +++ b/src/scripts/backupupload.js @@ -1,16 +1,18 @@ #!/usr/bin/env node -'use strict'; - +// --check is used by run-tests to verify sudo access works. +// It must exit before loading the full module graph (which requires BOX_ENV). if (process.argv[2] === '--check') { console.log('OK'); process.exit(0); } -const backuptask = require('../backuptask.js'), - database = require('../database.js'), - debug = require('debug')('box:backupupload'), - safe = require('safetydance'); +const backuptask = await import('../backuptask.js'); +const database = await import('../database.js'); +const { default: debugModule } = await import('debug'); +const { default: safe } = await import('safetydance'); + +const debug = debugModule('box:backupupload'); // Main process starts here const remotePath = process.argv[2]; @@ -41,13 +43,11 @@ function throttledProgressCallback(msecs) { }; } -(async function main() { - await database.initialize(); +await database.initialize(); - const [uploadError, result] = await safe(backuptask.upload(remotePath, format, dataLayoutString, throttledProgressCallback(5000))); - debug('upload completed. error: %o', uploadError); +const [uploadError, result] = await safe(backuptask.upload(remotePath, format, dataLayoutString, throttledProgressCallback(5000))); +debug('upload completed. error: %o', uploadError); - process.send({ result, errorMessage: uploadError?.message }); +process.send({ result, errorMessage: uploadError?.message }); - process.exit(uploadError ? 50 : 0); -})(); +process.exit(uploadError ? 50 : 0); diff --git a/src/server.js b/src/server.js index 8faf82385..2118f46b7 100644 --- a/src/server.js +++ b/src/server.js @@ -1,26 +1,26 @@ -'use strict'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import express from 'express'; +import http from 'node:http'; +import { HttpError } from '@cloudron/connect-lastmile'; +import * as middleware from './middleware/index.js'; +import * as platform from './platform.js'; +import * as routes from './routes/index.js'; +import safe from 'safetydance'; +import * as users from './users.js'; +import util from 'node:util'; +import ws from 'ws'; -exports = module.exports = { +const debug = debugModule('box:server'); + +export { start, stop }; -const assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - constants = require('./constants.js'), - debug = require('debug')('box:server'), - eventlog = require('./eventlog.js'), - express = require('express'), - http = require('node:http'), - HttpError = require('@cloudron/connect-lastmile').HttpError, - middleware = require('./middleware'), - platform = require('./platform.js'), - routes = require('./routes/index.js'), - safe = require('safetydance'), - users = require('./users.js'), - util = require('node:util'), - ws = require('ws'); - let gHttpServer = null; function notFoundHandler(req, res, next) { diff --git a/src/services.js b/src/services.js index cf8fc6d3a..b2fc160ec 100644 --- a/src/services.js +++ b/src/services.js @@ -1,6 +1,45 @@ -'use strict'; +import * as addonConfigs from './addonconfigs.js'; +import apps from './apps.js'; +import assert from 'node:assert'; +import blobs from './blobs.js'; +import BoxError from './boxerror.js'; +import * as branding from './branding.js'; +import constants from './constants.js'; +import crypto from 'node:crypto'; +import * as dashboard from './dashboard.js'; +import debugModule from 'debug'; +import * as dig from './dig.js'; +import * as docker from './docker.js'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import hat from './hat.js'; +import http from 'http'; +import infra from './infra_version.js'; +import * as logs from './logs.js'; +import * as mail from './mail.js'; +import * as mailServer from './mailserver.js'; +import oidcClients from './oidcclients.js'; +import os from 'node:os'; +import path from 'node:path'; +import paths from './paths.js'; +import { pipeFileToRequest, pipeRequestToFile } from '@cloudron/pipework'; +import promiseRetry from './promise-retry.js'; +import safe from 'safetydance'; +import semver from 'semver'; +import * as settings from './settings.js'; +import * as sftp from './sftp.js'; +import shellModule from './shell.js'; +import superagent from '@cloudron/superagent'; -exports = module.exports = { +const debug = debugModule('box:services'); +const shell = shellModule('services'); + +const SERVICE_STATUS_STARTING = 'starting'; +const SERVICE_STATUS_ACTIVE = 'active'; +const SERVICE_STATUS_STOPPED = 'stopped'; +const SERVICE_STATUS_DISABLED = 'disabled'; + +export default { getServiceConfig, listServices, @@ -33,52 +72,19 @@ exports = module.exports = { // exported only for apptask.js to update immich pgvectors extension - can be removed later _postgreSqlNames: postgreSqlNames, - SERVICE_STATUS_STARTING: 'starting', // container up, waiting for healthcheck - SERVICE_STATUS_ACTIVE: 'active', - SERVICE_STATUS_STOPPED: 'stopped', - SERVICE_STATUS_DISABLED: 'disabled', // feature not supported + SERVICE_STATUS_STARTING, + SERVICE_STATUS_ACTIVE, + SERVICE_STATUS_STOPPED, + SERVICE_STATUS_DISABLED, }; -const addonConfigs = require('./addonconfigs.js'), - apps = require('./apps.js'), - assert = require('node:assert'), - blobs = require('./blobs.js'), - BoxError = require('./boxerror.js'), - branding = require('./branding.js'), - constants = require('./constants.js'), - crypto = require('node:crypto'), - dashboard = require('./dashboard.js'), - debug = require('debug')('box:services'), - dig = require('./dig.js'), - docker = require('./docker.js'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - hat = require('./hat.js'), - http = require('http'), - infra = require('./infra_version.js'), - logs = require('./logs.js'), - mail = require('./mail.js'), - mailServer = require('./mailserver.js'), - oidcClients = require('./oidcclients.js'), - os = require('node:os'), - path = require('node:path'), - paths = require('./paths.js'), - { pipeFileToRequest, pipeRequestToFile } = require('@cloudron/pipework'), - promiseRetry = require('./promise-retry.js'), - safe = require('safetydance'), - semver = require('semver'), - settings = require('./settings.js'), - sftp = require('./sftp.js'), - shell = require('./shell.js')('services'), - superagent = require('@cloudron/superagent'); - const NOOP = async function (/*app, options*/) {}; -const RMADDONDIR_CMD = path.join(__dirname, 'scripts/rmaddondir.sh'); -const RESTART_SERVICE_CMD = path.join(__dirname, 'scripts/restartservice.sh'); -const CLEARVOLUME_CMD = path.join(__dirname, 'scripts/clearvolume.sh'); -const RMVOLUME_CMD = path.join(__dirname, 'scripts/rmvolume.sh'); -const SETUPVOLUME_CMD = path.join(__dirname, 'scripts/setupvolume.sh'); -const MV_VOLUME_CMD = path.join(__dirname, 'scripts/mvvolume.sh'); +const RMADDONDIR_CMD = path.join(import.meta.dirname, 'scripts/rmaddondir.sh'); +const RESTART_SERVICE_CMD = path.join(import.meta.dirname, 'scripts/restartservice.sh'); +const CLEARVOLUME_CMD = path.join(import.meta.dirname, 'scripts/clearvolume.sh'); +const RMVOLUME_CMD = path.join(import.meta.dirname, 'scripts/rmvolume.sh'); +const SETUPVOLUME_CMD = path.join(import.meta.dirname, 'scripts/setupvolume.sh'); +const MV_VOLUME_CMD = path.join(import.meta.dirname, 'scripts/mvvolume.sh'); // setup can be called multiple times for the same app (configure crash restart) and existing data must not be lost // teardown is destructive. app data stored with the addon is lost @@ -214,69 +220,75 @@ const ADDONS = { } }; -// services are actual containers that are running. addons are the concepts requested by app -const SERVICES = { - turn: { - name: 'TURN', - status: statusTurn, - restart: docker.restartContainer.bind(null, 'turn'), - defaultMemoryLimit: 256 * 1024 * 1024 - }, - mail: { - name: 'Mail', - status: containerStatus.bind(null, 'mail'), - restart: mailServer.restart, - defaultMemoryLimit: mailServer.DEFAULT_MEMORY_LIMIT - }, - mongodb: { - name: 'MongoDB', - status: statusMongodb, - restart: restartMongodb, - defaultMemoryLimit: (1 + Math.round(os.totalmem()/(1024*1024*1024)/4)) * 256 * 1024 * 1024 - }, - mysql: { - name: 'MySQL', - status: containerStatus.bind(null, 'mysql'), - restart: docker.restartContainer.bind(null, 'mysql'), - defaultMemoryLimit: (1 + Math.round(os.totalmem()/(1024*1024*1024)/4)) * 256 * 1024 * 1024 - }, - postgresql: { - name: 'PostgreSQL', - status: containerStatus.bind(null, 'postgresql'), - restart: docker.restartContainer.bind(null, 'postgresql'), - defaultMemoryLimit: (1 + Math.round(os.totalmem()/(1024*1024*1024)/4)) * 256 * 1024 * 1024 - }, - docker: { - name: 'Docker', - status: statusDocker, - restart: restartDocker, - defaultMemoryLimit: 0 - }, - unbound: { - name: 'Unbound', - status: statusUnbound, - restart: restartUnbound, - defaultMemoryLimit: 0 - }, - sftp: { - name: 'Filemanager', - status: containerStatus.bind(null, 'sftp'), - restart: docker.restartContainer.bind(null, 'sftp'), - defaultMemoryLimit: sftp.DEFAULT_MEMORY_LIMIT - }, - graphite: { - name: 'Metrics', - status: statusGraphite, - restart: restartGraphite, - defaultMemoryLimit: 256 * 1024 * 1024 - }, - nginx: { - name: 'Nginx', - status: statusNginx, - restart: restartNginx, - defaultMemoryLimit: 0 - } -}; +// Lazily initialized to avoid circular dependency TDZ issues at module load time +// (docker, mailServer, sftp may not be fully initialized when this module first evaluates) +let _services; +function SERVICES() { + if (_services) return _services; + _services = { + turn: { + name: 'TURN', + status: statusTurn, + restart: docker.restartContainer.bind(null, 'turn'), + defaultMemoryLimit: 256 * 1024 * 1024 + }, + mail: { + name: 'Mail', + status: containerStatus.bind(null, 'mail'), + restart: mailServer.restart, + defaultMemoryLimit: mailServer.DEFAULT_MEMORY_LIMIT + }, + mongodb: { + name: 'MongoDB', + status: statusMongodb, + restart: restartMongodb, + defaultMemoryLimit: (1 + Math.round(os.totalmem()/(1024*1024*1024)/4)) * 256 * 1024 * 1024 + }, + mysql: { + name: 'MySQL', + status: containerStatus.bind(null, 'mysql'), + restart: docker.restartContainer.bind(null, 'mysql'), + defaultMemoryLimit: (1 + Math.round(os.totalmem()/(1024*1024*1024)/4)) * 256 * 1024 * 1024 + }, + postgresql: { + name: 'PostgreSQL', + status: containerStatus.bind(null, 'postgresql'), + restart: docker.restartContainer.bind(null, 'postgresql'), + defaultMemoryLimit: (1 + Math.round(os.totalmem()/(1024*1024*1024)/4)) * 256 * 1024 * 1024 + }, + docker: { + name: 'Docker', + status: statusDocker, + restart: restartDocker, + defaultMemoryLimit: 0 + }, + unbound: { + name: 'Unbound', + status: statusUnbound, + restart: restartUnbound, + defaultMemoryLimit: 0 + }, + sftp: { + name: 'Filemanager', + status: containerStatus.bind(null, 'sftp'), + restart: docker.restartContainer.bind(null, 'sftp'), + defaultMemoryLimit: sftp.DEFAULT_MEMORY_LIMIT + }, + graphite: { + name: 'Metrics', + status: statusGraphite, + restart: restartGraphite, + defaultMemoryLimit: 256 * 1024 * 1024 + }, + nginx: { + name: 'Nginx', + status: statusNginx, + restart: restartNginx, + defaultMemoryLimit: 0 + } + }; + return _services; +} const APP_SERVICES = { redis: { @@ -338,25 +350,25 @@ async function containerStatus(containerName) { assert.strictEqual(typeof containerName, 'string'); const [error, container] = await safe(docker.inspect(containerName)); - if (error && error.reason === BoxError.NOT_FOUND) return { status: exports.SERVICE_STATUS_STOPPED }; + if (error && error.reason === BoxError.NOT_FOUND) return { status: SERVICE_STATUS_STOPPED }; if (error) throw error; const ip = safe.query(container, 'NetworkSettings.Networks.cloudron.IPAddress', null); - if (!ip) return { status: exports.SERVICE_STATUS_STOPPED }; + if (!ip) return { status: SERVICE_STATUS_STOPPED }; const isRunning = container.State?.Running; const [networkError, response] = await safe(superagent.get(`http://${ip}:3000/healthcheck`) .timeout(20000) .ok(() => true)); - if (networkError) return { status: exports.SERVICE_STATUS_STARTING, error: `Error waiting for ${containerName}: ${networkError.message}` }; - if (response.status !== 200 || !response.body.status) return { status: exports.SERVICE_STATUS_STARTING, error: `Error waiting for ${containerName}. Status code: ${response.status} message: ${response.body.message}` }; + if (networkError) return { status: SERVICE_STATUS_STARTING, error: `Error waiting for ${containerName}: ${networkError.message}` }; + if (response.status !== 200 || !response.body.status) return { status: SERVICE_STATUS_STARTING, error: `Error waiting for ${containerName}. Status code: ${response.status} message: ${response.body.message}` }; const result = await docker.getStats(containerName, { stream: false }); const stats = result.memory_stats || { usage: 0, limit: 1 }; return { - status: isRunning ? exports.SERVICE_STATUS_ACTIVE : exports.SERVICE_STATUS_STOPPED, + status: isRunning ? SERVICE_STATUS_ACTIVE : SERVICE_STATUS_STOPPED, memoryUsed: stats.usage, memoryPercent: parseInt(100 * stats.usage / stats.limit), healthcheck: response.body @@ -364,7 +376,7 @@ async function containerStatus(containerName) { } async function listServices() { - const serviceIds = Object.keys(SERVICES).map(k => { return { id: k, name: SERVICES[k].name }; }); + const serviceIds = Object.keys(SERVICES()).map(k => { return { id: k, name: SERVICES()[k].name }; }); const result = await apps.list(); for (const app of result) { @@ -400,11 +412,11 @@ async function getServiceStatus(id) { let containerStatusFunc, service; if (instance) { - service = APP_SERVICES[name]; + service = APP_SERVICES()[name]; if (!service) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); containerStatusFunc = service.status.bind(null, instance); - } else if (SERVICES[name]) { - service = SERVICES[name]; + } else if (SERVICES()[name]) { + service = SERVICES()[name]; containerStatusFunc = service.status; } else { throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); @@ -446,7 +458,7 @@ async function configureService(id, data, auditSource) { let needsRebuild = false; if (instance) { - if (!APP_SERVICES[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); + if (!APP_SERVICES()[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); const app = await apps.get(instance); if (!app) throw new BoxError(BoxError.NOT_FOUND, 'App not found'); @@ -456,7 +468,7 @@ async function configureService(id, data, auditSource) { servicesConfig[name] = data; await apps.update(instance, { servicesConfig }); - } else if (SERVICES[name]) { + } else if (SERVICES()[name]) { const servicesConfig = await getConfig(); needsRebuild = servicesConfig[name]?.recoveryMode != data.recoveryMode; // intentional != since 'recoveryMode' may or may not be there @@ -486,8 +498,8 @@ async function getServiceLogs(id, options) { const [name, instance ] = id.split(':'); if (instance) { - if (!APP_SERVICES[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); - } else if (!SERVICES[name]) { + if (!APP_SERVICES()[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); + } else if (!SERVICES()[name]) { throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); } @@ -500,7 +512,7 @@ async function getServiceLogs(id, options) { } else if (name === 'nginx') { cp = logs.tail(['/var/log/nginx/access.log', '/var/log/nginx/error.log'], { lines: options.lines, follow: options.follow }); } else { - const containerName = APP_SERVICES[name] ? `${name}-${instance}` : name; + const containerName = APP_SERVICES()[name] ? `${name}-${instance}` : name; cp = logs.tail([path.join(paths.LOG_DIR, containerName, 'app.log')], { lines: options.lines, follow: options.follow }); } @@ -566,11 +578,11 @@ async function restartService(id, auditSource) { const [name, instance ] = id.split(':'); if (instance) { - if (!APP_SERVICES[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); + if (!APP_SERVICES()[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); - await APP_SERVICES[name].restart(instance); - } else if (SERVICES[name]) { - await SERVICES[name].restart(); + await APP_SERVICES()[name].restart(instance); + } else if (SERVICES()[name]) { + await SERVICES()[name].restart(); } else { throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); } @@ -586,7 +598,7 @@ async function startAppServices(app) { for (const addon of Object.keys(app.manifest.addons || {})) { if (!(addon in APP_SERVICES)) continue; - const [error] = await safe(APP_SERVICES[addon].start(instance)); // assume addons name is service name + const [error] = await safe(APP_SERVICES()[addon].start(instance)); // assume addons name is service name // error ignored because we don't want "start app" to error. use can fix it from Services if (error) debug(`startAppServices: ${addon}:${instance}. %o`, error); } @@ -600,7 +612,7 @@ async function stopAppServices(app) { for (const addon of Object.keys(app.manifest.addons || {})) { if (!(addon in APP_SERVICES)) continue; - const [error] = await safe(APP_SERVICES[addon].stop(instance)); // assume addons name is service name + const [error] = await safe(APP_SERVICES()[addon].stop(instance)); // assume addons name is service name // error ignored because we don't want "start app" to error. use can fix it from Services if (error) debug(`stopAppServices: ${addon}:${instance}. %o`, error); } @@ -780,18 +792,18 @@ async function applyMemoryLimit(id) { const serviceConfig = await getServiceConfig(id); if (instance) { - if (!APP_SERVICES[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); + if (!APP_SERVICES()[name]) throw new BoxError(BoxError.NOT_FOUND, 'Service not found'); containerName = `${name}-${instance}`; - memoryLimit = serviceConfig && serviceConfig.memoryLimit ? serviceConfig.memoryLimit : APP_SERVICES[name].defaultMemoryLimit; - } else if (SERVICES[name]) { + memoryLimit = serviceConfig && serviceConfig.memoryLimit ? serviceConfig.memoryLimit : APP_SERVICES()[name].defaultMemoryLimit; + } else if (SERVICES()[name]) { if (name === 'mongodb' && !await hasAVX()) { debug('applyMemoryLimit: skipping mongodb because CPU does not have AVX'); return; } containerName = name; - memoryLimit = serviceConfig && serviceConfig.memoryLimit ? serviceConfig.memoryLimit : SERVICES[name].defaultMemoryLimit; + memoryLimit = serviceConfig && serviceConfig.memoryLimit ? serviceConfig.memoryLimit : SERVICES()[name].defaultMemoryLimit; } else { throw new BoxError(BoxError.NOT_FOUND, 'No such service'); } @@ -1003,7 +1015,7 @@ async function startTurn(existingInfra) { const serviceConfig = await getServiceConfig('turn'); const image = infra.images.turn; - const memoryLimit = serviceConfig.memoryLimit || SERVICES['turn'].defaultMemoryLimit; + const memoryLimit = serviceConfig.memoryLimit || SERVICES()['turn'].defaultMemoryLimit; const { fqdn:realm } = await dashboard.getLocation(); let turnSecret = await blobs.getString(blobs.ADDON_TURN_SECRET); @@ -1705,7 +1717,7 @@ async function restoreMongoDb(app, options) { } async function statusMongodb() { - if (!await hasAVX()) return { status: exports.SERVICE_STATUS_DISABLED }; + if (!await hasAVX()) return { status: SERVICE_STATUS_DISABLED }; return await containerStatus('mongodb'); } @@ -1862,7 +1874,7 @@ async function setupRedis(app, options) { const redisServiceToken = hat(4 * 48); // Compute redis memory limit based on app's memory limit (this is arbitrary) - const memoryLimit = app.servicesConfig['redis']?.memoryLimit || APP_SERVICES['redis'].defaultMemoryLimit; + const memoryLimit = app.servicesConfig['redis']?.memoryLimit || APP_SERVICES()['redis'].defaultMemoryLimit; const recoveryMode = app.servicesConfig['redis']?.recoveryMode || false; const readOnly = !recoveryMode ? '--read-only' : ''; @@ -1990,14 +2002,14 @@ async function teardownTls(app, options) { async function statusTurn() { const [error, container] = await safe(docker.inspect('turn')); - if (error && error.reason === BoxError.NOT_FOUND) return { status: exports.SERVICE_STATUS_STOPPED }; + if (error && error.reason === BoxError.NOT_FOUND) return { status: SERVICE_STATUS_STOPPED }; if (error) throw error; const result = await docker.getStats(container.Id, { stream: false }); const status = container.State.Running - ? (container.HostConfig.ReadonlyRootfs ? exports.SERVICE_STATUS_ACTIVE : exports.SERVICE_STATUS_STARTING) - : exports.SERVICE_STATUS_STOPPED; + ? (container.HostConfig.ReadonlyRootfs ? SERVICE_STATUS_ACTIVE : SERVICE_STATUS_STARTING) + : SERVICE_STATUS_STOPPED; const stats = result.memory_stats || { usage: 0, limit: 1 }; return { @@ -2009,7 +2021,7 @@ async function statusTurn() { async function statusDocker() { const [error] = await safe(docker.ping()); - return { status: error ? exports.SERVICE_STATUS_STOPPED: exports.SERVICE_STATUS_ACTIVE }; + return { status: error ? SERVICE_STATUS_STOPPED: SERVICE_STATUS_ACTIVE }; } async function restartDocker() { @@ -2019,13 +2031,13 @@ async function restartDocker() { async function statusUnbound() { const [error] = await safe(shell.spawn('systemctl', ['is-active', 'unbound'], { encoding: 'utf8' })); - if (error) return { status: exports.SERVICE_STATUS_STOPPED }; + if (error) return { status: SERVICE_STATUS_STOPPED }; const [digError, digResult] = await safe(dig.resolve('ipv4.api.cloudron.io', 'A', { timeout: 10000 })); - if (!digError && Array.isArray(digResult) && digResult.length !== 0) return { status: exports.SERVICE_STATUS_ACTIVE }; + if (!digError && Array.isArray(digResult) && digResult.length !== 0) return { status: SERVICE_STATUS_ACTIVE }; debug('statusUnbound: unbound is up, but failed to resolve ipv4.api.cloudron.io . %o %j', digError, digResult); - return { status: exports.SERVICE_STATUS_STARTING }; + return { status: SERVICE_STATUS_STARTING }; } async function restartUnbound() { @@ -2035,7 +2047,7 @@ async function restartUnbound() { async function statusNginx() { const [error] = await safe(shell.spawn('systemctl', ['is-active', 'nginx'], { encoding: 'utf8' })); - return { status: error ? exports.SERVICE_STATUS_STOPPED : exports.SERVICE_STATUS_ACTIVE }; + return { status: error ? SERVICE_STATUS_STOPPED : SERVICE_STATUS_ACTIVE }; } async function restartNginx() { @@ -2045,7 +2057,7 @@ async function restartNginx() { async function statusGraphite() { const [error, container] = await safe(docker.inspect('graphite')); - if (error && error.reason === BoxError.NOT_FOUND) return { status: exports.SERVICE_STATUS_STOPPED }; + if (error && error.reason === BoxError.NOT_FOUND) return { status: SERVICE_STATUS_STOPPED }; if (error) throw error; const ip = safe.query(container, 'NetworkSettings.Networks.cloudron.IPAddress', null); @@ -2055,14 +2067,14 @@ async function statusGraphite() { .timeout(20000) .ok(() => true)); - if (networkError) return { status: exports.SERVICE_STATUS_STARTING, error: `Error waiting for graphite: ${networkError.message}` }; - if (response.status !== 200) return { status: exports.SERVICE_STATUS_STARTING, error: `Error waiting for graphite. Status code: ${response.status} message: ${response.body.message}` }; + if (networkError) return { status: SERVICE_STATUS_STARTING, error: `Error waiting for graphite: ${networkError.message}` }; + if (response.status !== 200) return { status: SERVICE_STATUS_STARTING, error: `Error waiting for graphite. Status code: ${response.status} message: ${response.body.message}` }; const result = await docker.getStats('graphite', { stream: false }); const stats = result.memory_stats || { usage: 0, limit: 1 }; return { - status: container.State.Running ? exports.SERVICE_STATUS_ACTIVE : exports.SERVICE_STATUS_STOPPED, + status: container.State.Running ? SERVICE_STATUS_ACTIVE : SERVICE_STATUS_STOPPED, memoryUsed: stats.usage, memoryPercent: parseInt(100 * stats.usage / stats.limit) }; diff --git a/src/settings.js b/src/settings.js index 425ae72c9..de7fcf405 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1,53 +1,77 @@ -'use strict'; +import assert from 'node:assert'; +import * as database from './database.js'; +import safe from 'safetydance'; -exports = module.exports = { +const APPSTORE_API_TOKEN_KEY = 'appstore_api_token'; +const API_SERVER_ORIGIN_KEY = 'api_server_origin'; +const AUTOUPDATE_PATTERN_KEY = 'autoupdate_pattern'; +const CLOUDRON_AVATAR_KEY = 'cloudron_avatar'; +const CLOUDRON_BACKGROUND_KEY = 'cloudron_background'; +const CLOUDRON_ID_KEY = 'cloudron_id'; +const CLOUDRON_NAME_KEY = 'cloudron_name'; +const CONSOLE_SERVER_ORIGIN_KEY = 'console_server_origin'; +const DASHBOARD_DOMAIN_KEY = 'dashboard_domain'; +const DASHBOARD_SUBDOMAIN_KEY = 'dashboard_subdomain'; +const DIRECTORY_SERVER_KEY = 'directory_server_config'; +const DYNAMIC_DNS_KEY = 'dynamic_dns'; +const EXTERNAL_LDAP_KEY = 'external_ldap_config'; +const FOOTER_KEY = 'footer'; +const FIREWALL_BLOCKLIST_KEY = 'firewall_blocklist'; +const GHOSTS_CONFIG_KEY = 'ghosts_config'; +const IPV4_CONFIG_KEY = 'ipv4_config'; +const IPV6_CONFIG_KEY = 'ipv6_config'; +const LANGUAGE_KEY = 'language'; +const MAIL_DOMAIN_KEY = 'mail_domain'; +const MAIL_SUBDOMAIN_KEY = 'mail_subdomain'; +const OIDC_COOKIE_SECRET_KEY = 'cookie_secret'; +const PROFILE_CONFIG_KEY = 'profile_config'; +const REVERSE_PROXY_CONFIG_KEY = 'reverseproxy_config'; +const SERVICES_CONFIG_KEY = 'services_config'; +const TIME_ZONE_KEY = 'time_zone'; +const TRUSTED_IPS_KEY = 'trusted_ips_key'; +const WEB_SERVER_ORIGIN_KEY = 'web_server_origin'; +const _clear = clear; +const _set = set; + +export { get, set, - getJson, setJson, - getBlob, setBlob, - - APPSTORE_API_TOKEN_KEY: 'appstore_api_token', - API_SERVER_ORIGIN_KEY: 'api_server_origin', - AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern', - CLOUDRON_AVATAR_KEY: 'cloudron_avatar', - CLOUDRON_BACKGROUND_KEY: 'cloudron_background', - CLOUDRON_ID_KEY: 'cloudron_id', - CLOUDRON_NAME_KEY: 'cloudron_name', - CONSOLE_SERVER_ORIGIN_KEY: 'console_server_origin', - DASHBOARD_DOMAIN_KEY: 'dashboard_domain', - DASHBOARD_SUBDOMAIN_KEY: 'dashboard_subdomain', - DIRECTORY_SERVER_KEY: 'directory_server_config', - DYNAMIC_DNS_KEY: 'dynamic_dns', - EXTERNAL_LDAP_KEY: 'external_ldap_config', - FOOTER_KEY: 'footer', - FIREWALL_BLOCKLIST_KEY: 'firewall_blocklist', - GHOSTS_CONFIG_KEY: 'ghosts_config', - IPV4_CONFIG_KEY: 'ipv4_config', - IPV6_CONFIG_KEY: 'ipv6_config', - LANGUAGE_KEY: 'language', - MAIL_DOMAIN_KEY: 'mail_domain', - MAIL_SUBDOMAIN_KEY: 'mail_subdomain', - OIDC_COOKIE_SECRET_KEY: 'cookie_secret', - PROFILE_CONFIG_KEY: 'profile_config', - REVERSE_PROXY_CONFIG_KEY: 'reverseproxy_config', - SERVICES_CONFIG_KEY: 'services_config', - TIME_ZONE_KEY: 'time_zone', - TRUSTED_IPS_KEY: 'trusted_ips_key', - WEB_SERVER_ORIGIN_KEY: 'web_server_origin', - - // testing - _clear: clear, - _set: set + APPSTORE_API_TOKEN_KEY, + API_SERVER_ORIGIN_KEY, + AUTOUPDATE_PATTERN_KEY, + CLOUDRON_AVATAR_KEY, + CLOUDRON_BACKGROUND_KEY, + CLOUDRON_ID_KEY, + CLOUDRON_NAME_KEY, + CONSOLE_SERVER_ORIGIN_KEY, + DASHBOARD_DOMAIN_KEY, + DASHBOARD_SUBDOMAIN_KEY, + DIRECTORY_SERVER_KEY, + DYNAMIC_DNS_KEY, + EXTERNAL_LDAP_KEY, + FOOTER_KEY, + FIREWALL_BLOCKLIST_KEY, + GHOSTS_CONFIG_KEY, + IPV4_CONFIG_KEY, + IPV6_CONFIG_KEY, + LANGUAGE_KEY, + MAIL_DOMAIN_KEY, + MAIL_SUBDOMAIN_KEY, + OIDC_COOKIE_SECRET_KEY, + PROFILE_CONFIG_KEY, + REVERSE_PROXY_CONFIG_KEY, + SERVICES_CONFIG_KEY, + TIME_ZONE_KEY, + TRUSTED_IPS_KEY, + WEB_SERVER_ORIGIN_KEY, + _clear, + _set, }; -const assert = require('node:assert'), - database = require('./database.js'), - safe = require('safetydance'); - const SETTINGS_FIELDS = [ 'name', 'value' ].join(','); const SETTINGS_BLOB_FIELDS = [ 'name', 'valueBlob' ].join(','); diff --git a/src/sftp.js b/src/sftp.js index 0c26290bc..7e630393f 100644 --- a/src/sftp.js +++ b/src/sftp.js @@ -1,28 +1,30 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import blobs from './blobs.js'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import * as docker from './docker.js'; +import hat from './hat.js'; +import infra from './infra_version.js'; +import mounts from './mounts.js'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import services from './services.js'; +import shellModule from './shell.js'; +import * as volumes from './volumes.js'; -exports = module.exports = { +const debug = debugModule('box:sftp'); +const shell = shellModule('sftp'); + +const DEFAULT_MEMORY_LIMIT = 256 * 1024 * 1024; + +export { start, - - DEFAULT_MEMORY_LIMIT: 256 * 1024 * 1024 + DEFAULT_MEMORY_LIMIT, }; -const apps = require('./apps.js'), - assert = require('node:assert'), - blobs = require('./blobs.js'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:sftp'), - docker = require('./docker.js'), - hat = require('./hat.js'), - infra = require('./infra_version.js'), - mounts = require('./mounts.js'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - services = require('./services.js'), - shell = require('./shell.js')('sftp'), - volumes = require('./volumes.js'); - async function ensureKeys() { for (const keyType of [ 'rsa', 'ed25519' ]) { const privateKey = await blobs.get(`sftp_${keyType}_private_key`); @@ -54,7 +56,7 @@ async function start(existingInfra) { const serviceConfig = await services.getServiceConfig('sftp'); const image = infra.images.sftp; - const memoryLimit = serviceConfig.memoryLimit || exports.DEFAULT_MEMORY_LIMIT; + const memoryLimit = serviceConfig.memoryLimit || DEFAULT_MEMORY_LIMIT; const cloudronToken = hat(8 * 128); await ensureKeys(); diff --git a/src/shell.js b/src/shell.js index d549b58ac..3111fc276 100644 --- a/src/shell.js +++ b/src/shell.js @@ -1,14 +1,14 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import child_process from 'node:child_process'; +import debugModule from 'debug'; +import path from 'node:path'; +import safe from 'safetydance'; +import * as _ from './underscore.js'; -exports = module.exports = shell; +const debug = debugModule('box:shell'); -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - child_process = require('node:child_process'), - debug = require('debug')('box:shell'), - path = require('node:path'), - safe = require('safetydance'), - _ = require('./underscore.js'); +export default shell; function shell(tag) { assert.strictEqual(typeof tag, 'string'); @@ -21,7 +21,7 @@ function shell(tag) { } const SUDO = '/usr/bin/sudo'; -const KILL_CHILD_CMD = path.join(__dirname, 'scripts/kill-child.sh'); +const KILL_CHILD_CMD = path.join(import.meta.dirname, 'scripts/kill-child.sh'); function lineCount(buffer) { assert(Buffer.isBuffer(buffer)); diff --git a/src/storage/filesystem.js b/src/storage/filesystem.js index c7604f4ab..c49639366 100644 --- a/src/storage/filesystem.js +++ b/src/storage/filesystem.js @@ -1,6 +1,20 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; +import crypto from 'crypto'; +import debugModule from 'debug'; +import * as df from '../df.js'; +import fs from 'node:fs'; +import mounts from '../mounts.js'; +import path from 'node:path'; +import paths from '../paths.js'; +import safe from 'safetydance'; +import shellModule from '../shell.js'; +import * as _ from '../underscore.js'; -exports = module.exports = { +const debug = debugModule('box:storage/filesystem'); +const shell = shellModule('filesystem'); + +export { setup, teardown, cleanup, @@ -25,20 +39,6 @@ exports = module.exports = { removeDir, }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'), - crypto = require('crypto'), - debug = require('debug')('box:storage/filesystem'), - df = require('../df.js'), - fs = require('node:fs'), - mounts = require('../mounts.js'), - path = require('node:path'), - paths = require('../paths.js'), - safe = require('safetydance'), - shell = require('../shell.js')('filesystem'), - _ = require('../underscore.js'); - - function getRootPath(config) { assert.strictEqual(typeof config, 'object'); diff --git a/src/storage/gcs.js b/src/storage/gcs.js index a7dd43f44..a9c11f454 100644 --- a/src/storage/gcs.js +++ b/src/storage/gcs.js @@ -1,6 +1,16 @@ -'use strict'; +import assert from 'node:assert'; +import async from 'async'; +import BoxError from '../boxerror.js'; +import constants from '../constants.js'; +import debugModule from 'debug'; +import { Storage as GCS } from '@google-cloud/storage'; +import path from 'node:path'; +import safe from 'safetydance'; +import * as _ from '../underscore.js'; -exports = module.exports = { +const debug = debugModule('box:storage/gcs'); + +export { getAvailableSize, getStatus, @@ -25,16 +35,6 @@ exports = module.exports = { injectPrivateFields, }; -const assert = require('node:assert'), - async = require('async'), - BoxError = require('../boxerror.js'), - constants = require('../constants.js'), - debug = require('debug')('box:storage/gcs'), - GCS = require('@google-cloud/storage').Storage, - path = require('node:path'), - safe = require('safetydance'), - _ = require('../underscore.js'); - function getBucket(config) { assert.strictEqual(typeof config, 'object'); diff --git a/src/storage/interface.js b/src/storage/interface.js index 942de67dc..60d9ff920 100644 --- a/src/storage/interface.js +++ b/src/storage/interface.js @@ -1,4 +1,5 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from '../boxerror.js'; // ------------------------------------------- // This file just describes the interface @@ -10,7 +11,7 @@ // retry logic for upload() comes from the syncer since it is stream based // for the other API calls we leave it to the backend to retry. this allows // them to tune the concurrency based on failures/rate limits accordingly -exports = module.exports = { +export { getAvailableSize, getStatus, @@ -36,9 +37,6 @@ exports = module.exports = { injectPrivateFields }; -const assert = require('node:assert'), - BoxError = require('../boxerror.js'); - function removePrivateFields(config) { // in-place removal of tokens and api keys return config; diff --git a/src/storage/s3.js b/src/storage/s3.js index 954d51219..2ef407c0e 100644 --- a/src/storage/s3.js +++ b/src/storage/s3.js @@ -1,51 +1,46 @@ -'use strict'; +import assert from 'node:assert'; +import async from 'async'; +import BoxError from '../boxerror.js'; +import { ConfiguredRetryStrategy } from '@smithy/util-retry'; +import constants from '../constants.js'; +import consumers from 'node:stream/consumers'; +import crypto from 'node:crypto'; +import debugModule from 'debug'; +import http from 'node:http'; +import https from 'node:https'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; +import { PassThrough } from 'node:stream'; +import path from 'node:path'; +import { Readable } from 'node:stream'; +import { S3, NoSuchKey, NoSuchBucket } from '@aws-sdk/client-s3'; +import safe from 'safetydance'; +import { Upload } from '@aws-sdk/lib-storage'; +import * as _ from '../underscore.js'; -exports = module.exports = { +const debug = debugModule('box:storage/s3'); + +const _chunk = chunk; + +export { setup, teardown, cleanup, - verifyConfig, removePrivateFields, injectPrivateFields, - getAvailableSize, getStatus, - upload, exists, download, copy, copyDir, - listDir, - remove, removeDir, - - // Used to mock AWS - _chunk: chunk + _chunk, }; -const assert = require('node:assert'), - async = require('async'), - BoxError = require('../boxerror.js'), - { ConfiguredRetryStrategy } = require('@smithy/util-retry'), - constants = require('../constants.js'), - consumers = require('node:stream/consumers'), - crypto = require('node:crypto'), - debug = require('debug')('box:storage/s3'), - http = require('node:http'), - https = require('node:https'), - { NodeHttpHandler } = require('@smithy/node-http-handler'), - { PassThrough } = require('node:stream'), - path = require('node:path'), - { Readable } = require('node:stream'), - { S3, NoSuchKey, NoSuchBucket } = require('@aws-sdk/client-s3'), - safe = require('safetydance'), - { Upload } = require('@aws-sdk/lib-storage'), - _ = require('../underscore.js'); - function S3_NOT_FOUND(error) { return error instanceof NoSuchKey || error instanceof NoSuchBucket; } diff --git a/src/syncer.js b/src/syncer.js index 94563f36a..8ec42ec73 100644 --- a/src/syncer.js +++ b/src/syncer.js @@ -1,20 +1,20 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import DataLayout from './datalayout.js'; +import debugModule from 'debug'; +import fs from 'node:fs'; +import path from 'node:path'; +import readline from 'node:readline'; +import safe from 'safetydance'; +import util from 'node:util'; -exports = module.exports = { +const debug = debugModule('box:syncer'); + +export { sync, finalize }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - DataLayout = require('./datalayout.js'), - debug = require('debug')('box:syncer'), - fs = require('node:fs'), - path = require('node:path'), - readline = require('node:readline'), - safe = require('safetydance'), - util = require('node:util'); - function readCache(cacheFile) { assert.strictEqual(typeof cacheFile, 'string'); diff --git a/src/system.js b/src/system.js index 3acb5771d..85b43b09c 100644 --- a/src/system.js +++ b/src/system.js @@ -1,6 +1,26 @@ -'use strict'; +import apps from './apps.js'; +import assert from 'node:assert'; +import { AsyncTask } from './asynctask.js'; +import * as backupSites from './backupsites.js'; +import BoxError from './boxerror.js'; +import debugModule from 'debug'; +import * as df from './df.js'; +import * as docker from './docker.js'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import * as logs from './logs.js'; +import * as notifications from './notifications.js'; +import os from 'node:os'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import shellModule from './shell.js'; +import * as volumes from './volumes.js'; -exports = module.exports = { +const debug = debugModule('box:system'); +const shell = shellModule('system'); + +export { reboot, getInfo, getUbuntuVersion, @@ -17,28 +37,9 @@ exports = module.exports = { getFilesystemUsage }; -const apps = require('./apps.js'), - assert = require('node:assert'), - { AsyncTask } = require('./asynctask.js'), - backupSites = require('./backupsites.js'), - BoxError = require('./boxerror.js'), - debug = require('debug')('box:system'), - df = require('./df.js'), - docker = require('./docker.js'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - logs = require('./logs.js'), - notifications = require('./notifications.js'), - os = require('node:os'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - shell = require('./shell.js')('system'), - volumes = require('./volumes.js'); - -const DU_CMD = path.join(__dirname, 'scripts/du.sh'); -const HDPARM_CMD = path.join(__dirname, 'scripts/hdparm.sh'); -const REBOOT_CMD = path.join(__dirname, 'scripts/reboot.sh'); +const DU_CMD = path.join(import.meta.dirname, 'scripts/du.sh'); +const HDPARM_CMD = path.join(import.meta.dirname, 'scripts/hdparm.sh'); +const REBOOT_CMD = path.join(import.meta.dirname, 'scripts/reboot.sh'); async function du(file, options) { assert.strictEqual(typeof file, 'string'); diff --git a/src/tasks.js b/src/tasks.js index 8b26ef0f5..1dfa7fcdc 100644 --- a/src/tasks.js +++ b/src/tasks.js @@ -1,6 +1,23 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import * as logs from './logs.js'; +import mysql from 'mysql2'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import shellModule from './shell.js'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:tasks'); +const shell = shellModule('tasks'); + +const ESTOPPED = 'stopped'; +const ECRASHED = 'crashed'; +const ETIMEOUT = 'timeout'; + +export default { get, add, update, @@ -38,10 +55,9 @@ exports = module.exports = { TASK_CHECK_BACKUP_INTEGRITY: 'checkBackupIntegrity', // error codes - ESTOPPED: 'stopped', - ECRASHED: 'crashed', - ETIMEOUT: 'timeout', - + ESTOPPED, + ECRASHED, + ETIMEOUT, // testing _TASK_IDENTITY: 'identity', _TASK_CRASH: 'crash', @@ -49,22 +65,10 @@ exports = module.exports = { _TASK_SLEEP: 'sleep' }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - database = require('./database.js'), - debug = require('debug')('box:tasks'), - logs = require('./logs.js'), - mysql = require('mysql2'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - shell = require('./shell.js')('tasks'), - _ = require('./underscore.js'); - let gTasks = {}; // holds AbortControllers indexed by task id -const START_TASK_CMD = path.join(__dirname, 'scripts/starttask.sh'); -const STOP_TASK_CMD = path.join(__dirname, 'scripts/stoptask.sh'); +const START_TASK_CMD = path.join(import.meta.dirname, 'scripts/starttask.sh'); +const STOP_TASK_CMD = path.join(import.meta.dirname, 'scripts/stoptask.sh'); const TASKS_FIELDS = [ 'id', 'type', 'argsJson', 'percent', 'pending', 'completed', 'message', 'errorJson', 'creationTime', 'resultJson', 'ts' ]; @@ -94,7 +98,7 @@ function postProcess(task) { // the error in db will be empty if task is done but the completed flag is not set if (!task.active && !task.completed) { - task.error = { message: 'Task was stopped because the server restarted or crashed', code: exports.ECRASHED }; + task.error = { message: 'Task was stopped because the server restarted or crashed', code: ECRASHED }; } return task; @@ -238,11 +242,11 @@ async function startTask(id, options) { // taskworker.sh forwards the exit code of the actual worker. It's either a raw signal number OR the exit code let taskError = null; - if (sudoError.timedOut) taskError = { message: `Task ${id} timed out`, code: exports.ETIMEOUT }; - else if (sudoError.code === 70) taskError = { message: `Task ${id} stopped`, code: exports.ESTOPPED }; // set by taskworker SIGTERM - else if (sudoError.code === 9 /* SIGKILL */) taskError = { message: `Task ${id} ran out of memory or terminated`, code: exports.ECRASHED }; // SIGTERM with oom gets set as 2 by nodejs - else if (sudoError.code === 50) taskError = { message:`Task ${id} crashed with code ${sudoError.code}`, code: exports.ECRASHED }; - else taskError = { message:`Task ${id} crashed with unknown code ${sudoError.code}`, code: exports.ECRASHED }; + if (sudoError.timedOut) taskError = { message: `Task ${id} timed out`, code: ETIMEOUT }; + else if (sudoError.code === 70) taskError = { message: `Task ${id} stopped`, code: ESTOPPED }; // set by taskworker SIGTERM + else if (sudoError.code === 9 /* SIGKILL */) taskError = { message: `Task ${id} ran out of memory or terminated`, code: ECRASHED }; // SIGTERM with oom gets set as 2 by nodejs + else if (sudoError.code === 50) taskError = { message:`Task ${id} crashed with code ${sudoError.code}`, code: ECRASHED }; + else taskError = { message:`Task ${id} crashed with unknown code ${sudoError.code}`, code: ECRASHED }; debug(`startTask: ${id} done. error: %o`, taskError); await safe(setCompleted(id, { error: taskError }), { debug }); diff --git a/src/taskworker.js b/src/taskworker.js index a5e5028eb..08b7e5381 100755 --- a/src/taskworker.js +++ b/src/taskworker.js @@ -1,26 +1,25 @@ #!/usr/bin/env -S node --unhandled-rejections=strict -'use strict'; - -const apptask = require('./apptask.js'), - backupCleaner = require('./backupcleaner.js'), - backupIntegrity = require('./backupintegrity.js'), - backuptask = require('./backuptask.js'), - BoxError = require('./boxerror.js'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - dns = require('./dns.js'), - dyndns = require('./dyndns.js'), - externalLdap = require('./externalldap.js'), - fs = require('node:fs'), - locks = require('./locks.js'), - mailServer = require('./mailserver.js'), - net = require('node:net'), - reverseProxy = require('./reverseproxy.js'), - safe = require('safetydance'), - tasks = require('./tasks.js'), - timers = require('timers/promises'), - updater = require('./updater.js'); +import * as apptask from './apptask.js'; +import * as backupCleaner from './backupcleaner.js'; +import * as backupIntegrity from './backupintegrity.js'; +import * as backuptask from './backuptask.js'; +import BoxError from './boxerror.js'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import * as dns from './dns.js'; +import * as dyndns from './dyndns.js'; +import * as externalLdap from './externalldap.js'; +import fs from 'node:fs'; +import locks from './locks.js'; +import * as mailServer from './mailserver.js'; +import net from 'node:net'; +import * as reverseProxy from './reverseproxy.js'; +import safe from 'safetydance'; +import tasks from './tasks.js'; +import timers from 'timers/promises'; +import * as updater from './updater.js'; +import debugModule from 'debug'; const TASKS = { // indexed by task type app: apptask.run, @@ -101,7 +100,7 @@ async function main() { return process.exit(50); } - const debug = require('debug')('box:taskworker'); // require this here so that logging handler is already setup + const debug = debugModule('box:taskworker'); // import this here so that logging handler is already setup process.on('SIGTERM', () => { debug('Terminated'); diff --git a/src/test/acme2-test.js b/src/test/acme2-test.js index 63dcfa47e..70b063027 100644 --- a/src/test/acme2-test.js +++ b/src/test/acme2-test.js @@ -1,14 +1,13 @@ /* global it:false */ + +import * as acme2 from '../acme2.js'; +import * as common from './common.js'; +import expect from 'expect.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const acme2 = require('../acme2.js'), - common = require('./common.js'), - expect = require('expect.js'); - describe('Acme2', function () { const { setup, cleanup } = common; diff --git a/src/test/addonconfigs-test.js b/src/test/addonconfigs-test.js index 7dc56dc68..09085650c 100644 --- a/src/test/addonconfigs-test.js +++ b/src/test/addonconfigs-test.js @@ -1,14 +1,13 @@ /* global it:false */ + +import * as addonConfigs from '../addonconfigs.js'; +import * as common from './common.js'; +import expect from 'expect.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const addonConfigs = require('../addonconfigs.js'), - common = require('./common.js'), - expect = require('expect.js'); - describe('Addon config', function () { const { setup, cleanup, app } = common; diff --git a/src/test/applinks-test.js b/src/test/applinks-test.js index 6a1b775f1..b20e20060 100644 --- a/src/test/applinks-test.js +++ b/src/test/applinks-test.js @@ -1,16 +1,15 @@ /* global it:false */ + +import * as applinks from '../applinks.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const applinks = require('../applinks.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('Applinks', function () { const { setup, cleanup } = common; diff --git a/src/test/apppasswords-test.js b/src/test/apppasswords-test.js index dc15bd114..eae624cd4 100644 --- a/src/test/apppasswords-test.js +++ b/src/test/apppasswords-test.js @@ -1,18 +1,17 @@ /* jslint node:true */ + +import * as appPasswords from '../apppasswords.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; +import * as users from '../users.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const appPasswords = require('../apppasswords.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'), - users = require('../users.js'); - describe('App passwords', function () { const { setup, cleanup, admin } = common; diff --git a/src/test/apps-test.js b/src/test/apps-test.js index 704338e2b..c5ceee122 100644 --- a/src/test/apps-test.js +++ b/src/test/apps-test.js @@ -1,19 +1,20 @@ /* global it:false */ + +import apps from '../apps.js'; +import AuditSource from '../auditsource.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import Location from '../location.js'; +import safe from 'safetydance'; +import * as users from '../users.js'; + +const { proxyApp } = common; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const apps = require('../apps.js'), - AuditSource = require('../auditsource.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - Location = require('../location.js'), - safe = require('safetydance'), - users = require('../users.js'); - describe('Apps', function () { const { domainSetup, cleanup, app, admin, user , domain } = common; @@ -356,7 +357,6 @@ describe('Apps', function () { }); describe('proxy app', function () { - const proxyApp = require('./common.js').proxyApp; const newUpstreamUri = 'https://foobar.com:443'; before(async function () { diff --git a/src/test/apptask-test.js b/src/test/apptask-test.js index 6b993be6e..dd6433214 100644 --- a/src/test/apptask-test.js +++ b/src/test/apptask-test.js @@ -1,17 +1,16 @@ /* global it:false */ + +import * as apptask from '../apptask.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import paths from '../paths.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const apptask = require('../apptask.js'), - common = require('./common.js'), - expect = require('expect.js'), - fs = require('node:fs'), - paths = require('../paths.js'), - safe = require('safetydance'); - describe('apptask', function () { const { setup, cleanup, app } = common; diff --git a/src/test/archives-test.js b/src/test/archives-test.js index 4a6f42d06..9bf4543ff 100644 --- a/src/test/archives-test.js +++ b/src/test/archives-test.js @@ -1,17 +1,16 @@ /* global it:false */ + +import * as archives from '../archives.js'; +import backups from '../backups.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const archives = require('../archives.js'), - backups = require('../backups.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('Archives', function () { const { setup, cleanup, auditSource, getDefaultBackupSite } = common; diff --git a/src/test/backupcleaner-test.js b/src/test/backupcleaner-test.js index 09c92197c..aed6a6a61 100644 --- a/src/test/backupcleaner-test.js +++ b/src/test/backupcleaner-test.js @@ -1,21 +1,20 @@ /* jslint node:true */ + +import * as archives from '../archives.js'; +import * as backupCleaner from '../backupcleaner.js'; +import backups from '../backups.js'; +import * as backupSites from '../backupsites.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import moment from 'moment'; +import tasks from '../tasks.js'; +import timers from 'timers/promises'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const archives = require('../archives.js'), - backupCleaner = require('../backupcleaner.js'), - backups = require('../backups.js'), - backupSites = require('../backupsites.js'), - common = require('./common.js'), - expect = require('expect.js'), - moment = require('moment'), - tasks = require('../tasks.js'), - timers = require('timers/promises'); - describe('backup cleaner', function () { const { setup, cleanup, app, getDefaultBackupSite, auditSource } = common; diff --git a/src/test/backupformat-test.js b/src/test/backupformat-test.js index 7cecb0d6d..d62656ddb 100644 --- a/src/test/backupformat-test.js +++ b/src/test/backupformat-test.js @@ -1,22 +1,22 @@ /* jslint node:true */ + +import * as common from './common.js'; +import DataLayout from '../datalayout.js'; +import * as tgz from '../backupformat/tgz.js'; +const EnsureFileSizeStream = tgz._EnsureFileSizeStream; +import expect from 'expect.js'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import * as rsync from '../backupformat/rsync.js'; +import safe from 'safetydance'; +import stream from 'node:stream/promises'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - DataLayout = require('../datalayout.js'), - EnsureFileSizeStream = require('../backupformat/tgz.js')._EnsureFileSizeStream, - expect = require('expect.js'), - fs = require('node:fs'), - os = require('node:os'), - path = require('node:path'), - rsync = require('../backupformat/rsync.js'), - safe = require('safetydance'), - stream = require('node:stream/promises'); - describe('backuptask', function () { const { setup, cleanup, createTree } = common; @@ -77,7 +77,7 @@ describe('backuptask', function () { createTree(tmpdir, { 'data': { 'subdir': { 'emptydir': { } } }, 'dir2': { 'file': 'stuff' } }); fs.chmodSync(path.join(tmpdir, 'dir2/file'), parseInt('0755', 8)); - let dataLayout = new DataLayout(tmpdir, []); + const dataLayout = new DataLayout(tmpdir, []); await rsync._saveFsMetadata(dataLayout, `${dataLayout.localRoot()}/fsmetadata.json`); @@ -93,7 +93,7 @@ describe('backuptask', function () { expect(fs.existsSync(path.join(tmpdir, 'data/subdir/emptydir'))).to.be(false); // just make sure rimraf worked - let dataLayout = new DataLayout(tmpdir, []); + const dataLayout = new DataLayout(tmpdir, []); await rsync._restoreFsMetadata(dataLayout, `${dataLayout.localRoot()}/fsmetadata.json`); diff --git a/src/test/backups-test.js b/src/test/backups-test.js index 60cb83bda..44c5d413d 100644 --- a/src/test/backups-test.js +++ b/src/test/backups-test.js @@ -1,17 +1,16 @@ /* jslint node:true */ + +import backups from '../backups.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const backups = require('../backups.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('backups', function () { const { setup, cleanup, getDefaultBackupSite } = common; diff --git a/src/test/backupsites-test.js b/src/test/backupsites-test.js index 8b6e71cf0..d4d53186f 100644 --- a/src/test/backupsites-test.js +++ b/src/test/backupsites-test.js @@ -1,20 +1,19 @@ /* jslint node:true */ + +import * as archives from '../archives.js'; +import backups from '../backups.js'; +import * as backupSites from '../backupsites.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import constants from '../constants.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const archives = require('../archives.js'), - backups = require('../backups.js'), - backupSites = require('../backupsites.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - constants = require('../constants.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('backups', function () { const { setup, cleanup, auditSource, getDefaultBackupSite } = common; diff --git a/src/test/backuptask-test.js b/src/test/backuptask-test.js index 93c9952c1..89446b1ea 100644 --- a/src/test/backuptask-test.js +++ b/src/test/backuptask-test.js @@ -1,21 +1,21 @@ /* jslint node:true */ + +import backups from '../backups.js'; +import * as backupSites from '../backupsites.js'; +import child_process from 'node:child_process'; +import * as common from './common.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import tasks from '../tasks.js'; +import timers from 'timers/promises'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const backups = require('../backups.js'), - backupSites = require('../backupsites.js'), - common = require('./common.js'), - expect = require('expect.js'), - fs = require('node:fs'), - os = require('node:os'), - path = require('node:path'), - tasks = require('../tasks.js'), - timers = require('timers/promises'); - describe('backuptask', function () { const { setup, cleanup, getDefaultBackupSite, auditSource } = common; @@ -63,7 +63,7 @@ describe('backuptask', function () { it('can backup', async function () { // arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/ - if (require('node:child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) { + if (child_process.execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) { console.log('test skipped because of MariaDB'); return; } @@ -77,7 +77,7 @@ describe('backuptask', function () { it('can take another backup', async function () { // arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/ - if (require('node:child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) { + if (child_process.execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) { console.log('test skipped because of MariaDB'); return; } diff --git a/src/test/blobs-test.js b/src/test/blobs-test.js index 682672f3b..8a5540502 100644 --- a/src/test/blobs-test.js +++ b/src/test/blobs-test.js @@ -1,14 +1,13 @@ /* global it:false */ + +import blobs from '../blobs.js'; +import * as common from './common.js'; +import expect from 'expect.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const blobs = require('../blobs.js'), - common = require('./common.js'), - expect = require('expect.js'); - describe('blobs', function () { const { setup, cleanup } = common; diff --git a/src/test/branding-test.js b/src/test/branding-test.js index 9541f2016..be56a9fd9 100644 --- a/src/test/branding-test.js +++ b/src/test/branding-test.js @@ -1,16 +1,15 @@ /* global it:false */ + +import BoxError from '../boxerror.js'; +import * as branding from '../branding.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - branding = require('../branding.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('Branding', function () { const { setup, cleanup, auditSource } = common; diff --git a/src/test/check-install b/src/test/check-install index cfe672520..c6400b8ca 100755 --- a/src/test/check-install +++ b/src/test/check-install @@ -46,7 +46,7 @@ if ! grep -q "backupupload.js closefrom_override" "/etc/sudoers.d/yellowtent"; t exit 1 fi -images=$(node -e "const i = require('${source_dir}/src/infra_version.js'); console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));") +images=$(node --input-type=module -e "import i from '${source_dir}/src/infra_version.js'; console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));") for image in ${images}; do if ! docker inspect "${image}" >/dev/null 2>/dev/null; then diff --git a/src/test/cloudron-test.js b/src/test/cloudron-test.js index 68d2e11cd..648ffa6fa 100644 --- a/src/test/cloudron-test.js +++ b/src/test/cloudron-test.js @@ -1,16 +1,15 @@ /* global it:false */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import * as cloudron from '../cloudron.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - cloudron = require('../cloudron.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('Cloudron', function () { const { setup, cleanup } = common; diff --git a/src/test/common.js b/src/test/common.js index 1e3fb1bb1..351f32651 100644 --- a/src/test/common.js +++ b/src/test/common.js @@ -1,24 +1,22 @@ -'use strict'; - -const apps = require('../apps.js'), - appstore = require('../appstore.js'), - backupSites = require('../backupsites.js'), - constants = require('../constants.js'), - cron = require('../cron.js'), - dashboard = require('../dashboard.js'), - database = require('../database.js'), - domains = require('../domains.js'), - expect = require('expect.js'), - fs = require('node:fs'), - locks = require('../locks.js'), - mailer = require('../mailer.js'), - mailServer = require('../mailserver.js'), - nock = require('nock'), - path = require('node:path'), - settings = require('../settings.js'), - tasks = require('../tasks.js'), - timers = require('timers/promises'), - users = require('../users.js'); +import apps from '../apps.js'; +import * as appstore from '../appstore.js'; +import * as backupSites from '../backupsites.js'; +import constants from '../constants.js'; +import * as cron from '../cron.js'; +import * as dashboard from '../dashboard.js'; +import * as database from '../database.js'; +import * as domains from '../domains.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import locks from '../locks.js'; +import * as mailer from '../mailer.js'; +import * as mailServer from '../mailserver.js'; +import nock from 'nock'; +import path from 'node:path'; +import * as settings from '../settings.js'; +import tasks from '../tasks.js'; +import timers from 'timers/promises'; +import * as users from '../users.js'; const manifest = { 'id': 'io.cloudron.test', @@ -164,7 +162,13 @@ const proxyApp = { }; Object.freeze(proxyApp); -exports = module.exports = { +const mockApiServerOrigin = 'http://localhost:6060'; +const dashboardDomain = domain.domain; +const dashboardFqdn = `my.${domain.domain}`; +const appstoreToken = 'atoken'; +const serverUrl = `http://localhost:${constants.PORT}`; + +export { createTree, domainSetup, databaseSetup, @@ -172,23 +176,19 @@ exports = module.exports = { cleanup, checkMails, clearMailQueue, - getDefaultBackupSite, - - mockApiServerOrigin: 'http://localhost:6060', - dashboardDomain: domain.domain, - dashboardFqdn: `my.${domain.domain}`, - + mockApiServerOrigin, + dashboardDomain, + dashboardFqdn, app, proxyApp, admin, auditSource, - domain, // the domain object + domain, // the domain object, manifest, user, - appstoreToken: 'atoken', - - serverUrl: `http://localhost:${constants.PORT}`, + appstoreToken, + serverUrl, }; function createTree(root, obj) { @@ -218,8 +218,8 @@ async function databaseSetup() { await database.initialize(); await database._clear(); - await appstore._setApiServerOrigin(exports.mockApiServerOrigin); - await dashboard._setLocation(constants.DASHBOARD_SUBDOMAIN, exports.dashboardDomain); + await appstore._setApiServerOrigin(mockApiServerOrigin); + await dashboard._setLocation(constants.DASHBOARD_SUBDOMAIN, dashboardDomain); // duplicated here since we clear the database await backupSites.addDefault(auditSource); @@ -238,7 +238,7 @@ async function setup() { const ownerId = await users.createOwner(admin.email, admin.username, admin.password, admin.displayName, auditSource); admin.id = ownerId; await apps.add(app.id, app.appStoreId, '', app.manifest, app.subdomain, app.domain, app.portBindings, app); - await settings._set(settings.APPSTORE_API_TOKEN_KEY, exports.appstoreToken); // appstore token + await settings._set(settings.APPSTORE_API_TOKEN_KEY, appstoreToken); // appstore token const userId = await users.add(user.email, user, auditSource); user.id = userId; await tasks.stopAllTasks(); @@ -247,7 +247,7 @@ async function setup() { async function cleanup() { nock.cleanAll(); - mailer._mailQueue = []; + mailer.clearMailQueue(); await cron.stopJobs(); await database._clear(); @@ -255,13 +255,13 @@ async function cleanup() { } function clearMailQueue() { - mailer._mailQueue = []; + mailer.clearMailQueue(); } async function checkMails(number) { await timers.setTimeout(1000); expect(mailer._mailQueue.length).to.equal(number); - const emails = mailer._mailQueue; + const emails = [...mailer._mailQueue]; // copy before clearing clearMailQueue(); // return for further investigation diff --git a/src/test/database-test.js b/src/test/database-test.js index 7a16b6ee9..de42fdf63 100644 --- a/src/test/database-test.js +++ b/src/test/database-test.js @@ -1,11 +1,9 @@ /* global it, describe, before, after */ -'use strict'; - -const BoxError = require('../boxerror.js'), - database = require('../database'), - expect = require('expect.js'), - safe = require('safetydance'); +import BoxError from '../boxerror.js'; +import * as database from '../database.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; describe('database', function () { describe('init', function () { diff --git a/src/test/datalayout-test.js b/src/test/datalayout-test.js index 96f9f23da..72fce39fe 100644 --- a/src/test/datalayout-test.js +++ b/src/test/datalayout-test.js @@ -1,14 +1,11 @@ -'use strict'; - /* global it:false */ + +import DataLayout from '../datalayout.js'; +import expect from 'expect.js'; + /* global describe:false */ /* global before:false */ -'use strict'; - -const DataLayout = require('../datalayout.js'), - expect = require('expect.js'); - describe('DataLayout', function () { describe('no dirMap', function () { const BOX_DATA_DIR = '/home/yellowtent/boxdata/box'; @@ -84,4 +81,4 @@ describe('DataLayout', function () { }); }); -}); \ No newline at end of file +}); diff --git a/src/test/df-test.js b/src/test/df-test.js index 51860d469..807523bb1 100644 --- a/src/test/df-test.js +++ b/src/test/df-test.js @@ -1,15 +1,15 @@ /* jslint node:true */ + +import child_process from 'node:child_process'; +import * as common from './common.js'; +import * as df from '../df.js'; +import expect from 'expect.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - df = require('../df.js'), - expect = require('expect.js'); - describe('df', function () { const { setup, cleanup } = common; @@ -18,7 +18,7 @@ describe('df', function () { it('can get filesystems', async function () { // does not work on archlinux 8! - if (require('node:child_process').execSync('uname -a').toString().indexOf('-arch') !== -1) return; + if (child_process.execSync('uname -a').toString().indexOf('-arch') !== -1) return; const disks = await df.filesystems(); expect(disks).to.be.ok(); @@ -27,9 +27,9 @@ describe('df', function () { it('can get file', async function () { // does not work on archlinux 8! - if (require('node:child_process').execSync('uname -a').toString().indexOf('-arch') !== -1) return; + if (child_process.execSync('uname -a').toString().indexOf('-arch') !== -1) return; - const disks = await df.file(__dirname); + const disks = await df.file(import.meta.dirname); expect(disks).to.be.ok(); expect(disks.mountpoint).to.be('/home'); }); diff --git a/src/test/directoryserver-test.js b/src/test/directoryserver-test.js index 431b2a9b7..e64995243 100644 --- a/src/test/directoryserver-test.js +++ b/src/test/directoryserver-test.js @@ -1,22 +1,21 @@ /* jslint node:true */ + +import async from 'async'; +import * as common from './common.js'; +import constants from '../constants.js'; +import * as directoryServer from '../directoryserver.js'; +import expect from 'expect.js'; +import * as groups from '../groups.js'; +import ldap from 'ldapjs'; +import safe from 'safetydance'; +import speakeasy from 'speakeasy'; +import * as users from '../users.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const async = require('async'), - common = require('./common.js'), - constants = require('../constants.js'), - directoryServer = require('../directoryserver.js'), - expect = require('expect.js'), - groups = require('../groups.js'), - ldap = require('ldapjs'), - safe = require('safetydance'), - speakeasy = require('speakeasy'), - users = require('../users.js'); - async function ldapBind(dn, password) { return new Promise((resolve, reject) => { const client = ldap.createClient({ url: 'ldaps://127.0.0.1:' + constants.USER_DIRECTORY_LDAPS_PORT, tlsOptions: { rejectUnauthorized: false }}); @@ -55,7 +54,7 @@ async function ldapSearch(dn, opts, auth) { client.search(dn, opts, function (error, result) { if (error) return done(error); - let entries = []; + const entries = []; result.on('searchEntry', function (entry) { entries.push(entry.object); }); diff --git a/src/test/dns-providers-test.js b/src/test/dns-providers-test.js index d15a351b8..51d2f6a33 100644 --- a/src/test/dns-providers-test.js +++ b/src/test/dns-providers-test.js @@ -1,20 +1,19 @@ /* jslint node:true */ + +import * as common from './common.js'; +import * as dns from '../dns.js'; +import * as domains from '../domains.js'; +import expect from 'expect.js'; +import { DNS as GCDNS } from '@google-cloud/dns'; +import nock from 'nock'; +import * as _ from '../underscore.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global beforeEach:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - dns = require('../dns.js'), - domains = require('../domains.js'), - expect = require('expect.js'), - GCDNS = require('@google-cloud/dns').DNS, - nock = require('nock'), - _ = require('../underscore.js'); - describe('dns provider', function () { const { setup, cleanup, auditSource, domain } = common; const domainCopy = Object.assign({}, domain); // make a copy diff --git a/src/test/dns-test.js b/src/test/dns-test.js index cc2910018..72eaca225 100644 --- a/src/test/dns-test.js +++ b/src/test/dns-test.js @@ -1,14 +1,13 @@ /* global it:false */ + +import * as common from './common.js'; +import * as dns from '../dns.js'; +import expect from 'expect.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - dns = require('../dns.js'), - expect = require('expect.js'); - describe('DNS', function () { const { setup, cleanup, app, domain:domainObject } = common; diff --git a/src/test/docker-test.js b/src/test/docker-test.js index 1b8503f3d..3ab18b0bd 100644 --- a/src/test/docker-test.js +++ b/src/test/docker-test.js @@ -1,16 +1,15 @@ /* jslint node:true */ + +import * as common from './common.js'; +import * as docker from '../docker.js'; +import expect from 'expect.js'; +import nock from 'nock'; + /* global it:false */ /* global before:false */ /* global after:false */ /* global describe:false */ -'use strict'; - -const common = require('./common.js'), - docker = require('../docker.js'), - expect = require('expect.js'), - nock = require('nock'); - describe('docker', function () { const { setup, cleanup } = common; diff --git a/src/test/dockerproxy-test.js b/src/test/dockerproxy-test.js index 1c6cd0b58..9ded2e973 100644 --- a/src/test/dockerproxy-test.js +++ b/src/test/dockerproxy-test.js @@ -1,21 +1,19 @@ /* jslint node:true */ + +import child_process from 'node:child_process'; +import * as common from './common.js'; +import constants from '../constants.js'; +import * as dockerProxy from '../dockerproxy.js'; +import expect from 'expect.js'; +import nock from 'nock'; +import * as syslogServer from '../../syslog.js'; + /* global it:false */ /* global xit:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - - -const child_process = require('node:child_process'), - common = require('./common.js'), - constants = require('../constants.js'), - dockerProxy = require('../dockerproxy.js'), - expect = require('expect.js'), - nock = require('nock'), - syslogServer = require('../../syslog.js'); - const DOCKER = `docker -H tcp://172.18.0.1:${constants.DOCKER_PROXY_PORT} `; async function exec(cmd) { @@ -84,7 +82,7 @@ describe('Dockerproxy', function () { }); it('can use PUT to upload archive into a container', async function () { - const stdout = await exec(`${DOCKER} cp ${__dirname}/proxytestarchive.tar ${containerId}:/tmp/`); + const stdout = await exec(`${DOCKER} cp ${import.meta.dirname}/proxytestarchive.tar ${containerId}:/tmp/`); expect(stdout).to.be.empty(); }); diff --git a/src/test/dockerregistries-test.js b/src/test/dockerregistries-test.js index 3cdc9ecd6..519766d4b 100644 --- a/src/test/dockerregistries-test.js +++ b/src/test/dockerregistries-test.js @@ -1,17 +1,16 @@ /* jslint node:true */ + +import * as dockerRegistries from '../dockerregistries.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const dockerRegistries = require('../dockerregistries.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('Docker Registries', function () { const { setup, cleanup, auditSource } = common; diff --git a/src/test/domains-test.js b/src/test/domains-test.js index bf1fd356c..b5227969f 100644 --- a/src/test/domains-test.js +++ b/src/test/domains-test.js @@ -1,18 +1,17 @@ /* jslint node:true */ + +import apps from '../apps.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import * as domains from '../domains.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const apps = require('../apps.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - domains = require('../domains.js'), - expect = require('expect.js'), - safe = require('safetydance'); - describe('Domains', function () { const { setup, cleanup, domain, app, auditSource } = common; diff --git a/src/test/eventlog-test.js b/src/test/eventlog-test.js index eb628260a..7863ecf5b 100644 --- a/src/test/eventlog-test.js +++ b/src/test/eventlog-test.js @@ -1,18 +1,17 @@ /* jslint node:true */ + +import * as common from './common.js'; +import * as database from '../database.js'; +import eventlog from '../eventlog.js'; +import expect from 'expect.js'; +import * as notifications from '../notifications.js'; +import timers from 'timers/promises'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - database = require('../database.js'), - eventlog = require('../eventlog.js'), - expect = require('expect.js'), - notifications = require('../notifications.js'), - timers = require('timers/promises'); - describe('Eventlog', function () { const { setup, cleanup } = common; @@ -99,7 +98,7 @@ describe('Eventlog', function () { }); it('upsert with existing old entry succeeds', async function () { - let yesterday = new Date(); + const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); await database.query('INSERT INTO eventlog (id, action, sourceJson, dataJson, creationTime) VALUES (?, ?, ?, ?, ?)', [ 'anotherid', 'user.login2', JSON.stringify({ ip: '1.2.3.4' }), JSON.stringify({ appId: 'thatapp' }), yesterday ]); diff --git a/src/test/externalldap-test.js b/src/test/externalldap-test.js index 3c32adf65..a4f8c368e 100644 --- a/src/test/externalldap-test.js +++ b/src/test/externalldap-test.js @@ -1,20 +1,19 @@ /* global it:false */ + +import async from 'async'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import * as externalLdap from '../externalldap.js'; +import * as groups from '../groups.js'; +import ldap from 'ldapjs'; +import safe from 'safetydance'; +import * as users from '../users.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const async = require('async'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - externalLdap = require('../externalldap.js'), - groups = require('../groups.js'), - ldap = require('ldapjs'), - safe = require('safetydance'), - users = require('../users.js'); - const LDAP_SHARED_PASSWORD = 'validpassword'; const LDAP_SHARED_TOTP_TOKEN = '1234'; const LDAP_PORT = 4321; diff --git a/src/test/groups-test.js b/src/test/groups-test.js index bdff692f4..2ad6c7d77 100644 --- a/src/test/groups-test.js +++ b/src/test/groups-test.js @@ -1,17 +1,16 @@ /* jslint node:true */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import * as groups from '../groups.js'; +import safe from 'safetydance'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - groups = require('../groups.js'), - safe = require('safetydance'); - describe('Groups', function () { const { setup, cleanup, admin, user, auditSource, app } = common; diff --git a/src/test/ipaddr-test.js b/src/test/ipaddr-test.js index cdd9f71d0..bb293166f 100644 --- a/src/test/ipaddr-test.js +++ b/src/test/ipaddr-test.js @@ -1,9 +1,7 @@ -'use strict'; - /* global it, describe */ -const expect = require('expect.js'), - ipaddr = require('../ipaddr.js'); +import expect from 'expect.js'; +import * as ipaddr from '../ipaddr.js'; describe('ipaddr', function () { describe('IPv4', function () { diff --git a/src/test/janitor-test.js b/src/test/janitor-test.js index 26bc99c71..6d94f0126 100644 --- a/src/test/janitor-test.js +++ b/src/test/janitor-test.js @@ -1,16 +1,15 @@ /* jslint node:true */ + +import * as common from './common.js'; +import expect from 'expect.js'; +import * as janitor from '../janitor.js'; +import * as tokens from '../tokens.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - janitor = require('../janitor.js'), - tokens = require('../tokens.js'); - describe('janitor', function () { const { setup, cleanup } = common; diff --git a/src/test/ldapserver-test.js b/src/test/ldapserver-test.js index 971b67ca1..6fdc0abec 100644 --- a/src/test/ldapserver-test.js +++ b/src/test/ldapserver-test.js @@ -1,22 +1,21 @@ /* jslint node:true */ + +import * as addonConfigs from '../addonconfigs.js'; +import async from 'async'; +import * as common from './common.js'; +import constants from '../constants.js'; +import expect from 'expect.js'; +import * as groups from '../groups.js'; +import ldap from 'ldapjs'; +import * as ldapServer from '../ldapserver.js'; +import * as mail from '../mail.js'; +import safe from 'safetydance'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const addonConfigs = require('../addonconfigs.js'), - async = require('async'), - common = require('./common.js'), - constants = require('../constants.js'), - expect = require('expect.js'), - groups = require('../groups.js'), - ldap = require('ldapjs'), - ldapServer = require('../ldapserver.js'), - mail = require('../mail.js'), - safe = require('safetydance'); - async function ldapBind(dn, password) { return new Promise((resolve, reject) => { const client = ldap.createClient({ url: 'ldap://127.0.0.1:' + constants.LDAP_PORT }); @@ -45,7 +44,7 @@ async function ldapSearch(dn, opts) { client.search(dn, opts, function (error, result) { if (error) return done(error); - let entries = []; + const entries = []; result.on('searchEntry', function (entry) { entries.push(entry.object); }); @@ -87,7 +86,7 @@ describe('Ldap Server', function () { } ], done); - ldapServer._MOCK_APP = mockApp; + ldapServer._setMockApp(mockApp); }); after(function (done) { diff --git a/src/test/locks-test.js b/src/test/locks-test.js index 54026bc3d..45ee5b5f7 100644 --- a/src/test/locks-test.js +++ b/src/test/locks-test.js @@ -1,12 +1,10 @@ -'use strict'; - /* global it, describe, before, after */ -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'), - locks = require('../locks.js'); +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; +import locks from '../locks.js'; describe('Locks', function () { this.timeout(20000); diff --git a/src/test/logs-test.js b/src/test/logs-test.js index 5d7836a47..56899aaed 100644 --- a/src/test/logs-test.js +++ b/src/test/logs-test.js @@ -1,13 +1,12 @@ /* global it:false */ + +import expect from 'expect.js'; +import fs from 'node:fs'; +import * as logs from '../logs.js'; +import stream from 'node:stream'; + /* global describe:false */ -'use strict'; - -const expect = require('expect.js'), - fs = require('node:fs'), - logs = require('../logs.js'), - stream = require('node:stream'); - describe('log stream', function () { it('can create stream', function (done) { fs.writeFileSync('/tmp/test-input.log', '2022-10-09T15:19:48.740Z message', 'utf8'); diff --git a/src/test/mail-test.js b/src/test/mail-test.js index 55114e0c3..de73ff1a6 100644 --- a/src/test/mail-test.js +++ b/src/test/mail-test.js @@ -1,16 +1,15 @@ /* global it:false */ + +import * as common from './common.js'; +import BoxError from '../boxerror.js'; +import expect from 'expect.js'; +import * as mail from '../mail.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - BoxError = require('../boxerror.js'), - expect = require('expect.js'), - mail = require('../mail.js'), - safe = require('safetydance'); - describe('Mail', function () { const { setup, cleanup, domain, auditSource } = common; @@ -116,7 +115,6 @@ describe('Mail', function () { }); }); - describe('mailboxes', function () { it('add user mailbox succeeds', async function () { await mail.addMailbox('girish', domain.domain, { ownerId: 'uid-0', ownerType: mail.OWNERTYPE_USER, active: true, enablePop3: false, storageQuota: 0, messagesQuota: 0 }, auditSource); diff --git a/src/test/network-test.js b/src/test/network-test.js index bda2628ca..dcf58d7a8 100644 --- a/src/test/network-test.js +++ b/src/test/network-test.js @@ -1,18 +1,17 @@ /* global it:false */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import * as network from '../network.js'; +import paths from '../paths.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - fs = require('node:fs'), - network = require('../network.js'), - paths = require('../paths.js'), - safe = require('safetydance'); - describe('Network', function () { const { setup, cleanup, auditSource } = common; diff --git a/src/test/notifications-test.js b/src/test/notifications-test.js index 3fb2a9172..f03e08b2b 100644 --- a/src/test/notifications-test.js +++ b/src/test/notifications-test.js @@ -1,18 +1,17 @@ /* jslint node:true */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import * as notifications from '../notifications.js'; +import safe from 'safetydance'; +import timers from 'timers/promises'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - notifications = require('../notifications.js'), - safe = require('safetydance'), - timers = require('timers/promises'); - const EVENT_0 = { id: 'event_0', action: 'action', diff --git a/src/test/oidcclients-test.js b/src/test/oidcclients-test.js index c3a7dff81..d722050c6 100644 --- a/src/test/oidcclients-test.js +++ b/src/test/oidcclients-test.js @@ -1,12 +1,10 @@ /* global describe, before, it, after */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - oidcClients = require('../oidcclients.js'), - safe = require('safetydance'); +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import oidcClients from '../oidcclients.js'; +import safe from 'safetydance'; describe('OIDC Clients', function () { const { setup, cleanup } = common; diff --git a/src/test/openssl-test.js b/src/test/openssl-test.js index 46e302a3a..eba097b27 100644 --- a/src/test/openssl-test.js +++ b/src/test/openssl-test.js @@ -1,13 +1,12 @@ /* global it:false */ + +import BoxError from '../boxerror.js'; +import expect from 'expect.js'; +import * as openssl from '../openssl.js'; +import safe from 'safetydance'; + /* global describe:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - expect = require('expect.js'), - openssl = require('../openssl.js'), - safe = require('safetydance'); - describe('openssl', function () { describe('validateCertificate', function () { const foobarDomain = 'foobar.com'; diff --git a/src/test/progress-stream-test.js b/src/test/progress-stream-test.js index f9ea40b71..c1b9ac4f8 100644 --- a/src/test/progress-stream-test.js +++ b/src/test/progress-stream-test.js @@ -1,22 +1,21 @@ /* global it:false */ + +import expect from 'expect.js'; +import fs from 'node:fs'; +import ProgressStream from '../progress-stream.js'; +import stream from 'node:stream'; + /* global describe:false */ -'use strict'; - -const expect = require('expect.js'), - fs = require('node:fs'), - ProgressStream = require('../progress-stream.js'), - stream = require('node:stream'); - describe('progress stream', function () { it('can create stream', function (done) { - const input = fs.createReadStream(`${__dirname}/progress-stream-test.js`); + const input = fs.createReadStream(`${import.meta.dirname}/progress-stream-test.js`); const progress = new ProgressStream({ interval: 1000 }); const output = fs.createWriteStream('/dev/null'); stream.pipeline(input, progress, output, function (error) { expect(error).to.not.be.ok(); - const size = fs.statSync(`${__dirname}/progress-stream-test.js`).size; + const size = fs.statSync(`${import.meta.dirname}/progress-stream-test.js`).size; expect(progress.stats().transferred).to.be(size); done(); diff --git a/src/test/promise-retry-test.js b/src/test/promise-retry-test.js index 0a19147c8..ed31896a9 100644 --- a/src/test/promise-retry-test.js +++ b/src/test/promise-retry-test.js @@ -1,13 +1,12 @@ /* jslint node:true */ + +import expect from 'expect.js'; +import promiseRetry from '../promise-retry.js'; +import safe from 'safetydance'; + /* global it:false */ /* global describe:false */ -'use strict'; - -const expect = require('expect.js'), - promiseRetry = require('../promise-retry.js'), - safe = require('safetydance'); - describe('promiseRetry', function () { this.timeout(0); diff --git a/src/test/provision-test.js b/src/test/provision-test.js index 3e893b21e..aa885be20 100644 --- a/src/test/provision-test.js +++ b/src/test/provision-test.js @@ -1,18 +1,17 @@ /* global it:false */ + +import * as appstore from '../appstore.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import nock from 'nock'; +import * as provision from '../provision.js'; +import safe from 'safetydance'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const appstore = require('../appstore.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - nock = require('nock'), - provision = require('../provision.js'), - safe = require('safetydance'); - describe('Provision', function () { const { domainSetup, auditSource, cleanup } = common; diff --git a/src/test/reverseproxy-test.js b/src/test/reverseproxy-test.js index 1902c2463..b90b2a0b6 100644 --- a/src/test/reverseproxy-test.js +++ b/src/test/reverseproxy-test.js @@ -1,17 +1,16 @@ /* global it:false */ + +import * as common from './common.js'; +import * as domains from '../domains.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import paths from '../paths.js'; +import * as reverseProxy from '../reverseproxy.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - domains = require('../domains.js'), - expect = require('expect.js'), - fs = require('node:fs'), - paths = require('../paths.js'), - reverseProxy = require('../reverseproxy.js'); - describe('Reverse Proxy', function () { const { setup, cleanup, domain, auditSource, app } = common; const domainCopy = Object.assign({}, domain); diff --git a/src/test/server-test.js b/src/test/server-test.js index d57ed60f4..aa9433531 100644 --- a/src/test/server-test.js +++ b/src/test/server-test.js @@ -1,17 +1,16 @@ /* jslint node:true */ + +import constants from '../constants.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; +import * as server from '../server.js'; +import superagent from '@cloudron/superagent'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const constants = require('../constants.js'), - expect = require('expect.js'), - safe = require('safetydance'), - server = require('../server.js'), - superagent = require('@cloudron/superagent'); - const SERVER_URL = 'http://localhost:' + constants.PORT; describe('Server', function () { diff --git a/src/test/shell-test.js b/src/test/shell-test.js index cff995cb8..bd4c0f349 100644 --- a/src/test/shell-test.js +++ b/src/test/shell-test.js @@ -1,15 +1,16 @@ /* jslint node:true */ + +import BoxError from '../boxerror.js'; +import expect from 'expect.js'; +import path from 'node:path'; +import safe from 'safetydance'; +import shellModule from '../shell.js'; + +const shell = shellModule('test'); + /* global it:false */ /* global describe:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - expect = require('expect.js'), - path = require('node:path'), - safe = require('safetydance'), - shell = require('../shell.js')('test'); - describe('shell', function () { describe('spawn', function () { it('can run valid program', async function () { @@ -52,7 +53,7 @@ describe('shell', function () { }); it('can sudo valid program', async function () { - const RELOAD_NGINX_CMD = path.join(__dirname, '../src/scripts/restartservice.sh'); + const RELOAD_NGINX_CMD = path.join(import.meta.dirname, '../src/scripts/restartservice.sh'); const [error] = await safe(shell.sudo([ RELOAD_NGINX_CMD, 'nginx' ], {})); expect(error).to.be.ok(); }); diff --git a/src/test/storage-provider-test.js b/src/test/storage-provider-test.js index 56f4e3b9b..bc275ddca 100644 --- a/src/test/storage-provider-test.js +++ b/src/test/storage-provider-test.js @@ -1,26 +1,25 @@ /* global it:false */ + +import * as backupSites from '../backupsites.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import consumers from 'node:stream/consumers'; +import { execSync } from 'node:child_process'; +import expect from 'expect.js'; +import * as filesystem from '../storage/filesystem.js'; +import fs from 'node:fs'; +import * as gcs from '../storage/gcs.js'; +import os from 'node:os'; +import path from 'node:path'; +import * as s3 from '../storage/s3.js'; +import safe from 'safetydance'; +import stream from 'stream/promises'; + /* global describe:false */ /* global before:false */ /* global after:false */ /* global xit:false */ -'use strict'; - -const backupSites = require('../backupsites.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - consumers = require('node:stream/consumers'), - execSync = require('node:child_process').execSync, - expect = require('expect.js'), - filesystem = require('../storage/filesystem.js'), - fs = require('node:fs'), - gcs = require('../storage/gcs.js'), - os = require('node:os'), - path = require('node:path'), - s3 = require('../storage/s3.js'), - safe = require('safetydance'), - stream = require('stream/promises'); - describe('Storage', function () { const { setup, cleanup, getDefaultBackupSite, auditSource } = common; @@ -62,7 +61,7 @@ describe('Storage', function () { }); it('can upload', async function () { - const sourceFile = path.join(__dirname, 'storage/data/test.txt'); + const sourceFile = path.join(import.meta.dirname, 'storage/data/test.txt'); const sourceStream = fs.createReadStream(sourceFile); const destFile = path.join(gBackupConfig.backupDir, gBackupConfig.prefix, '/uploadtest/test.txt'); const uploader = await filesystem.upload(gBackupConfig, {}, 'uploadtest/test.txt'); @@ -73,7 +72,7 @@ describe('Storage', function () { }); xit('upload waits for empty file to be created', async function () { - const sourceFile = path.join(__dirname, 'storage/data/empty'); + const sourceFile = path.join(import.meta.dirname, 'storage/data/empty'); const sourceStream = fs.createReadStream(sourceFile); const destFile = path.join(gBackupConfig.backupDir, gBackupConfig.prefix, '/uploadtest/empty'); const uploader = await filesystem.upload(gBackupConfig, {}, destFile); @@ -84,7 +83,7 @@ describe('Storage', function () { }); it('upload unlinks old file', async function () { - const sourceFile = path.join(__dirname, 'storage/data/test.txt'); + const sourceFile = path.join(import.meta.dirname, 'storage/data/test.txt'); const sourceStream = fs.createReadStream(sourceFile); const destFile = path.join(gBackupConfig.backupDir, gBackupConfig.prefix, '/uploadtest/test.txt'); const oldStat = fs.statSync(destFile); @@ -111,7 +110,7 @@ describe('Storage', function () { }); it('list dir lists the source dir', async function () { - const sourceDir = path.join(__dirname, 'storage'); + const sourceDir = path.join(import.meta.dirname, 'storage'); execSync(`cp -r ${sourceDir} ${gBackupConfig.backupDir}/${gBackupConfig.prefix}`, { encoding: 'utf8' }); let allFiles = [], marker = null; @@ -242,7 +241,7 @@ describe('Storage', function () { }); it('can upload', async function () { - const sourceFile = path.join(__dirname, 'storage/data/test.txt'); + const sourceFile = path.join(import.meta.dirname, 'storage/data/test.txt'); const sourceStream = fs.createReadStream(sourceFile); const destKey = 'uploadtest/test.txt'; const uploader = await s3.upload(backupConfig, {}, destKey); @@ -276,7 +275,7 @@ describe('Storage', function () { fs.writeFileSync(path.join(bucketPath, 'uploadtest/C++.gitignore'), 'special', 'utf8'); await s3.copyDir(backupConfig, {}, 'uploadtest', 'uploadtest-copy', () => {}); - const sourceFile = path.join(__dirname, 'storage/data/test.txt'); + const sourceFile = path.join(import.meta.dirname, 'storage/data/test.txt'); expect(fs.statSync(path.join(bucketPath, 'uploadtest-copy/test.txt')).size).to.be(fs.statSync(sourceFile).size); expect(fs.statSync(path.join(bucketPath, 'uploadtest-copy/C++.gitignore')).size).to.be(7); }); @@ -379,7 +378,7 @@ describe('Storage', function () { }); it('can upload', async function () { - const sourceFile = path.join(__dirname, 'storage/data/test.txt'); + const sourceFile = path.join(import.meta.dirname, 'storage/data/test.txt'); const sourceStream = fs.createReadStream(sourceFile); const destKey = 'uploadtest/test.txt'; const uploader = await gcs.upload(backupConfig, {}, destKey); @@ -413,7 +412,7 @@ describe('Storage', function () { fs.writeFileSync(path.join(bucketPath, 'uploadtest/C++.gitignore'), 'special', 'utf8'); await gcs.copyDir(backupConfig, {}, 'uploadtest', 'uploadtest-copy', () => {}); - const sourceFile = path.join(__dirname, 'storage/data/test.txt'); + const sourceFile = path.join(import.meta.dirname, 'storage/data/test.txt'); expect(fs.statSync(path.join(bucketPath, 'uploadtest-copy/test.txt')).size).to.be(fs.statSync(sourceFile).size); expect(fs.statSync(path.join(bucketPath, 'uploadtest-copy/C++.gitignore')).size).to.be(7); }); diff --git a/src/test/superagent-test.js b/src/test/superagent-test.js index 8601e070d..eca5954d4 100644 --- a/src/test/superagent-test.js +++ b/src/test/superagent-test.js @@ -1,12 +1,11 @@ /* global it:false */ + +import expect from 'expect.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; + /* global describe:false */ -'use strict'; - -const expect = require('expect.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - describe('Superagent', function () { it('can get URL', async function () { await superagent.get('https://www.cloudron.io').set('User-Agent', 'Mozilla').timeout(10*1000); diff --git a/src/test/syncer-test.js b/src/test/syncer-test.js index ab1f62df3..ba73442d0 100644 --- a/src/test/syncer-test.js +++ b/src/test/syncer-test.js @@ -1,20 +1,20 @@ /* global it:false */ + +import * as common from './common.js'; +const { createTree } = common; +import DataLayout from '../datalayout.js'; +import { execSync } from 'node:child_process'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import paths from '../paths.js'; +import safe from 'safetydance'; +import * as syncer from '../syncer.js'; + /* global describe:false */ /* global before:false */ -'use strict'; - -const createTree = require('./common.js').createTree, - DataLayout = require('../datalayout.js'), - execSync = require('node:child_process').execSync, - expect = require('expect.js'), - fs = require('node:fs'), - os = require('node:os'), - path = require('node:path'), - paths = require('../paths.js'), - safe = require('safetydance'), - syncer = require('../syncer.js'); - const gTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'syncer-test')), gCacheFile = path.join(paths.BACKUP_INFO_DIR, 'syncer-test.sync.cache'); diff --git a/src/test/syslog-test.js b/src/test/syslog-test.js index e862a3ac9..b03383c05 100644 --- a/src/test/syslog-test.js +++ b/src/test/syslog-test.js @@ -1,20 +1,18 @@ #!/usr/bin/env node - /* global it:false */ + +import expect from 'expect.js'; +import fs from 'node:fs'; +import net from 'node:net'; +import path from 'node:path'; +import paths from '../paths.js'; +import safe from 'safetydance'; +import * as syslogServer from '../../syslog.js'; +import timers from 'timers/promises'; + /* global describe:false */ /* global after:false */ -'use strict'; - -const expect = require('expect.js'), - fs = require('node:fs'), - net = require('node:net'), - path = require('node:path'), - paths = require('../paths.js'), - safe = require('safetydance'), - syslogServer = require('../../syslog.js'), - timers = require('timers/promises'); - async function sendMessage(message) { const client = net.createConnection(paths.SYSLOG_SOCKET_FILE); diff --git a/src/test/system-test.js b/src/test/system-test.js index 26c53dffe..b9a638965 100644 --- a/src/test/system-test.js +++ b/src/test/system-test.js @@ -1,16 +1,16 @@ /* jslint node:true */ + +import child_process from 'node:child_process'; +import * as common from './common.js'; +import expect from 'expect.js'; +import nock from 'nock'; +import * as system from '../system.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - nock = require('nock'), - system = require('../system.js'); - describe('System', function () { const { setup, cleanup } = common; @@ -22,7 +22,7 @@ describe('System', function () { it('can get filesystems', async function () { // does not work on archlinux 8! - if (require('node:child_process').execSync('uname -a').toString().indexOf('-arch') !== -1) return; + if (child_process.execSync('uname -a').toString().indexOf('-arch') !== -1) return; const filesystems = await system.getFilesystems(); expect(filesystems).to.be.ok(); @@ -31,7 +31,7 @@ describe('System', function () { it('can get swaps', async function () { // does not work on archlinux 8! - if (require('node:child_process').execSync('uname -a').toString().indexOf('-arch') !== -1) return; + if (child_process.execSync('uname -a').toString().indexOf('-arch') !== -1) return; const swaps = await system.getSwaps(); expect(swaps).to.be.ok(); @@ -40,7 +40,7 @@ describe('System', function () { it('can check for disk space', async function () { // does not work on archlinux 8! - if (require('node:child_process').execSync('uname -a').toString().indexOf('-arch') !== -1) return; + if (child_process.execSync('uname -a').toString().indexOf('-arch') !== -1) return; await system.checkDiskSpace(); }); diff --git a/src/test/tasks-test.js b/src/test/tasks-test.js index 4f8b74756..c6bac080c 100644 --- a/src/test/tasks-test.js +++ b/src/test/tasks-test.js @@ -1,20 +1,19 @@ /* jslint node:true */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import fs from 'node:fs'; +import paths from '../paths.js'; +import safe from 'safetydance'; +import tasks from '../tasks.js'; +import * as _ from '../underscore.js'; + /* global it:false */ /* global before:false */ /* global after:false */ /* global describe:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - fs = require('node:fs'), - paths = require('../paths.js'), - safe = require('safetydance'), - tasks = require('../tasks.js'), - _ = require('../underscore.js'); - describe('task', function () { const { setup, cleanup } = common; diff --git a/src/test/tokens-test.js b/src/test/tokens-test.js index 421e9cd75..d6517593e 100644 --- a/src/test/tokens-test.js +++ b/src/test/tokens-test.js @@ -1,18 +1,17 @@ /* jslint node:true */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import oidcClients from '../oidcclients.js'; +import safe from 'safetydance'; +import * as tokens from '../tokens.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - oidcClients = require('../oidcclients.js'), - safe = require('safetydance'), - tokens = require('../tokens.js'); - describe('Tokens', function () { const { setup, cleanup } = common; diff --git a/src/test/translations-test.js b/src/test/translations-test.js index 0ff0832f2..f87750302 100644 --- a/src/test/translations-test.js +++ b/src/test/translations-test.js @@ -1,12 +1,11 @@ /* jslint node:true */ + +import expect from 'expect.js'; +import * as translations from '../translations.js'; + /* global it:false */ /* global describe:false */ -'use strict'; - -const expect = require('expect.js'), - translations = require('../translations.js'); - describe('translation', function () { describe('translate', function () { diff --git a/src/test/underscore-test.js b/src/test/underscore-test.js index 373cabb87..aa9f4035e 100644 --- a/src/test/underscore-test.js +++ b/src/test/underscore-test.js @@ -1,9 +1,7 @@ -'use strict'; - /* global it, describe, before, after */ -const expect = require('expect.js'), - _ = require('../underscore.js'); +import expect from 'expect.js'; +import * as _ from '../underscore.js'; describe('Underscore', function () { it('pick', function () { diff --git a/src/test/updater-test.js b/src/test/updater-test.js index e9aa6fc9b..964740600 100644 --- a/src/test/updater-test.js +++ b/src/test/updater-test.js @@ -1,21 +1,20 @@ /* global it:false */ + +import apps from '../apps.js'; +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import constants from '../constants.js'; +import expect from 'expect.js'; +import nock from 'nock'; +import paths from '../paths.js'; +import safe from 'safetydance'; +import semver from 'semver'; +import * as updater from '../updater.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const apps = require('../apps.js'), - BoxError = require('../boxerror.js'), - common = require('./common.js'), - constants = require('../constants.js'), - expect = require('expect.js'), - nock = require('nock'), - paths = require('../paths.js'), - safe = require('safetydance'), - semver = require('semver'), - updater = require('../updater.js'); - const UPDATE_VERSION = semver.inc(constants.VERSION, 'major'); describe('updater', function () { diff --git a/src/test/user-directory-test.js b/src/test/user-directory-test.js index 4aad4c42a..efd2c911f 100644 --- a/src/test/user-directory-test.js +++ b/src/test/user-directory-test.js @@ -1,16 +1,15 @@ /* global it:false */ + +import * as common from './common.js'; +import expect from 'expect.js'; +import oidcClients from '../oidcclients.js'; +import * as tokens from '../tokens.js'; +import * as userDirectory from '../user-directory.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const common = require('./common.js'), - expect = require('expect.js'), - oidcClients = require('../oidcclients.js'), - tokens = require('../tokens.js'), - userDirectory = require('../user-directory.js'); - describe('User Directory', function () { const { setup, cleanup, admin, auditSource } = common; diff --git a/src/test/users-test.js b/src/test/users-test.js index 30544c266..3a6f97bfd 100644 --- a/src/test/users-test.js +++ b/src/test/users-test.js @@ -1,18 +1,17 @@ /* global it:false */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import safe from 'safetydance'; +import speakeasy from 'speakeasy'; +import * as users from '../users.js'; +import * as _ from '../underscore.js'; + /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - safe = require('safetydance'), - speakeasy = require('speakeasy'), - users = require('../users.js'), - _ = require('../underscore.js'); - describe('User', function () { const { domainSetup, cleanup, admin, user, auditSource, checkMails, clearMailQueue } = common; diff --git a/src/test/validator-test.js b/src/test/validator-test.js index 5a3c74c61..410b22a7b 100644 --- a/src/test/validator-test.js +++ b/src/test/validator-test.js @@ -1,9 +1,7 @@ -'use strict'; - /* global it, describe, before, after */ -const expect = require('expect.js'), - validator = require('../validator.js'); +import expect from 'expect.js'; +import * as validator from '../validator.js'; describe('Validator', function () { const goodEmails = [ diff --git a/src/test/volumes-test.js b/src/test/volumes-test.js index 56f26a218..a4135546d 100644 --- a/src/test/volumes-test.js +++ b/src/test/volumes-test.js @@ -1,18 +1,17 @@ /* jslint node:true */ + +import BoxError from '../boxerror.js'; +import * as common from './common.js'; +import expect from 'expect.js'; +import paths from '../paths.js'; +import safe from 'safetydance'; +import * as volumes from '../volumes.js'; + /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ -'use strict'; - -const BoxError = require('../boxerror.js'), - common = require('./common.js'), - expect = require('expect.js'), - paths = require('../paths.js'), - safe = require('safetydance'), - volumes = require('../volumes.js'); - describe('Volumes', function () { const { setup, cleanup, auditSource } = common; diff --git a/src/tokens.js b/src/tokens.js index 6f97be74a..b0094a0c7 100644 --- a/src/tokens.js +++ b/src/tokens.js @@ -1,35 +1,30 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import hat from './hat.js'; +import * as ipaddr from './ipaddr.js'; +import safe from 'safetydance'; -exports = module.exports = { +const SCOPES = ['*'];//, 'apps', 'domains']; + +export { add, get, update, del, - delByAccessToken, delExpired, delByUserIdAndType, - listByUserId, getByAccessToken, - hasScope, - isIpAllowedSync, - - SCOPES: ['*']//, 'apps', 'domains'], + SCOPES, }; const TOKENS_FIELDS = [ 'id', 'accessToken', 'identifier', 'clientId', 'scopeJson', 'expires', 'name', 'lastUsedTime', 'allowedIpRanges' ].join(','); -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - database = require('./database.js'), - hat = require('./hat.js'), - ipaddr = require('./ipaddr.js'), - safe = require('safetydance'); - const gParsedRangesCache = new Map(); // indexed by token.id function postProcess(result) { @@ -53,7 +48,7 @@ function validateScope(scope) { assert.strictEqual(typeof scope, 'object'); for (const key in scope) { - if (exports.SCOPES.indexOf(key) === -1) return new BoxError(BoxError.BAD_FIELD, `Unkown token scope ${key}. Valid scopes are ${exports.SCOPES.join(',')}`); + if (SCOPES.indexOf(key) === -1) return new BoxError(BoxError.BAD_FIELD, `Unkown token scope ${key}. Valid scopes are ${SCOPES.join(',')}`); if (scope[key] !== 'rw' && scope[key] !== 'r') return new BoxError(BoxError.BAD_FIELD, `Unkown token scope value ${scope[key]} for ${key}. Valid values are 'rw' or 'r'`); } diff --git a/src/translations.js b/src/translations.js index 9290792e2..fd08913b0 100644 --- a/src/translations.js +++ b/src/translations.js @@ -1,19 +1,19 @@ -'use strict'; +import assert from 'node:assert'; +import * as cloudron from './cloudron.js'; +import debugModule from 'debug'; +import fs from 'node:fs'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; -exports = module.exports = { +const debug = debugModule('box:translation'); + +export { translate, getTranslations, listLanguages }; -const assert = require('node:assert'), - cloudron = require('./cloudron.js'), - debug = require('debug')('box:translation'), - fs = require('node:fs'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'); - // to be used together with getTranslations() => { translations, fallback } function translate(input, assets) { assert.strictEqual(typeof input, 'string'); diff --git a/src/underscore.js b/src/underscore.js index ef5a85dca..5457098eb 100644 --- a/src/underscore.js +++ b/src/underscore.js @@ -1,6 +1,6 @@ -'use strict'; +import assert from 'node:assert'; -exports = module.exports = { +export { pick, omit, intersection, @@ -9,7 +9,6 @@ exports = module.exports = { }; // IMPORTANT: this file is required from the migration logic. avoid requires -const assert = require('node:assert'); // note: returns shallow copy. use structuredClone() on top to get a deep copy function pick(obj, keys) { diff --git a/src/updater.js b/src/updater.js index e3bfc42c8..15626b26c 100644 --- a/src/updater.js +++ b/src/updater.js @@ -1,6 +1,35 @@ -'use strict'; +import apps from './apps.js'; +import * as appstore from './appstore.js'; +import assert from 'node:assert'; +import AuditSource from './auditsource.js'; +import BoxError from './boxerror.js'; +import * as backupSites from './backupsites.js'; +import * as backuptask from './backuptask.js'; +import * as community from './community.js'; +import constants from './constants.js'; +import * as cron from './cron.js'; +import { CronTime } from 'cron'; +import crypto from 'node:crypto'; +import debugModule from 'debug'; +import * as df from './df.js'; +import eventlog from './eventlog.js'; +import fs from 'node:fs'; +import locks from './locks.js'; +import * as notifications from './notifications.js'; +import os from 'node:os'; +import path from 'node:path'; +import paths from './paths.js'; +import promiseRetry from './promise-retry.js'; +import safe from 'safetydance'; +import semver from 'semver'; +import * as settings from './settings.js'; +import shellModule from './shell.js'; +import tasks from './tasks.js'; -exports = module.exports = { +const debug = debugModule('box:updater'); +const shell = shellModule('updater'); + +export { setAutoupdatePattern, getAutoupdatePattern, @@ -18,36 +47,8 @@ exports = module.exports = { getBoxUpdate, }; -const apps = require('./apps.js'), - appstore = require('./appstore.js'), - assert = require('node:assert'), - AuditSource = require('./auditsource.js'), - BoxError = require('./boxerror.js'), - backupSites = require('./backupsites.js'), - backuptask = require('./backuptask.js'), - community = require('./community.js'), - constants = require('./constants.js'), - cron = require('./cron.js'), - { CronTime } = require('cron'), - crypto = require('node:crypto'), - debug = require('debug')('box:updater'), - df = require('./df.js'), - eventlog = require('./eventlog.js'), - fs = require('node:fs'), - locks = require('./locks.js'), - notifications = require('./notifications.js'), - os = require('node:os'), - path = require('node:path'), - paths = require('./paths.js'), - promiseRetry = require('./promise-retry.js'), - safe = require('safetydance'), - semver = require('semver'), - settings = require('./settings.js'), - shell = require('./shell.js')('updater'), - tasks = require('./tasks.js'); - -const RELEASES_PUBLIC_KEY = path.join(__dirname, 'releases.gpg'); -const UPDATE_CMD = path.join(__dirname, 'scripts/update.sh'); +const RELEASES_PUBLIC_KEY = path.join(import.meta.dirname, 'releases.gpg'); +const UPDATE_CMD = path.join(import.meta.dirname, 'scripts/update.sh'); async function setAutoupdatePattern(pattern) { assert.strictEqual(typeof pattern, 'string'); diff --git a/src/user-directory.js b/src/user-directory.js index 7a8cffce0..35a0772f4 100644 --- a/src/user-directory.js +++ b/src/user-directory.js @@ -1,21 +1,21 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import constants from './constants.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import oidcClients from './oidcclients.js'; +import * as oidcServer from './oidcserver.js'; +import * as settings from './settings.js'; +import * as tokens from './tokens.js'; +import * as users from './users.js'; -exports = module.exports = { +const debug = debugModule('box:user-directory'); + +export { getProfileConfig, setProfileConfig }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - constants = require('./constants.js'), - debug = require('debug')('box:user-directory'), - eventlog = require('./eventlog.js'), - oidcClients = require('./oidcclients.js'), - oidcServer = require('./oidcserver.js'), - settings = require('./settings.js'), - tokens = require('./tokens.js'), - users = require('./users.js'); - async function getProfileConfig() { const value = await settings.getJson(settings.PROFILE_CONFIG_KEY); return value || { lockUserProfiles: false, mandatory2FA: false }; diff --git a/src/users.js b/src/users.js index bc28cf043..3da12906c 100644 --- a/src/users.js +++ b/src/users.js @@ -1,12 +1,48 @@ -'use strict'; +import * as appPasswords from './apppasswords.js'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import constants from './constants.js'; +import * as dashboard from './dashboard.js'; +import * as database from './database.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import * as externalLdap from './externalldap.js'; +import hat from './hat.js'; +import * as mail from './mail.js'; +import * as mailer from './mailer.js'; +import mysql from 'mysql2'; +import * as notifications from './notifications.js'; +import oidcClients from './oidcclients.js'; +import * as passkeys from './passkeys.js'; +import qrcode from 'qrcode'; +import safe from 'safetydance'; +import * as settings from './settings.js'; +import speakeasy from 'speakeasy'; +import * as tokens from './tokens.js'; +import * as translations from './translations.js'; +import { UAParser as uaParser } from 'ua-parser-js'; +import * as userDirectory from './user-directory.js'; +import superagent from '@cloudron/superagent'; +import util from 'node:util'; +import * as validator from './validator.js'; +import * as _ from './underscore.js'; -exports = module.exports = { +const debug = debugModule('box:user'); + +const AP_MAIL = 'mail'; +const AP_WEBADMIN = 'webadmin'; +const ROLE_ADMIN = 'admin'; +const ROLE_USER = 'user'; +const ROLE_USER_MANAGER = 'usermanager'; +const ROLE_MAIL_MANAGER = 'mailmanager'; +const ROLE_OWNER = 'owner'; + +export { removePrivateFields, - add, createOwner, isActivated, - list, listPaged, get, @@ -17,87 +53,41 @@ exports = module.exports = { getOwner, getAdmins, getSuperadmins, - verifyWithId, verifyWithUsername, verifyWithEmail, - setPassword, setGhost, updateProfile, update, - del, - setTwoFactorAuthenticationSecret, enableTwoFactorAuthentication, disableTwoFactorAuthentication, - sendPasswordResetByIdentifier, - getPasswordResetLink, sendPasswordResetEmail, - getInviteLink, sendInviteEmail, - notifyLoginLocation, - setupAccount, - setAvatar, getAvatar, - getBackgroundImage, setBackgroundImage, - setNotificationConfig, - resetSources, - parseDisplayName, - - AP_MAIL: 'mail', - AP_WEBADMIN: 'webadmin', - - ROLE_ADMIN: 'admin', - ROLE_USER: 'user', - ROLE_USER_MANAGER: 'usermanager', - ROLE_MAIL_MANAGER: 'mailmanager', - ROLE_OWNER: 'owner', + AP_MAIL, + AP_WEBADMIN, + ROLE_ADMIN, + ROLE_USER, + ROLE_USER_MANAGER, + ROLE_MAIL_MANAGER, + ROLE_OWNER, compareRoles, }; -const appPasswords = require('./apppasswords.js'), - assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - constants = require('./constants.js'), - dashboard = require('./dashboard.js'), - database = require('./database.js'), - debug = require('debug')('box:user'), - eventlog = require('./eventlog.js'), - externalLdap = require('./externalldap.js'), - hat = require('./hat.js'), - mail = require('./mail.js'), - mailer = require('./mailer.js'), - mysql = require('mysql2'), - notifications = require('./notifications'), - oidcClients = require('./oidcclients.js'), - passkeys = require('./passkeys.js'), - qrcode = require('qrcode'), - safe = require('safetydance'), - settings = require('./settings.js'), - speakeasy = require('speakeasy'), - tokens = require('./tokens.js'), - translations = require('./translations.js'), - uaParser = require('ua-parser-js'), - userDirectory = require('./user-directory.js'), - superagent = require('@cloudron/superagent'), - util = require('node:util'), - validator = require('./validator.js'), - _ = require('./underscore.js'); - // the avatar and backgroundImage fields are special and not added here to reduce response sizes const USERS_FIELDS = [ 'id', 'username', 'email', 'fallbackEmail', 'password', 'salt', 'creationTime', 'inviteToken', 'resetToken', 'displayName', 'language', 'twoFactorAuthenticationEnabled', 'twoFactorAuthenticationSecret', 'active', 'source', 'role', 'resetTokenCreationTime', 'loginLocationsJson', 'notificationConfigJson', @@ -202,7 +192,7 @@ function removePrivateFields(user) { function validateRole(role) { assert.strictEqual(typeof role, 'string'); - const ORDERED_ROLES = [ exports.ROLE_USER, exports.ROLE_USER_MANAGER, exports.ROLE_MAIL_MANAGER, exports.ROLE_ADMIN, exports.ROLE_OWNER ]; + const ORDERED_ROLES = [ ROLE_USER, ROLE_USER_MANAGER, ROLE_MAIL_MANAGER, ROLE_ADMIN, ROLE_OWNER ]; if (ORDERED_ROLES.includes(role)) return null; @@ -213,7 +203,7 @@ function compareRoles(role1, role2) { assert.strictEqual(typeof role1, 'string'); assert.strictEqual(typeof role2, 'string'); - const ORDERED_ROLES = [ exports.ROLE_USER, exports.ROLE_USER_MANAGER, exports.ROLE_MAIL_MANAGER, exports.ROLE_ADMIN, exports.ROLE_OWNER ]; + const ORDERED_ROLES = [ ROLE_USER, ROLE_USER_MANAGER, ROLE_MAIL_MANAGER, ROLE_ADMIN, ROLE_OWNER ]; const roleInt1 = ORDERED_ROLES.indexOf(role1); const roleInt2 = ORDERED_ROLES.indexOf(role2); @@ -235,7 +225,7 @@ async function add(email, data, auditSource) { let { username, password } = data; let fallbackEmail = data.fallbackEmail || ''; const source = data.source || ''; // empty is local user - const role = data.role || exports.ROLE_USER; + const role = data.role || ROLE_USER; const notificationConfig = 'notificationConfig' in data ? data.notificationConfig : null; let error; @@ -547,20 +537,20 @@ async function listByRole(role) { } async function getOwner() { - const owners = await listByRole(exports.ROLE_OWNER); + const owners = await listByRole(ROLE_OWNER); if (owners.length === 0) return null; return owners[0]; } async function getAdmins() { - const owners = await listByRole(exports.ROLE_OWNER); - const admins = await listByRole(exports.ROLE_ADMIN); + const owners = await listByRole(ROLE_OWNER); + const admins = await listByRole(ROLE_ADMIN); return owners.concat(admins); } async function getSuperadmins() { - return await listByRole(exports.ROLE_OWNER); + return await listByRole(ROLE_OWNER); } async function setGhost(user, password, expiresAt) { @@ -875,7 +865,7 @@ async function createOwner(email, username, password, displayName, auditSource) if (activated) throw new BoxError(BoxError.ALREADY_EXISTS, 'Cloudron already activated'); const notificationConfig = [notifications.TYPE_BACKUP_FAILED, notifications.TYPE_CERTIFICATE_RENEWAL_FAILED, notifications.TYPE_MANUAL_APP_UPDATE_NEEDED, notifications.TYPE_APP_DOWN, notifications.TYPE_CLOUDRON_UPDATE_FAILED ]; - return await add(email, { username, password, fallbackEmail: '', displayName, role: exports.ROLE_OWNER, notificationConfig }, auditSource); + return await add(email, { username, password, fallbackEmail: '', displayName, role: ROLE_OWNER, notificationConfig }, auditSource); } async function getInviteLink(user, auditSource) { diff --git a/src/validator.js b/src/validator.js index 657e0b5b4..0db0c70a0 100644 --- a/src/validator.js +++ b/src/validator.js @@ -1,11 +1,9 @@ -'use strict'; +import assert from 'node:assert'; -exports = module.exports = { +export { isEmail }; -const assert = require('node:assert'); - // this currently does not match: "john.doe"@example.com, user@[192.168.1.1], john.doe(comment)@example.com or 用户@例子.世界 function isEmail(email) { assert.strictEqual(typeof email, 'string'); diff --git a/src/volumes.js b/src/volumes.js index 4f44d490d..d78e4464c 100644 --- a/src/volumes.js +++ b/src/volumes.js @@ -1,6 +1,20 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import crypto from 'node:crypto'; +import * as database from './database.js'; +import debugModule from 'debug'; +import eventlog from './eventlog.js'; +import mounts from './mounts.js'; +import path from 'node:path'; +import paths from './paths.js'; +import safe from 'safetydance'; +import services from './services.js'; -exports = module.exports = { +const debug = debugModule('box:volumes'); + +const _validateHostPath = validateHostPath; + +export { add, get, update, @@ -9,25 +23,10 @@ exports = module.exports = { remount, getStatus, removePrivateFields, - mountAll, - - // exported for testing - _validateHostPath: validateHostPath + _validateHostPath, }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - crypto = require('node:crypto'), - database = require('./database.js'), - debug = require('debug')('box:volumes'), - eventlog = require('./eventlog.js'), - mounts = require('./mounts.js'), - path = require('node:path'), - paths = require('./paths.js'), - safe = require('safetydance'), - services = require('./services.js'); - const VOLUMES_FIELDS = [ 'id', 'name', 'hostPath', 'creationTime', 'mountType', 'mountOptionsJson' ].join(','); function postProcess(result) { diff --git a/src/wellknown.js b/src/wellknown.js index ad53c9669..d9f4ab47d 100644 --- a/src/wellknown.js +++ b/src/wellknown.js @@ -1,21 +1,19 @@ -'use strict'; +import assert from 'node:assert'; +import BoxError from './boxerror.js'; +import * as dashboard from './dashboard.js'; +import * as domains from './domains.js'; +import ejs from 'ejs'; +import fs from 'node:fs'; +import * as mail from './mail.js'; +import * as mailServer from './mailserver.js'; +import safe from 'safetydance'; +import superagent from '@cloudron/superagent'; -exports = module.exports = { +export { get }; -const assert = require('node:assert'), - BoxError = require('./boxerror.js'), - dashboard = require('./dashboard.js'), - domains = require('./domains.js'), - ejs = require('ejs'), - fs = require('node:fs'), - mail = require('./mail.js'), - mailServer = require('./mailserver.js'), - safe = require('safetydance'), - superagent = require('@cloudron/superagent'); - -const MAIL_AUTOCONFIG_EJS = fs.readFileSync(__dirname + '/autoconfig.xml.ejs', { encoding: 'utf8' }); +const MAIL_AUTOCONFIG_EJS = fs.readFileSync(import.meta.dirname + '/autoconfig.xml.ejs', { encoding: 'utf8' }); async function get(domain, location) { assert.strictEqual(typeof domain, 'string'); diff --git a/syslog.js b/syslog.js index e83c6378c..f6ea6acff 100755 --- a/syslog.js +++ b/syslog.js @@ -1,19 +1,19 @@ #!/usr/bin/env node -'use strict'; +import debugModule from 'debug'; +import fs from 'node:fs'; +import net from 'node:net'; +import path from 'node:path'; +import paths from './src/paths.js'; +import util from 'node:util'; -exports = module.exports = { +const debug = debugModule('syslog:server'); + +export { start, stop }; -const debug = require('debug')('syslog:server'), - fs = require('node:fs'), - net = require('node:net'), - path = require('node:path'), - paths = require('./src/paths.js'), - util = require('node:util'); - let gServer = null; // https://docs.docker.com/engine/logging/drivers/syslog/ @@ -97,6 +97,6 @@ async function main() { }); } -if (require.main === module) { +if (process.argv[1] === import.meta.filename) { main(); }