-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
132 lines (120 loc) · 3.45 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
const ccurllib = require('ccurllib')
const headers = {}
// fetch the document in question.
const getDoc = async (url) => {
const req = {
method: 'get',
url,
headers,
qs: { conflicts: true }
}
return ccurllib.request(req)
}
// opts - { url: '' , keep: '', batch: 100, verbose: false, deletions: [] }
const processDeletions = async (opts) => {
const deletionAim = opts.deletions.length
let deletionCount = 0
const progress = function (x, y) {
const percent = 100 * x / y
const charCount = Math.floor(percent / 2)
const p = '[' + '='.repeat(charCount) + '-'.repeat(50 - charCount) + ']'
process.stdout.write(' ' + p + ' ' + Math.floor(percent) + '% ' + x + '/' + y + ' \r')
}
let done = false
do {
const b = opts.deletions.splice(0, opts.batch)
if (b.length > 0) {
if (opts.dryrun) {
for (const doc of b) {
console.log('DELETE ' + JSON.stringify(doc))
}
} else {
const r = {
method: 'post',
url: opts.url.replace(/\/[^/]+$/, '/_bulk_docs'),
headers,
data: { docs: b }
}
await ccurllib.request(r)
deletionCount += b.length
if (opts.verbose) {
progress(deletionCount, deletionAim)
}
}
} else {
done = true
}
} while (!done)
console.log('\n')
return deletionCount
}
// opts - { url: '' , keep: '', batch: 100, verbose: false }
const decon = async (opts) => {
let doc = null
const deletions = []
let found = false
// defaults
if (typeof opts.batch === 'undefined') {
opts.batch = 100
}
if (typeof opts.verbose === 'undefined') {
opts.verbose = false
}
if (typeof opts.keep === 'undefined') {
opts.keep = null
}
if (typeof opts.keep === 'undefined') {
opts.dryrun = false
}
// fetch the document with list of conflicts
if (opts.verbose) {
console.log('couchdeconflict')
console.log('---------------')
console.log('options: ' + JSON.stringify({ url: opts.url.replace(/\/\/(.*)@/, '//###:###@'), keep: opts.keep, batch: opts.batch }))
console.log('Fetching document')
}
doc = await getDoc(opts.url)
if (doc.status >= 400) {
throw new Error("Could not fetch document")
}
doc = doc.result
// if the document has no conflicts, there's nothing to do
if (!doc._conflicts) {
throw new Error('document has no conflicts')
}
if (opts.verbose) {
console.log(doc._conflicts.length + ' conflicts')
}
// generate a list of revisions to delete
for (const i in doc._conflicts) {
if (doc._conflicts[i] !== opts.keep) {
deletions.push({ _id: doc._id, _rev: doc._conflicts[i], _deleted: true })
} else {
found = true
}
}
// delete the current winner if required
if (opts.keep) {
if (doc._rev !== opts.keep) {
deletions.push({ _id: doc._id, _rev: doc._rev, _deleted: true })
} else {
found = true
}
}
// if we haven't come across the revision we are supposed to keep, don't proceed
if (opts.keep && !found) {
throw new Error('Revision ' + opts.keep + ' not found - no conflicts removed')
}
// if there are no deletions in the array, we've nothing to do
if (deletions.length === 0) {
throw new Error('No conflicts found')
}
if (opts.verbose && opts.keep) {
console.log('Keeping revision ' + opts.keep)
}
// perform deletions
opts.deletions = deletions
const retval = await processDeletions(opts)
return retval
}
module.exports = decon