You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.2 KiB
105 lines
3.2 KiB
import { readFileSync, writeFileSync, statSync, copyFileSync } from 'fs'
|
|
import { resolve } from 'path'
|
|
import { homedir } from 'os'
|
|
|
|
import yaml from 'js-yaml'
|
|
import CronParser from 'cron-parser'
|
|
|
|
import { Backend, Config } from './types'
|
|
import { makeArrayIfIsNot, makeObjectKeysLowercase, rand } from './utils'
|
|
|
|
export enum LocationFromPrefixes {
|
|
Filesystem,
|
|
DockerVolume,
|
|
}
|
|
|
|
export const normalizeAndCheckBackends = (config: Config) => {
|
|
config.backends = makeObjectKeysLowercase(config.backends)
|
|
|
|
for (const [name, { type, path, key, ...rest }] of Object.entries(config.backends)) {
|
|
if (!type || !path) throw new Error(`The backend "${name}" is missing some required attributes`)
|
|
|
|
const tmp: any = {
|
|
type,
|
|
path,
|
|
key: key || rand(128),
|
|
}
|
|
for (const [key, value] of Object.entries(rest)) tmp[key.toUpperCase()] = value
|
|
|
|
config.backends[name] = tmp as Backend
|
|
}
|
|
}
|
|
|
|
export const normalizeAndCheckLocations = (config: Config) => {
|
|
config.locations = makeObjectKeysLowercase(config.locations)
|
|
const backends = Object.keys(config.backends)
|
|
|
|
const checkDestination = (backend: string, location: string) => {
|
|
if (!backends.includes(backend)) throw new Error(`Cannot find the backend "${backend}" for "${location}"`)
|
|
}
|
|
|
|
for (const [name, { from, to, cron, ...rest }] of Object.entries(config.locations)) {
|
|
if (!from) throw new Error(`The location "${name.blue}" is missing the "${'from'.underline.red}" source folder. See https://git.io/Jf0xw`)
|
|
if (!to || (Array.isArray(to) && !to.length))
|
|
throw new Error(`The location "${name.blue}" has no backend "${'to'.underline.red}" to save the backups. See https://git.io/Jf0xw`)
|
|
|
|
for (const t of makeArrayIfIsNot(to)) checkDestination(t, name)
|
|
|
|
if (cron) {
|
|
try {
|
|
CronParser.parseExpression(cron)
|
|
} catch {
|
|
throw new Error(`The location "${name.blue}" has an invalid ${'cron'.underline.red} entry. See https://git.io/Jf0xP`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const findConfigFile = (custom: string): string => {
|
|
const config = '.autorestic.yml'
|
|
const paths = [resolve(custom || ''), resolve('./' + config), homedir() + '/' + config]
|
|
for (const path of paths) {
|
|
try {
|
|
const file = statSync(path)
|
|
if (file.isFile()) return path
|
|
} catch (e) {}
|
|
}
|
|
throw new Error('Config file not found')
|
|
}
|
|
|
|
export let CONFIG_FILE: string = ''
|
|
|
|
export const init = (custom: string): Config => {
|
|
const file = findConfigFile(custom)
|
|
CONFIG_FILE = file
|
|
|
|
const parsed = yaml.safeLoad(readFileSync(CONFIG_FILE).toString())
|
|
if (!parsed || typeof parsed === 'string') throw new Error('Could not parse the config file')
|
|
const raw: Config = makeObjectKeysLowercase(parsed)
|
|
|
|
const current = JSON.stringify(raw)
|
|
|
|
normalizeAndCheckBackends(raw)
|
|
normalizeAndCheckLocations(raw)
|
|
|
|
const changed = JSON.stringify(raw) !== current
|
|
|
|
if (changed) {
|
|
const OLD_CONFIG_FILE = CONFIG_FILE + '.old'
|
|
copyFileSync(CONFIG_FILE, OLD_CONFIG_FILE)
|
|
writeFileSync(CONFIG_FILE, yaml.safeDump(raw))
|
|
console.log(
|
|
'\n' +
|
|
'⚠️ MOVED OLD CONFIG FILE TO: ⚠️'.red.underline.bold +
|
|
'\n' +
|
|
OLD_CONFIG_FILE +
|
|
'\n' +
|
|
'What? Why? '.grey +
|
|
'https://git.io/Jf0xK'.underline.grey +
|
|
'\n'
|
|
)
|
|
}
|
|
|
|
return raw
|
|
}
|