Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions scripts/api/scheduleStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const CRON_FIELD_RANGES = [
{ min: 0, max: 23 }, // hour
{ min: 1, max: 31 }, // day of month
{ min: 1, max: 12 }, // month
{ min: 0, max: 7 } // day of week (7 == Sunday)
{ min: 0, max: 7 } // day of week (7 == Sunday)
]

function validateField(expr, { min, max }) {
Expand Down Expand Up @@ -101,10 +101,9 @@ export function writeSchedule(projectRoot, patch) {

if ('cron' in patch) {
if (typeof patch.cron !== 'string' || !isValidCron(patch.cron)) {
throw Object.assign(
new Error('Invalid cron expression (5 fields, e.g. "0 9 * * *").'),
{ code: 'BAD_REQUEST' }
)
throw Object.assign(new Error('Invalid cron expression (5 fields, e.g. "0 9 * * *").'), {
code: 'BAD_REQUEST'
})
}
next.cron = patch.cron.trim()
}
Expand All @@ -116,10 +115,9 @@ export function writeSchedule(projectRoot, patch) {
}
const indexes = [...new Set(patch.excludedAccountIndexes.map(Number))]
if (indexes.some(i => !Number.isSafeInteger(i) || i < 1)) {
throw Object.assign(
new Error('excludedAccountIndexes must contain only positive integers.'),
{ code: 'BAD_REQUEST' }
)
throw Object.assign(new Error('excludedAccountIndexes must contain only positive integers.'), {
code: 'BAD_REQUEST'
})
}
next.excludedAccountIndexes = indexes.sort((a, b) => a - b)
}
Expand Down Expand Up @@ -168,10 +166,9 @@ export function applyCrontab({ enabled, cron }) {
}

if (!fs.existsSync(CRON_TEMPLATE)) {
throw Object.assign(
new Error(`Cron template not found at ${CRON_TEMPLATE} — image may be corrupt.`),
{ code: 'TEMPLATE_MISSING' }
)
throw Object.assign(new Error(`Cron template not found at ${CRON_TEMPLATE} — image may be corrupt.`), {
code: 'TEMPLATE_MISSING'
})
}

const tz = process.env.TZ || 'UTC'
Expand Down
10 changes: 5 additions & 5 deletions scripts/api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ try {
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'))
pkgVersion = pkg.version ?? pkgVersion
pkgName = pkg.name ?? pkgName
} catch { }
} catch {}

const HOST = envStr('API_HOST') ?? (typeof cliArgs.host === 'string' ? cliArgs.host : '127.0.0.1')
const PORT = Number(cliArgs.port) || envInt('API_PORT', 3010)
Expand Down Expand Up @@ -422,7 +422,7 @@ const requestHandler = async (req, res) => {
const force = Boolean(body.force)
try {
const stopping = pm.stop({ force })
stopping.catch(() => { })
stopping.catch(() => {})
return sendJson(res, 202, { stopping: true, force })
} catch (err) {
if (err.code === 'NOT_RUNNING') return sendJson(res, 409, { error: err.message, code: err.code })
Expand Down Expand Up @@ -554,14 +554,14 @@ function listDiagnostics() {
let error = null
try {
files = fs.readdirSync(full)
} catch { }
} catch {}
try {
createdAt = fs.statSync(full).mtime.toISOString()
} catch { }
} catch {}
if (files.includes('error.txt')) {
try {
error = fs.readFileSync(path.join(full, 'error.txt'), 'utf8').slice(0, 2000)
} catch { }
} catch {}
}
return {
name: d.name,
Expand Down
42 changes: 20 additions & 22 deletions scripts/docker/schedule-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,29 @@
const SCHEDULE_FILE = envStr('SCHEDULE_FILE') ?? path.join(projectRoot, 'config', 'schedule.json')

function readSchedule() {
if (fs.existsSync(SCHEDULE_FILE)) {
return JSON.parse(fs.readFileSync(SCHEDULE_FILE, 'utf8')) // source: 'override'
}
// Fall back to the env-configured default so nothing breaks for
// no-frontend users who only ever set CRON_SCHEDULE.
return {
enabled: Boolean(process.env.CRON_SCHEDULE),
cron: process.env.CRON_SCHEDULE || null,
skipIfRunning: true,
excludedAccountIndexes: [],
source: 'env'
}
if (fs.existsSync(SCHEDULE_FILE)) {
return JSON.parse(fs.readFileSync(SCHEDULE_FILE, 'utf8')) // source: 'override'
}
// Fall back to the env-configured default so nothing breaks for
// no-frontend users who only ever set CRON_SCHEDULE.
return {
enabled: Boolean(process.env.CRON_SCHEDULE),
cron: process.env.CRON_SCHEDULE || null,
skipIfRunning: true,
excludedAccountIndexes: [],
source: 'env'
}
}

function writeSchedule(next) {
writeConfigAtomic(SCHEDULE_FILE, next) // reuse the atomic-write helper already in configEditor.js
applyCrontab(next) // regenerate /etc/cron.d file + `crontab` it live
writeConfigAtomic(SCHEDULE_FILE, next) // reuse the atomic-write helper already in configEditor.js
applyCrontab(next) // regenerate /etc/cron.d file + `crontab` it live
}

function applyCrontab({ enabled, cron }) {
if (!enabled || !cron) return execSync('crontab -r', { stdio: 'ignore' }) // no-op if none exists
const template = fs.readFileSync('/etc/cron.d/microsoft-rewards-cron.template', 'utf8')
const rendered = template
.replace('${CRON_SCHEDULE}', cron)
.replace('${TZ}', process.env.TZ || 'UTC')
fs.writeFileSync('/etc/cron.d/microsoft-rewards-cron', rendered, { mode: 0o644 })
execSync('crontab /etc/cron.d/microsoft-rewards-cron')
}
if (!enabled || !cron) return execSync('crontab -r', { stdio: 'ignore' }) // no-op if none exists
const template = fs.readFileSync('/etc/cron.d/microsoft-rewards-cron.template', 'utf8')
const rendered = template.replace('${CRON_SCHEDULE}', cron).replace('${TZ}', process.env.TZ || 'UTC')
fs.writeFileSync('/etc/cron.d/microsoft-rewards-cron', rendered, { mode: 0o644 })
execSync('crontab /etc/cron.d/microsoft-rewards-cron')
}