@qkix/strapi-plugin-rewind 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -5
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +198 -152
- package/dist/admin/src/components/ChangesDialog.d.ts +22 -1
- package/dist/admin/src/diffRegistry.d.ts +16 -10
- package/dist/server/index.js +2 -2
- package/dist/server/index.mjs +420 -357
- package/dist/server/src/services/serializer.d.ts +48 -3
- package/dist/server/src/utils/versionLabel.d.ts +19 -3
- package/package.json +3 -3
package/dist/server/index.mjs
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
|
-
import { createHash as
|
|
2
|
-
import { AsyncLocalStorage as
|
|
3
|
-
const
|
|
1
|
+
import { createHash as x } from "node:crypto";
|
|
2
|
+
import { AsyncLocalStorage as ee } from "node:async_hooks";
|
|
3
|
+
const W = (e) => {
|
|
4
|
+
const t = /* @__PURE__ */ new Map();
|
|
5
|
+
return async (n) => {
|
|
6
|
+
if (t.has(n)) return t.get(n);
|
|
7
|
+
let i;
|
|
8
|
+
try {
|
|
9
|
+
i = (await e.plugin("content-manager").service("content-types").findConfiguration(e.contentTypes[n]))?.settings?.mainField;
|
|
10
|
+
} catch {
|
|
11
|
+
}
|
|
12
|
+
return t.set(n, i), i;
|
|
13
|
+
};
|
|
14
|
+
}, K = ["title", "name", "label"], G = (e) => {
|
|
15
|
+
const t = W(e);
|
|
16
|
+
return async (n, i) => {
|
|
17
|
+
if (!i) return null;
|
|
18
|
+
const r = [await t(n), ...K].filter(Boolean);
|
|
19
|
+
for (const s of r) {
|
|
20
|
+
const a = i[s];
|
|
21
|
+
if (typeof a == "string" && a.trim())
|
|
22
|
+
return a.trim().slice(0, 255);
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
}, te = ["id", "documentId", "locale"], q = [
|
|
4
27
|
"createdAt",
|
|
5
28
|
"updatedAt",
|
|
6
29
|
"publishedAt",
|
|
@@ -10,35 +33,66 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
10
33
|
"strapi_assignee",
|
|
11
34
|
"locale",
|
|
12
35
|
"localizations"
|
|
13
|
-
],
|
|
36
|
+
], S = (e, t) => e.getModel(t), ne = (e) => e?.type === "relation" || e?.type === "media", $ = (e) => e?.type === "relation" && String(e.relation ?? "").toLowerCase().startsWith("morph"), _ = "plugin::upload.file", oe = /* @__PURE__ */ new Set(["string", "uid", "email", "text", "enumeration"]), se = async (e, t, n) => {
|
|
37
|
+
const i = /* @__PURE__ */ new Map(), o = async (r) => {
|
|
38
|
+
if (!r || i.has(r)) return;
|
|
39
|
+
let s;
|
|
40
|
+
try {
|
|
41
|
+
s = S(e, r).attributes;
|
|
42
|
+
} catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const a = (d) => !!d && oe.has(s[d]?.type), l = [await n(r), ...K].find(a);
|
|
46
|
+
l && i.set(r, l);
|
|
47
|
+
};
|
|
48
|
+
for (const r of Object.values(S(e, t).attributes))
|
|
49
|
+
r?.type === "media" ? await o(_) : r?.type === "relation" && !$(r) && await o(r.target);
|
|
50
|
+
return i;
|
|
51
|
+
}, ie = (e) => Object.fromEntries(
|
|
52
|
+
Object.entries(e).map(([t, n]) => [
|
|
53
|
+
t,
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
55
|
+
n.map(({ label: i, ...o }) => o)
|
|
56
|
+
])
|
|
57
|
+
), j = (e, t) => Object.entries(S(e, t).attributes).filter(
|
|
14
58
|
([, n]) => !["relation", "media", "component", "dynamiczone"].includes(n.type)
|
|
15
|
-
).map(([n]) => n),
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
59
|
+
).map(([n]) => n), F = (e, t, n = !0, i) => {
|
|
60
|
+
const o = S(e, t), r = n ? "select" : "fields", s = (a) => {
|
|
61
|
+
const l = i?.get(a);
|
|
62
|
+
return l ? [l] : [];
|
|
63
|
+
};
|
|
64
|
+
return Object.entries(o.attributes).reduce(
|
|
65
|
+
(a, [l, d]) => {
|
|
66
|
+
switch (d.type) {
|
|
20
67
|
case "relation": {
|
|
21
|
-
if ($(
|
|
22
|
-
a[
|
|
68
|
+
if ($(d)) break;
|
|
69
|
+
a[l] = {
|
|
70
|
+
[r]: [
|
|
71
|
+
"documentId",
|
|
72
|
+
"locale",
|
|
73
|
+
"publishedAt",
|
|
74
|
+
...s(d.target)
|
|
75
|
+
]
|
|
76
|
+
};
|
|
23
77
|
break;
|
|
24
78
|
}
|
|
25
79
|
case "media": {
|
|
26
|
-
a[
|
|
80
|
+
a[l] = { [r]: ["id", ...s(_)] };
|
|
27
81
|
break;
|
|
28
82
|
}
|
|
29
83
|
case "component": {
|
|
30
|
-
a[
|
|
31
|
-
populate:
|
|
32
|
-
[
|
|
84
|
+
a[l] = {
|
|
85
|
+
populate: F(e, d.component, n),
|
|
86
|
+
[r]: j(e, d.component)
|
|
33
87
|
};
|
|
34
88
|
break;
|
|
35
89
|
}
|
|
36
90
|
case "dynamiczone": {
|
|
37
|
-
a[
|
|
38
|
-
on: (
|
|
39
|
-
(c,
|
|
40
|
-
populate:
|
|
41
|
-
[
|
|
91
|
+
a[l] = {
|
|
92
|
+
on: (d.components ?? []).reduce(
|
|
93
|
+
(c, u) => (c[u] = {
|
|
94
|
+
populate: F(e, u, n),
|
|
95
|
+
[r]: j(e, u)
|
|
42
96
|
}, c),
|
|
43
97
|
{}
|
|
44
98
|
)
|
|
@@ -50,138 +104,133 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
50
104
|
},
|
|
51
105
|
{}
|
|
52
106
|
);
|
|
53
|
-
},
|
|
54
|
-
const n = {}, i = (
|
|
55
|
-
if (n[
|
|
56
|
-
const s =
|
|
57
|
-
n[
|
|
58
|
-
for (const
|
|
59
|
-
if (
|
|
60
|
-
for (const
|
|
61
|
-
}, o =
|
|
62
|
-
for (const
|
|
63
|
-
if (
|
|
64
|
-
for (const s of
|
|
107
|
+
}, re = (e, t) => {
|
|
108
|
+
const n = {}, i = (r) => {
|
|
109
|
+
if (n[r]) return;
|
|
110
|
+
const s = S(e, r).attributes;
|
|
111
|
+
n[r] = N(s, q);
|
|
112
|
+
for (const a of Object.values(s))
|
|
113
|
+
if (a.type === "component" && i(a.component), a.type === "dynamiczone")
|
|
114
|
+
for (const l of a.components ?? []) i(l);
|
|
115
|
+
}, o = S(e, t).attributes;
|
|
116
|
+
for (const r of Object.values(o))
|
|
117
|
+
if (r.type === "component" && i(r.component), r.type === "dynamiczone")
|
|
118
|
+
for (const s of r.components ?? []) i(s);
|
|
65
119
|
return {
|
|
66
|
-
attributes:
|
|
120
|
+
attributes: N(o, q),
|
|
67
121
|
components: n
|
|
68
122
|
};
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
123
|
+
}, N = (e, t) => Object.fromEntries(Object.entries(e ?? {}).filter(([n]) => !t.includes(n))), ae = (e, t, n, i) => {
|
|
124
|
+
const o = Array.isArray(e) ? e : [e], r = (s) => {
|
|
125
|
+
const a = i ? s[i] : void 0;
|
|
126
|
+
return typeof a == "string" && a.trim() ? { label: a.trim().slice(0, 255) } : {};
|
|
127
|
+
};
|
|
128
|
+
return o.filter((s) => !!s).map(
|
|
129
|
+
(s) => n ? { id: s.id, targetUid: t, ...r(s) } : {
|
|
130
|
+
documentId: s.documentId,
|
|
131
|
+
locale: s.locale ?? null,
|
|
132
|
+
targetUid: t,
|
|
133
|
+
...r(s)
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
}, ce = (e, t, n, i) => {
|
|
137
|
+
const o = S(e, t).attributes, r = {}, s = {}, a = N(n, [...q, ...te]);
|
|
138
|
+
for (const [l, d] of Object.entries(a)) {
|
|
139
|
+
const c = o[l];
|
|
140
|
+
if (c && ne(c)) {
|
|
141
|
+
if ($(c)) continue;
|
|
142
|
+
const u = c.type === "media" ? _ : c.target;
|
|
143
|
+
s[l] = ae(
|
|
144
|
+
d,
|
|
145
|
+
u,
|
|
146
|
+
c.type === "media",
|
|
147
|
+
i?.get(u)
|
|
148
|
+
);
|
|
83
149
|
continue;
|
|
84
150
|
}
|
|
85
|
-
|
|
151
|
+
r[l] = c ? V(e, c, d) : d;
|
|
86
152
|
}
|
|
87
|
-
return { data:
|
|
88
|
-
},
|
|
153
|
+
return { data: r, relations: s, schemaSnapshot: re(e, t) };
|
|
154
|
+
}, V = (e, t, n) => {
|
|
89
155
|
if (n == null) return n;
|
|
90
156
|
if (t.type === "component") {
|
|
91
|
-
const i = (o) =>
|
|
157
|
+
const i = (o) => M(e, t.component, o);
|
|
92
158
|
return Array.isArray(n) ? n.map((o) => i(o)) : i(n);
|
|
93
159
|
}
|
|
94
160
|
return t.type === "dynamiczone" && Array.isArray(n) ? n.map((i) => {
|
|
95
161
|
const o = i;
|
|
96
|
-
return
|
|
162
|
+
return M(e, o.__component, o);
|
|
97
163
|
}) : n;
|
|
98
|
-
},
|
|
164
|
+
}, M = (e, t, n) => {
|
|
99
165
|
if (!n || !t) return n;
|
|
100
|
-
const i =
|
|
101
|
-
for (const [
|
|
102
|
-
if (
|
|
103
|
-
const
|
|
104
|
-
o[
|
|
166
|
+
const i = S(e, t).attributes, o = {};
|
|
167
|
+
for (const [r, s] of Object.entries(n)) {
|
|
168
|
+
if (r === "id") continue;
|
|
169
|
+
const a = i[r];
|
|
170
|
+
o[r] = a ? V(e, a, s) : s;
|
|
105
171
|
}
|
|
106
172
|
return o;
|
|
107
|
-
},
|
|
108
|
-
const t = /* @__PURE__ */ new Map(),
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
return t.set(i, o), o;
|
|
116
|
-
};
|
|
117
|
-
return async (i, o) => {
|
|
118
|
-
if (!o) return null;
|
|
119
|
-
const s = [await n(i), "title", "name", "label"].filter(Boolean);
|
|
120
|
-
for (const r of s) {
|
|
121
|
-
const c = o[r];
|
|
122
|
-
if (typeof c == "string" && c.trim())
|
|
123
|
-
return c.trim().slice(0, 255);
|
|
124
|
-
}
|
|
125
|
-
return null;
|
|
126
|
-
};
|
|
127
|
-
}, v = "plugin::rewind.version", oe = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), se = (e) => J("sha256").update(JSON.stringify(e)).digest("hex"), ie = ({ strapi: e }) => {
|
|
128
|
-
const t = H(e), n = (r) => !!e.contentTypes[r]?.options?.draftAndPublish, i = (r) => !!(e.getModel(r)?.pluginOptions?.i18n?.localized ?? e.contentTypes[r]?.pluginOptions?.i18n?.localized), o = async (r, c, l) => {
|
|
129
|
-
const d = i(r) && l.filter(Boolean).length > 0 ? { locale: { $in: l.filter(Boolean) } } : {};
|
|
130
|
-
return e.db.query(r).findMany({
|
|
173
|
+
}, D = "plugin::rewind.version", le = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), de = (e) => x("sha256").update(JSON.stringify(e)).digest("hex"), ue = ({ strapi: e }) => {
|
|
174
|
+
const t = G(e), n = W(e), i = /* @__PURE__ */ new Map(), o = (c) => {
|
|
175
|
+
let u = i.get(c);
|
|
176
|
+
return u || (u = se(e, c, n), i.set(c, u)), u;
|
|
177
|
+
}, r = (c) => !!e.contentTypes[c]?.options?.draftAndPublish, s = (c) => !!(e.getModel(c)?.pluginOptions?.i18n?.localized ?? e.contentTypes[c]?.pluginOptions?.i18n?.localized), a = async (c, u, f) => {
|
|
178
|
+
const g = s(c) && f.filter(Boolean).length > 0 ? { locale: { $in: f.filter(Boolean) } } : {};
|
|
179
|
+
return e.db.query(c).findMany({
|
|
131
180
|
where: {
|
|
132
|
-
documentId:
|
|
133
|
-
...
|
|
134
|
-
...
|
|
181
|
+
documentId: u,
|
|
182
|
+
...g,
|
|
183
|
+
...r(c) ? { publishedAt: null } : {}
|
|
135
184
|
},
|
|
136
|
-
populate:
|
|
185
|
+
populate: F(e, c, !0, await o(c))
|
|
137
186
|
});
|
|
138
|
-
},
|
|
187
|
+
}, l = async (c, u) => {
|
|
139
188
|
try {
|
|
140
|
-
const
|
|
141
|
-
return
|
|
189
|
+
const f = e.plugin("content-manager").service("document-metadata"), g = await f.getMetadata(c, u);
|
|
190
|
+
return f.getStatus(u, g.availableStatus);
|
|
142
191
|
} catch {
|
|
143
192
|
return "draft";
|
|
144
193
|
}
|
|
145
|
-
},
|
|
146
|
-
where: { contentType:
|
|
194
|
+
}, d = async (c, u, f) => e.db.query(D).findOne({
|
|
195
|
+
where: { contentType: c, relatedDocumentId: u, locale: f },
|
|
147
196
|
orderBy: { createdAt: "desc" }
|
|
148
197
|
});
|
|
149
198
|
return {
|
|
150
|
-
readRows:
|
|
199
|
+
readRows: a,
|
|
151
200
|
/**
|
|
152
201
|
* Writes one version per affected locale, for each buffered intent.
|
|
153
202
|
*
|
|
154
203
|
* Called from the transaction's commit hook and never awaited by the write
|
|
155
204
|
* path - a failure here must not be able to reach the editor's save.
|
|
156
205
|
*/
|
|
157
|
-
async captureAll(
|
|
158
|
-
for (const
|
|
159
|
-
await this.capture(
|
|
206
|
+
async captureAll(c, u) {
|
|
207
|
+
for (const f of c)
|
|
208
|
+
await this.capture(f, u);
|
|
160
209
|
},
|
|
161
|
-
async capture(
|
|
162
|
-
const { uid:
|
|
163
|
-
for (const
|
|
164
|
-
const
|
|
165
|
-
!
|
|
210
|
+
async capture(c, u) {
|
|
211
|
+
const { uid: f, relatedDocumentId: g, locale: y, origin: T } = c, p = c.before ?? await a(f, g, [y]);
|
|
212
|
+
for (const h of p) {
|
|
213
|
+
const b = h.locale ?? null, { data: m, relations: v, schemaSnapshot: A } = ce(e, f, h, await o(f)), I = de({ data: m, relations: ie(v) });
|
|
214
|
+
!le.has(T) && (await d(f, g, b))?.contentHash === I || await e.db.query(D).create({
|
|
166
215
|
data: {
|
|
167
|
-
contentType:
|
|
168
|
-
relatedDocumentId:
|
|
169
|
-
locale:
|
|
170
|
-
status: await
|
|
171
|
-
label: await t(
|
|
172
|
-
data:
|
|
173
|
-
relations:
|
|
174
|
-
schemaSnapshot:
|
|
175
|
-
contentHash:
|
|
176
|
-
origin:
|
|
177
|
-
userId:
|
|
216
|
+
contentType: f,
|
|
217
|
+
relatedDocumentId: g,
|
|
218
|
+
locale: b,
|
|
219
|
+
status: await l(f, h),
|
|
220
|
+
label: await t(f, m),
|
|
221
|
+
data: m,
|
|
222
|
+
relations: v,
|
|
223
|
+
schemaSnapshot: A,
|
|
224
|
+
contentHash: I,
|
|
225
|
+
origin: T,
|
|
226
|
+
userId: u,
|
|
178
227
|
pinned: !1
|
|
179
228
|
}
|
|
180
229
|
});
|
|
181
230
|
}
|
|
182
231
|
}
|
|
183
232
|
};
|
|
184
|
-
},
|
|
233
|
+
}, C = [
|
|
185
234
|
"restore",
|
|
186
235
|
"publish",
|
|
187
236
|
"unpublish",
|
|
@@ -189,10 +238,10 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
189
238
|
"clone",
|
|
190
239
|
"create",
|
|
191
240
|
"update"
|
|
192
|
-
],
|
|
193
|
-
let t =
|
|
194
|
-
return t || (t = { intents: [], flushRegistered: !1 },
|
|
195
|
-
},
|
|
241
|
+
], B = /* @__PURE__ */ new WeakMap(), fe = (e) => {
|
|
242
|
+
let t = B.get(e);
|
|
243
|
+
return t || (t = { intents: [], flushRegistered: !1 }, B.set(e, t)), t;
|
|
244
|
+
}, pe = (e) => {
|
|
196
245
|
const t = /* @__PURE__ */ new Map();
|
|
197
246
|
for (const n of e) {
|
|
198
247
|
const i = `${n.uid}:${n.relatedDocumentId}:${n.locale ?? ""}`, o = t.get(i);
|
|
@@ -200,86 +249,86 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
200
249
|
t.set(i, n);
|
|
201
250
|
continue;
|
|
202
251
|
}
|
|
203
|
-
const
|
|
252
|
+
const r = C.indexOf(n.origin) < C.indexOf(o.origin);
|
|
204
253
|
t.set(i, {
|
|
205
254
|
// A winning `publish` still wants the rows an earlier `discardDraft`
|
|
206
255
|
// captured - losing them would lose the only copy of that state.
|
|
207
|
-
...
|
|
256
|
+
...r ? n : o,
|
|
208
257
|
before: o.before ?? n.before
|
|
209
258
|
});
|
|
210
259
|
}
|
|
211
260
|
return [...t.values()];
|
|
212
|
-
},
|
|
261
|
+
}, X = new ee(), me = (e) => X.run({ active: !0 }, e), ge = () => X.getStore()?.active === !0, ye = 200, he = async (e, t) => {
|
|
213
262
|
const n = e.db.query(t), i = await n.count({ where: { label: null } });
|
|
214
263
|
if (i === 0) return 0;
|
|
215
264
|
e.log.info(`[rewind] labelling ${i} older version(s)…`);
|
|
216
|
-
const o =
|
|
217
|
-
let
|
|
265
|
+
const o = G(e);
|
|
266
|
+
let r = 0;
|
|
218
267
|
for (; ; ) {
|
|
219
268
|
const s = await n.findMany({
|
|
220
269
|
select: ["id", "contentType", "data"],
|
|
221
270
|
where: { label: null },
|
|
222
|
-
limit:
|
|
271
|
+
limit: ye
|
|
223
272
|
});
|
|
224
273
|
if (s.length === 0) break;
|
|
225
|
-
for (const
|
|
226
|
-
const
|
|
274
|
+
for (const a of s) {
|
|
275
|
+
const l = await o(a.contentType, a.data);
|
|
227
276
|
await n.update({
|
|
228
|
-
where: { id:
|
|
229
|
-
data: { label:
|
|
230
|
-
}),
|
|
277
|
+
where: { id: a.id },
|
|
278
|
+
data: { label: l ?? "" }
|
|
279
|
+
}), r += 1;
|
|
231
280
|
}
|
|
232
281
|
}
|
|
233
|
-
return e.log.info(`[rewind] labelled ${
|
|
234
|
-
},
|
|
235
|
-
const n = await e.store.get(
|
|
282
|
+
return e.log.info(`[rewind] labelled ${r} older version(s)`), r;
|
|
283
|
+
}, L = { type: "core", key: "persisted_tables" }, we = async (e, t) => {
|
|
284
|
+
const n = await e.store.get(L) ?? [];
|
|
236
285
|
n.map((o) => typeof o == "string" ? o : o.name).includes(t) || await e.store.set({
|
|
237
|
-
...
|
|
286
|
+
...L,
|
|
238
287
|
value: [...n, { name: t }]
|
|
239
288
|
});
|
|
240
|
-
},
|
|
289
|
+
}, be = [
|
|
241
290
|
"create",
|
|
242
291
|
"update",
|
|
243
292
|
"clone",
|
|
244
293
|
"publish",
|
|
245
294
|
"unpublish",
|
|
246
295
|
"discardDraft"
|
|
247
|
-
],
|
|
296
|
+
], Te = async ({ strapi: e }) => {
|
|
248
297
|
const t = e.config.get("plugin::rewind");
|
|
249
|
-
await
|
|
298
|
+
await we(e, "rewind_versions"), await De(e), he(e, D).catch(
|
|
250
299
|
(o) => e.log.warn(
|
|
251
300
|
`[rewind] could not label older versions: ${o instanceof Error ? o.message : String(o)}`
|
|
252
301
|
)
|
|
253
|
-
),
|
|
302
|
+
), Ae(e, t);
|
|
254
303
|
const n = () => e.plugin("rewind").service("snapshot"), i = (o) => {
|
|
255
|
-
if (!
|
|
256
|
-
const
|
|
257
|
-
return !(!
|
|
304
|
+
if (!be.includes(o.action)) return !1;
|
|
305
|
+
const r = o.contentType?.uid ?? o.uid ?? "";
|
|
306
|
+
return !(!r.startsWith("api::") || !t.contentTypes.includes(r) || ge() || !t.trackApiWrites && !e.requestContext.get()?.request?.url?.startsWith("/content-manager"));
|
|
258
307
|
};
|
|
259
|
-
e.documents.use(async (o,
|
|
260
|
-
if (!i(o)) return
|
|
261
|
-
const s = o.contentType?.uid ?? o.uid,
|
|
262
|
-
if (!
|
|
263
|
-
const
|
|
264
|
-
return await e.db.transaction(async ({ trx:
|
|
265
|
-
const
|
|
266
|
-
|
|
308
|
+
e.documents.use(async (o, r) => {
|
|
309
|
+
if (!i(o)) return r();
|
|
310
|
+
const s = o.contentType?.uid ?? o.uid, a = o.params?.locale ?? null, l = o.action === "discardDraft" && o.params?.documentId ? await n().readRows(s, o.params.documentId, [a]) : null, d = await r(), c = o.action === "create" || o.action === "clone" ? d?.documentId : o.params?.documentId;
|
|
311
|
+
if (!c) return d;
|
|
312
|
+
const u = ve(e);
|
|
313
|
+
return await e.db.transaction(async ({ trx: f, onCommit: g }) => {
|
|
314
|
+
const y = fe(f);
|
|
315
|
+
y.intents.push({
|
|
267
316
|
uid: s,
|
|
268
|
-
relatedDocumentId:
|
|
269
|
-
locale:
|
|
317
|
+
relatedDocumentId: c,
|
|
318
|
+
locale: a,
|
|
270
319
|
origin: o.action,
|
|
271
|
-
before:
|
|
272
|
-
}), !
|
|
273
|
-
const
|
|
274
|
-
n().captureAll(
|
|
275
|
-
(
|
|
320
|
+
before: l
|
|
321
|
+
}), !y.flushRegistered && (y.flushRegistered = !0, g(() => {
|
|
322
|
+
const T = y.intents.splice(0, y.intents.length);
|
|
323
|
+
n().captureAll(pe(T), u).catch(
|
|
324
|
+
(p) => e.log.error("[rewind] failed to record a version", p)
|
|
276
325
|
);
|
|
277
326
|
}));
|
|
278
|
-
}),
|
|
327
|
+
}), d;
|
|
279
328
|
});
|
|
280
|
-
},
|
|
329
|
+
}, ve = (e) => e.requestContext.get()?.state?.user?.id ?? null, De = async (e) => {
|
|
281
330
|
try {
|
|
282
|
-
const { tableName: t } = e.db.metadata.get(
|
|
331
|
+
const { tableName: t } = e.db.metadata.get(D);
|
|
283
332
|
await e.db.connection.raw(
|
|
284
333
|
`CREATE INDEX IF NOT EXISTS idx_rewind_lookup
|
|
285
334
|
ON ${t} (content_type, related_document_id, locale, created_at)`
|
|
@@ -289,7 +338,7 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
289
338
|
`[rewind] could not create the lookup index, queries will be slower: ${t instanceof Error ? t.message : String(t)}`
|
|
290
339
|
);
|
|
291
340
|
}
|
|
292
|
-
},
|
|
341
|
+
}, Ae = (e, t) => {
|
|
293
342
|
t.retention?.enabled !== !1 && e.cron.add({
|
|
294
343
|
rewindPrune: {
|
|
295
344
|
async task() {
|
|
@@ -309,7 +358,7 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
309
358
|
options: { rule: t.cron ?? "0 3 * * *" }
|
|
310
359
|
}
|
|
311
360
|
});
|
|
312
|
-
},
|
|
361
|
+
}, Ie = {
|
|
313
362
|
default: {
|
|
314
363
|
/**
|
|
315
364
|
* Content types to version, by uid. Empty means nothing is tracked.
|
|
@@ -360,10 +409,10 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
360
409
|
validator(e = {}) {
|
|
361
410
|
const { contentTypes: t, retention: n } = e;
|
|
362
411
|
if (n) {
|
|
363
|
-
const { keepAllDays: o = 7, dailyUntilDays:
|
|
364
|
-
if (!(o <=
|
|
412
|
+
const { keepAllDays: o = 7, dailyUntilDays: r = 30, maxAgeDays: s = 365 } = n;
|
|
413
|
+
if (!(o <= r && r <= s))
|
|
365
414
|
throw new Error(
|
|
366
|
-
`[rewind] config.retention needs keepAllDays <= dailyUntilDays <= maxAgeDays (received ${o}, ${
|
|
415
|
+
`[rewind] config.retention needs keepAllDays <= dailyUntilDays <= maxAgeDays (received ${o}, ${r}, ${s})`
|
|
367
416
|
);
|
|
368
417
|
}
|
|
369
418
|
if (t === void 0) return;
|
|
@@ -377,15 +426,15 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
377
426
|
`[rewind] config.contentTypes only supports api:: content types, received: ${i.join(", ")}`
|
|
378
427
|
);
|
|
379
428
|
}
|
|
380
|
-
},
|
|
429
|
+
}, Oe = "collectionType", Se = "rewind_versions", ke = {
|
|
381
430
|
singularName: "version",
|
|
382
431
|
pluralName: "versions",
|
|
383
432
|
displayName: "Rewind Version",
|
|
384
433
|
description: "One snapshot of one document, in one locale, at one point in time."
|
|
385
|
-
},
|
|
434
|
+
}, Ee = {
|
|
386
435
|
draftAndPublish: !1,
|
|
387
436
|
comment: "Managed by strapi-plugin-rewind. Do not edit manually."
|
|
388
|
-
},
|
|
437
|
+
}, qe = {
|
|
389
438
|
"content-manager": {
|
|
390
439
|
visible: !1
|
|
391
440
|
},
|
|
@@ -395,7 +444,7 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
395
444
|
i18n: {
|
|
396
445
|
localized: !1
|
|
397
446
|
}
|
|
398
|
-
},
|
|
447
|
+
}, Re = {
|
|
399
448
|
contentType: {
|
|
400
449
|
type: "string",
|
|
401
450
|
required: !0
|
|
@@ -454,18 +503,18 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
454
503
|
userId: {
|
|
455
504
|
type: "integer"
|
|
456
505
|
}
|
|
457
|
-
},
|
|
458
|
-
kind:
|
|
459
|
-
collectionName:
|
|
460
|
-
info:
|
|
461
|
-
options:
|
|
462
|
-
pluginOptions:
|
|
463
|
-
attributes:
|
|
464
|
-
},
|
|
465
|
-
version: { schema:
|
|
466
|
-
},
|
|
506
|
+
}, $e = {
|
|
507
|
+
kind: Oe,
|
|
508
|
+
collectionName: Se,
|
|
509
|
+
info: ke,
|
|
510
|
+
options: Ee,
|
|
511
|
+
pluginOptions: qe,
|
|
512
|
+
attributes: Re
|
|
513
|
+
}, Fe = {
|
|
514
|
+
version: { schema: $e }
|
|
515
|
+
}, Ne = ({ strapi: e }) => {
|
|
467
516
|
e.cron.remove("rewindPrune");
|
|
468
|
-
},
|
|
517
|
+
}, _e = [
|
|
469
518
|
"id",
|
|
470
519
|
"contentType",
|
|
471
520
|
"relatedDocumentId",
|
|
@@ -477,37 +526,39 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
477
526
|
"pinned",
|
|
478
527
|
"contentHash",
|
|
479
528
|
"createdAt"
|
|
480
|
-
],
|
|
529
|
+
], je = ({ strapi: e }) => ({
|
|
481
530
|
async find(t) {
|
|
482
|
-
const { contentType: n, documentId: i, locale: o, page:
|
|
531
|
+
const { contentType: n, documentId: i, locale: o, page: r = 1, pageSize: s = 10 } = t.query;
|
|
483
532
|
if (!n || !i)
|
|
484
533
|
return t.badRequest("contentType and documentId are required.");
|
|
485
|
-
const
|
|
534
|
+
const a = {
|
|
486
535
|
contentType: n,
|
|
487
536
|
relatedDocumentId: i,
|
|
488
537
|
...o ? { locale: o } : {}
|
|
489
|
-
},
|
|
490
|
-
e.db.query(
|
|
491
|
-
select:
|
|
492
|
-
where:
|
|
538
|
+
}, l = Math.min(Number(s) || 10, 100), d = (Math.max(Number(r) || 1, 1) - 1) * l, [c, u] = await Promise.all([
|
|
539
|
+
e.db.query(D).findMany({
|
|
540
|
+
select: _e,
|
|
541
|
+
where: a,
|
|
493
542
|
orderBy: { createdAt: "desc" },
|
|
494
|
-
limit:
|
|
495
|
-
offset:
|
|
543
|
+
limit: l,
|
|
544
|
+
offset: d
|
|
496
545
|
}),
|
|
497
|
-
e.db.query(
|
|
546
|
+
e.db.query(D).count({ where: a })
|
|
498
547
|
]);
|
|
499
548
|
t.body = {
|
|
500
|
-
data: await U(e,
|
|
501
|
-
meta: { page: Number(
|
|
549
|
+
data: await U(e, c),
|
|
550
|
+
meta: { page: Number(r) || 1, pageSize: l, total: u }
|
|
502
551
|
};
|
|
503
552
|
},
|
|
504
553
|
async findOne(t) {
|
|
505
|
-
const n = await e.db.query(
|
|
554
|
+
const n = await e.db.query(D).findOne({ where: { id: Number(t.params.id) } });
|
|
506
555
|
if (!n) return t.notFound("No such version.");
|
|
507
556
|
t.body = { data: (await U(e, [n]))[0] };
|
|
508
557
|
},
|
|
509
558
|
async diff(t) {
|
|
510
559
|
const { against: n } = t.query;
|
|
560
|
+
if (n !== void 0 && !Number.isInteger(Number(n)))
|
|
561
|
+
return t.badRequest("against must be a version id.");
|
|
511
562
|
try {
|
|
512
563
|
t.body = {
|
|
513
564
|
data: await e.plugin("rewind").service("diff").between(Number(t.params.id), n ? Number(n) : void 0)
|
|
@@ -554,22 +605,22 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
554
605
|
}
|
|
555
606
|
}
|
|
556
607
|
}), U = async (e, t) => {
|
|
557
|
-
const n = [...new Set(t.map((
|
|
558
|
-
if (n.length === 0) return t.map((
|
|
559
|
-
const i = await e.db.query("admin::user").findMany({ select: ["id", "firstname", "lastname", "email"], where: { id: n } }), o = new Map(i.map((
|
|
560
|
-
return t.map((
|
|
561
|
-
const s = o.get(
|
|
608
|
+
const n = [...new Set(t.map((r) => r.userId).filter(Boolean))];
|
|
609
|
+
if (n.length === 0) return t.map((r) => ({ ...r, user: null }));
|
|
610
|
+
const i = await e.db.query("admin::user").findMany({ select: ["id", "firstname", "lastname", "email"], where: { id: n } }), o = new Map(i.map((r) => [r.id, r]));
|
|
611
|
+
return t.map((r) => {
|
|
612
|
+
const s = o.get(r.userId);
|
|
562
613
|
return {
|
|
563
|
-
...
|
|
614
|
+
...r,
|
|
564
615
|
user: s ? {
|
|
565
616
|
id: s.id,
|
|
566
617
|
name: [s.firstname, s.lastname].filter(Boolean).join(" ") || s.email
|
|
567
618
|
} : null
|
|
568
619
|
};
|
|
569
620
|
});
|
|
570
|
-
},
|
|
571
|
-
version:
|
|
572
|
-
},
|
|
621
|
+
}, Me = {
|
|
622
|
+
version: je
|
|
623
|
+
}, Ce = {
|
|
573
624
|
type: "admin",
|
|
574
625
|
routes: [
|
|
575
626
|
{
|
|
@@ -609,11 +660,11 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
609
660
|
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
610
661
|
}
|
|
611
662
|
]
|
|
612
|
-
},
|
|
613
|
-
admin:
|
|
614
|
-
},
|
|
615
|
-
const n =
|
|
616
|
-
if (n.length >
|
|
663
|
+
}, Be = {
|
|
664
|
+
admin: Ce
|
|
665
|
+
}, z = (e) => e.match(/\S+\s*/g) ?? [], P = 2500, Le = (e, t) => {
|
|
666
|
+
const n = z(e), i = z(t);
|
|
667
|
+
if (n.length > P || i.length > P)
|
|
617
668
|
return [
|
|
618
669
|
{ op: "removed", value: e },
|
|
619
670
|
{ op: "added", value: t }
|
|
@@ -622,20 +673,20 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
622
673
|
{ length: n.length + 1 },
|
|
623
674
|
() => new Array(i.length + 1).fill(0)
|
|
624
675
|
);
|
|
625
|
-
for (let
|
|
626
|
-
for (let
|
|
627
|
-
o[
|
|
628
|
-
const
|
|
629
|
-
const
|
|
630
|
-
|
|
676
|
+
for (let d = n.length - 1; d >= 0; d -= 1)
|
|
677
|
+
for (let c = i.length - 1; c >= 0; c -= 1)
|
|
678
|
+
o[d][c] = n[d] === i[c] ? o[d + 1][c + 1] + 1 : Math.max(o[d + 1][c], o[d][c + 1]);
|
|
679
|
+
const r = [], s = (d, c) => {
|
|
680
|
+
const u = r[r.length - 1];
|
|
681
|
+
u && u.op === d ? u.value += c : r.push({ op: d, value: c });
|
|
631
682
|
};
|
|
632
|
-
let
|
|
633
|
-
for (;
|
|
634
|
-
n[
|
|
635
|
-
for (;
|
|
636
|
-
for (;
|
|
637
|
-
return
|
|
638
|
-
},
|
|
683
|
+
let a = 0, l = 0;
|
|
684
|
+
for (; a < n.length && l < i.length; )
|
|
685
|
+
n[a] === i[l] ? (s("equal", n[a]), a += 1, l += 1) : o[a + 1][l] >= o[a][l + 1] ? (s("removed", n[a]), a += 1) : (s("added", i[l]), l += 1);
|
|
686
|
+
for (; a < n.length; ) s("removed", n[a++]);
|
|
687
|
+
for (; l < i.length; ) s("added", i[l++]);
|
|
688
|
+
return r;
|
|
689
|
+
}, Ue = /* @__PURE__ */ new Set([
|
|
639
690
|
"id",
|
|
640
691
|
"__component",
|
|
641
692
|
"type",
|
|
@@ -643,22 +694,22 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
643
694
|
"level",
|
|
644
695
|
"language",
|
|
645
696
|
"url"
|
|
646
|
-
]),
|
|
697
|
+
]), R = (e, t = 0) => t > 12 || e == null ? "" : typeof e == "string" ? e : typeof e == "boolean" ? "" : typeof e == "number" ? String(e) : Array.isArray(e) ? e.map((n) => R(n, t + 1)).filter(Boolean).join(" ") : typeof e == "object" ? Object.entries(e).filter(([n]) => !Ue.has(n)).map(([, n]) => R(n, t + 1)).filter(Boolean).join(" ") : "", Y = (e) => e.replace(/\s+/g, " ").trim(), ze = 6, Pe = (e, t = ze) => {
|
|
647
698
|
const n = [];
|
|
648
699
|
return e.forEach((i, o) => {
|
|
649
700
|
if (i.op !== "equal") {
|
|
650
701
|
n.push(i);
|
|
651
702
|
return;
|
|
652
703
|
}
|
|
653
|
-
const
|
|
654
|
-
if (
|
|
704
|
+
const r = i.value.match(/\S+\s*/g) ?? [];
|
|
705
|
+
if (r.length <= t * 2 + 1) {
|
|
655
706
|
n.push(i);
|
|
656
707
|
return;
|
|
657
708
|
}
|
|
658
|
-
const s = o === 0,
|
|
659
|
-
|
|
709
|
+
const s = o === 0, a = o === e.length - 1, l = s ? [] : r.slice(0, t), d = a ? [] : r.slice(-t);
|
|
710
|
+
l.length && n.push({ op: "equal", value: l.join("") }), n.push({ op: "equal", value: " … " }), d.length && n.push({ op: "equal", value: d.join("") });
|
|
660
711
|
}), n;
|
|
661
|
-
},
|
|
712
|
+
}, Ye = /* @__PURE__ */ new Set([
|
|
662
713
|
"string",
|
|
663
714
|
"text",
|
|
664
715
|
"integer",
|
|
@@ -673,18 +724,18 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
673
724
|
"uid",
|
|
674
725
|
"email",
|
|
675
726
|
"password"
|
|
676
|
-
]),
|
|
727
|
+
]), He = (e, t) => e === "text" || e === "richtext" || e === "blocks" || e === "json" || e === "customField" || e === "component" || e === "dynamiczone" || e === "string" && typeof t == "string" && t.length > 80, E = (e) => `${e.targetUid}:${e.documentId ?? e.id}`, We = ({ strapi: e }) => {
|
|
677
728
|
const t = (o) => o && {
|
|
678
729
|
id: o.id,
|
|
679
730
|
label: o.label ?? null,
|
|
680
731
|
createdAt: o.createdAt,
|
|
681
732
|
origin: o.origin
|
|
682
733
|
}, n = async (o) => {
|
|
683
|
-
const
|
|
684
|
-
if (!
|
|
685
|
-
return
|
|
734
|
+
const r = await e.db.query(D).findOne({ where: { id: o } });
|
|
735
|
+
if (!r) throw new Error(`No version with id ${o}`);
|
|
736
|
+
return r;
|
|
686
737
|
}, i = async (o) => {
|
|
687
|
-
const [
|
|
738
|
+
const [r] = await e.db.query(D).findMany({
|
|
688
739
|
where: {
|
|
689
740
|
contentType: o.contentType,
|
|
690
741
|
relatedDocumentId: o.relatedDocumentId,
|
|
@@ -694,7 +745,7 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
694
745
|
orderBy: { id: "desc" },
|
|
695
746
|
limit: 1
|
|
696
747
|
});
|
|
697
|
-
return
|
|
748
|
+
return r ?? null;
|
|
698
749
|
};
|
|
699
750
|
return {
|
|
700
751
|
/**
|
|
@@ -705,51 +756,56 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
705
756
|
* did this edit do?". The other question, "what would restoring this
|
|
706
757
|
* change?", is what `restore.preview()` already answers.
|
|
707
758
|
*/
|
|
708
|
-
async between(o,
|
|
709
|
-
const s = await n(o),
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
759
|
+
async between(o, r) {
|
|
760
|
+
const s = await n(o), a = r ? await n(r) : null;
|
|
761
|
+
if (a && (a.contentType !== s.contentType || a.relatedDocumentId !== s.relatedDocumentId || a.locale !== s.locale))
|
|
762
|
+
throw new Error("Those two versions are not of the same document and locale.");
|
|
763
|
+
let l, d;
|
|
764
|
+
a ? (l = a.id < s.id ? a : s, d = a.id < s.id ? s : a) : (l = await i(s), d = s);
|
|
765
|
+
const c = e.getModel(d.contentType)?.attributes, u = d.data ?? {}, f = l?.data ?? {}, g = d.relations ?? {}, y = l?.relations ?? {}, T = [];
|
|
766
|
+
for (const p of /* @__PURE__ */ new Set([...Object.keys(f), ...Object.keys(u)])) {
|
|
767
|
+
const h = f[p], b = u[p];
|
|
768
|
+
if (JSON.stringify(h) === JSON.stringify(b)) continue;
|
|
769
|
+
const m = c?.[p]?.type ?? "unknown", v = c?.[p]?.customField, A = h === void 0 ? "added" : b === void 0 ? "removed" : "changed";
|
|
770
|
+
if (Ye.has(m) && !He(m, h ?? b)) {
|
|
771
|
+
T.push({ field: p, type: m, customField: v, kind: A, before: h, after: b });
|
|
716
772
|
continue;
|
|
717
773
|
}
|
|
718
|
-
const
|
|
719
|
-
if (
|
|
720
|
-
|
|
774
|
+
const I = Y(R(h)), O = Y(R(b));
|
|
775
|
+
if (I === O) {
|
|
776
|
+
T.push({ field: p, type: m, customField: v, kind: A, before: void 0, after: void 0 });
|
|
721
777
|
continue;
|
|
722
778
|
}
|
|
723
|
-
|
|
724
|
-
field:
|
|
725
|
-
type:
|
|
726
|
-
customField:
|
|
727
|
-
kind:
|
|
779
|
+
T.push({
|
|
780
|
+
field: p,
|
|
781
|
+
type: m,
|
|
782
|
+
customField: v,
|
|
783
|
+
kind: A,
|
|
728
784
|
// Collapsed here rather than in the panel: sending an entire article
|
|
729
785
|
// to highlight one word wastes the payload as well as the reader's
|
|
730
786
|
// attention.
|
|
731
|
-
spans:
|
|
787
|
+
spans: Pe(Le(I, O))
|
|
732
788
|
});
|
|
733
789
|
}
|
|
734
|
-
for (const
|
|
735
|
-
const h =
|
|
736
|
-
|
|
737
|
-
field:
|
|
738
|
-
type: c?.[
|
|
790
|
+
for (const p of /* @__PURE__ */ new Set([...Object.keys(y), ...Object.keys(g)])) {
|
|
791
|
+
const h = y[p] ?? [], b = g[p] ?? [], m = new Set(h.map(E)), v = new Set(b.map(E)), A = b.filter((O) => !m.has(E(O))), I = h.filter((O) => !v.has(E(O)));
|
|
792
|
+
A.length === 0 && I.length === 0 || T.push({
|
|
793
|
+
field: p,
|
|
794
|
+
type: c?.[p]?.type ?? "relation",
|
|
739
795
|
kind: "changed",
|
|
740
|
-
linked:
|
|
741
|
-
unlinked:
|
|
796
|
+
linked: A,
|
|
797
|
+
unlinked: I
|
|
742
798
|
});
|
|
743
799
|
}
|
|
744
800
|
return {
|
|
745
|
-
from:
|
|
746
|
-
to: t(
|
|
747
|
-
changes:
|
|
748
|
-
identical:
|
|
801
|
+
from: l ? t(l) : null,
|
|
802
|
+
to: t(d),
|
|
803
|
+
changes: T,
|
|
804
|
+
identical: T.length === 0
|
|
749
805
|
};
|
|
750
806
|
}
|
|
751
807
|
};
|
|
752
|
-
},
|
|
808
|
+
}, Ke = ({ strapi: e }) => {
|
|
753
809
|
const t = (n) => ({
|
|
754
810
|
type: "plugin",
|
|
755
811
|
name: "rewind",
|
|
@@ -767,56 +823,56 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
767
823
|
await e.store.set({ ...t(n), value: { expires: 0 } });
|
|
768
824
|
}
|
|
769
825
|
};
|
|
770
|
-
},
|
|
826
|
+
}, Ge = ({ strapi: e }) => ({
|
|
771
827
|
async set(t, n) {
|
|
772
828
|
if (!Number.isInteger(t))
|
|
773
829
|
throw new Error("A version id is required.");
|
|
774
|
-
const i = await e.db.query(
|
|
830
|
+
const i = await e.db.query(D).findOne({ where: { id: t }, select: ["id", "pinned"] });
|
|
775
831
|
if (!i)
|
|
776
832
|
throw new Error(`No version with id ${t}`);
|
|
777
|
-
return !!i.pinned === n ? { id: t, pinned: n } : (await e.db.query(
|
|
833
|
+
return !!i.pinned === n ? { id: t, pinned: n } : (await e.db.query(D).update({ where: { id: t }, data: { pinned: n } }), { id: t, pinned: n });
|
|
778
834
|
}
|
|
779
|
-
}),
|
|
835
|
+
}), J = {
|
|
780
836
|
keepAllDays: 7,
|
|
781
837
|
dailyUntilDays: 30,
|
|
782
838
|
maxAgeDays: 365,
|
|
783
839
|
keepAnchors: !0
|
|
784
|
-
},
|
|
840
|
+
}, Ve = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), Z = 24 * 60 * 60 * 1e3, Xe = (e, t) => (t.getTime() - new Date(e).getTime()) / Z, Je = (e) => e.toISOString().slice(0, 10), Ze = (e) => {
|
|
785
841
|
const t = new Date(Date.UTC(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate())), n = t.getUTCDay() || 7;
|
|
786
842
|
t.setUTCDate(t.getUTCDate() + 4 - n);
|
|
787
|
-
const i = new Date(Date.UTC(t.getUTCFullYear(), 0, 1)), o = Math.ceil(((t.getTime() - i.getTime()) /
|
|
843
|
+
const i = new Date(Date.UTC(t.getUTCFullYear(), 0, 1)), o = Math.ceil(((t.getTime() - i.getTime()) / Z + 1) / 7);
|
|
788
844
|
return `${t.getUTCFullYear()}-W${o}`;
|
|
789
|
-
},
|
|
790
|
-
const i = /* @__PURE__ */ new Map(), o = [],
|
|
791
|
-
(s,
|
|
845
|
+
}, Qe = (e, t, n = J) => {
|
|
846
|
+
const i = /* @__PURE__ */ new Map(), o = [], r = [...e].sort(
|
|
847
|
+
(s, a) => new Date(a.createdAt).getTime() - new Date(s.createdAt).getTime()
|
|
792
848
|
);
|
|
793
|
-
for (const s of
|
|
794
|
-
if (s.pinned || n.keepAnchors &&
|
|
795
|
-
const
|
|
796
|
-
if (
|
|
797
|
-
if (
|
|
849
|
+
for (const s of r) {
|
|
850
|
+
if (s.pinned || n.keepAnchors && Ve.has(s.origin)) continue;
|
|
851
|
+
const a = Xe(s.createdAt, t);
|
|
852
|
+
if (a <= n.keepAllDays) continue;
|
|
853
|
+
if (a > n.maxAgeDays) {
|
|
798
854
|
o.push(s.id);
|
|
799
855
|
continue;
|
|
800
856
|
}
|
|
801
|
-
const
|
|
802
|
-
i.has(
|
|
857
|
+
const l = new Date(s.createdAt), d = a <= n.dailyUntilDays ? `d:${Je(l)}` : `w:${Ze(l)}`;
|
|
858
|
+
i.has(d) ? o.push(s.id) : i.set(d, s);
|
|
803
859
|
}
|
|
804
860
|
return o;
|
|
805
|
-
},
|
|
806
|
-
const t = () => e.db.query(
|
|
861
|
+
}, H = 500, xe = ({ strapi: e }) => {
|
|
862
|
+
const t = () => e.db.query(D), n = async (i) => {
|
|
807
863
|
const o = await t().findMany({
|
|
808
864
|
select: ["contentType", "relatedDocumentId", "locale"],
|
|
809
865
|
where: { createdAt: { $lt: i } }
|
|
810
|
-
}),
|
|
866
|
+
}), r = /* @__PURE__ */ new Map();
|
|
811
867
|
for (const s of o) {
|
|
812
|
-
const
|
|
813
|
-
|
|
868
|
+
const a = `${s.contentType}|${s.relatedDocumentId}|${s.locale ?? ""}`;
|
|
869
|
+
r.has(a) || r.set(a, {
|
|
814
870
|
contentType: s.contentType,
|
|
815
871
|
relatedDocumentId: s.relatedDocumentId,
|
|
816
872
|
locale: s.locale ?? null
|
|
817
873
|
});
|
|
818
874
|
}
|
|
819
|
-
return [...
|
|
875
|
+
return [...r.values()];
|
|
820
876
|
};
|
|
821
877
|
return {
|
|
822
878
|
/**
|
|
@@ -826,37 +882,42 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
826
882
|
* report it and a test can assert on it.
|
|
827
883
|
*/
|
|
828
884
|
async prune(i = /* @__PURE__ */ new Date()) {
|
|
829
|
-
const o = e.config.get("plugin::rewind"),
|
|
885
|
+
const o = e.config.get("plugin::rewind"), r = { ...J, ...o?.retention };
|
|
830
886
|
if (o?.retention?.enabled === !1)
|
|
831
887
|
return { scanned: 0, deleted: 0, documents: 0 };
|
|
832
|
-
const s = new Date(i.getTime() -
|
|
833
|
-
let
|
|
834
|
-
for (const
|
|
835
|
-
const
|
|
888
|
+
const s = new Date(i.getTime() - r.keepAllDays * 24 * 60 * 60 * 1e3), a = await n(s);
|
|
889
|
+
let l = 0, d = 0;
|
|
890
|
+
for (const c of a) {
|
|
891
|
+
const u = await t().findMany({
|
|
836
892
|
select: ["id", "createdAt", "origin", "pinned"],
|
|
837
893
|
where: {
|
|
838
|
-
contentType:
|
|
839
|
-
relatedDocumentId:
|
|
840
|
-
locale:
|
|
894
|
+
contentType: c.contentType,
|
|
895
|
+
relatedDocumentId: c.relatedDocumentId,
|
|
896
|
+
locale: c.locale
|
|
841
897
|
}
|
|
842
898
|
});
|
|
843
|
-
|
|
844
|
-
const
|
|
845
|
-
for (let
|
|
846
|
-
const
|
|
847
|
-
await t().deleteMany({ where: { id: { $in:
|
|
899
|
+
l += u.length;
|
|
900
|
+
const f = Qe(u, i, r);
|
|
901
|
+
for (let g = 0; g < f.length; g += H) {
|
|
902
|
+
const y = f.slice(g, g + H);
|
|
903
|
+
await t().deleteMany({ where: { id: { $in: y } } }), d += y.length;
|
|
848
904
|
}
|
|
849
905
|
}
|
|
850
|
-
return { scanned:
|
|
906
|
+
return { scanned: l, deleted: d, documents: a.length };
|
|
851
907
|
}
|
|
852
908
|
};
|
|
853
|
-
},
|
|
909
|
+
}, et = (e) => {
|
|
910
|
+
const t = e.map((i) => i.label).filter(Boolean);
|
|
911
|
+
if (t.length === 0) return `${e.length} target(s)`;
|
|
912
|
+
const n = e.length - t.length;
|
|
913
|
+
return n ? `${t.join(", ")} and ${n} more` : t.join(", ");
|
|
914
|
+
}, tt = ({ strapi: e }) => {
|
|
854
915
|
const t = (s) => e.getModel(s), n = async (s) => {
|
|
855
|
-
const
|
|
856
|
-
if (!
|
|
916
|
+
const a = await e.db.query(D).findOne({ where: { id: s } });
|
|
917
|
+
if (!a)
|
|
857
918
|
throw new Error(`No version with id ${s}`);
|
|
858
|
-
return
|
|
859
|
-
}, i = (s) => s?.pluginOptions?.i18n?.localized === !0, o = (s) => e.contentTypes[s]?.pluginOptions?.i18n?.localized === !0,
|
|
919
|
+
return a;
|
|
920
|
+
}, i = (s) => s?.pluginOptions?.i18n?.localized === !0, o = (s) => e.contentTypes[s]?.pluginOptions?.i18n?.localized === !0, r = async (s) => s.targetUid === "plugin::upload.file" ? !!await e.db.query("plugin::upload.file").findOne({ where: { id: s.id } }) : !!await e.db.query(s.targetUid).findOne({
|
|
860
921
|
where: {
|
|
861
922
|
documentId: s.documentId,
|
|
862
923
|
...s.locale ? { locale: s.locale } : {}
|
|
@@ -872,21 +933,21 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
872
933
|
* version of a document quietly rewrites a field in every other language.
|
|
873
934
|
*/
|
|
874
935
|
async preview(s) {
|
|
875
|
-
const
|
|
876
|
-
for (const [
|
|
877
|
-
if (
|
|
878
|
-
for (const
|
|
879
|
-
await
|
|
880
|
-
const
|
|
936
|
+
const a = await n(s), l = a.contentType, d = t(l).attributes, c = a.schemaSnapshot?.attributes ?? {}, u = a.data ?? {}, f = a.relations ?? {}, g = Object.keys(d).filter((w) => !q.includes(w)), y = Object.keys(c), T = y.filter((w) => !g.includes(w)), p = g.filter((w) => !y.includes(w)), h = y.filter((w) => g.includes(w)), b = h.filter((w) => $(d[w])), m = h.filter((w) => !b.includes(w)), v = o(l) ? m.filter((w) => !i(d[w])) : [], A = [], I = [];
|
|
937
|
+
for (const [w, Q] of Object.entries(f))
|
|
938
|
+
if (m.includes(w))
|
|
939
|
+
for (const k of Q)
|
|
940
|
+
await r(k) || (k.targetUid === "plugin::upload.file" ? I.push(k) : A.push(k));
|
|
941
|
+
const O = v.length ? await e.plugin("i18n")?.service("locales").find() : [];
|
|
881
942
|
return {
|
|
882
|
-
fieldsToRestore:
|
|
883
|
-
fieldsKeptAsIs:
|
|
884
|
-
fieldsDropped:
|
|
885
|
-
crossLocaleFields:
|
|
886
|
-
unsupportedFields:
|
|
943
|
+
fieldsToRestore: m.filter((w) => w in u),
|
|
944
|
+
fieldsKeptAsIs: p,
|
|
945
|
+
fieldsDropped: T,
|
|
946
|
+
crossLocaleFields: v,
|
|
947
|
+
unsupportedFields: b,
|
|
887
948
|
brokenRelations: A,
|
|
888
|
-
missingMedia:
|
|
889
|
-
affectedLocales:
|
|
949
|
+
missingMedia: I,
|
|
950
|
+
affectedLocales: O.map((w) => w.code)
|
|
890
951
|
};
|
|
891
952
|
},
|
|
892
953
|
/**
|
|
@@ -895,69 +956,71 @@ const Q = ["id", "documentId", "locale"], S = [
|
|
|
895
956
|
* The document lands in "modified" and publishing stays a deliberate act -
|
|
896
957
|
* restoring must never change what the public sees on its own.
|
|
897
958
|
*/
|
|
898
|
-
async apply(s,
|
|
899
|
-
const
|
|
959
|
+
async apply(s, a) {
|
|
960
|
+
const l = await n(s), d = l.contentType, c = await this.preview(s), u = [];
|
|
900
961
|
await e.plugin("rewind").service("snapshot").capture(
|
|
901
962
|
{
|
|
902
|
-
uid:
|
|
903
|
-
relatedDocumentId:
|
|
904
|
-
locale:
|
|
963
|
+
uid: d,
|
|
964
|
+
relatedDocumentId: l.relatedDocumentId,
|
|
965
|
+
locale: l.locale,
|
|
905
966
|
origin: "restore",
|
|
906
967
|
before: null
|
|
907
968
|
},
|
|
908
|
-
|
|
969
|
+
a
|
|
909
970
|
);
|
|
910
|
-
const
|
|
911
|
-
for (const
|
|
912
|
-
|
|
913
|
-
const
|
|
914
|
-
[...
|
|
915
|
-
(
|
|
971
|
+
const f = {}, g = l.data ?? {}, y = l.relations ?? {};
|
|
972
|
+
for (const p of c.fieldsToRestore)
|
|
973
|
+
f[p] = g[p];
|
|
974
|
+
const T = new Set(
|
|
975
|
+
[...c.brokenRelations, ...c.missingMedia].map(
|
|
976
|
+
(p) => `${p.targetUid}:${p.documentId ?? p.id}`
|
|
916
977
|
)
|
|
917
978
|
);
|
|
918
|
-
for (const [
|
|
919
|
-
if (
|
|
920
|
-
const
|
|
921
|
-
(
|
|
979
|
+
for (const [p, h] of Object.entries(y)) {
|
|
980
|
+
if (c.unsupportedFields.includes(p) || c.fieldsDropped.includes(p)) continue;
|
|
981
|
+
const b = h.filter(
|
|
982
|
+
(m) => !T.has(`${m.targetUid}:${m.documentId ?? m.id}`)
|
|
922
983
|
);
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
984
|
+
if (b.length !== h.length) {
|
|
985
|
+
const m = h.filter((v) => !b.includes(v));
|
|
986
|
+
u.push(`${et(m)} of "${p}" no longer exist and were skipped.`);
|
|
987
|
+
}
|
|
988
|
+
f[p] = b.map(
|
|
989
|
+
(m) => m.targetUid === "plugin::upload.file" ? m.id : m.documentId
|
|
927
990
|
);
|
|
928
991
|
}
|
|
929
|
-
return
|
|
930
|
-
`Skipped ${
|
|
931
|
-
),
|
|
932
|
-
`${
|
|
933
|
-
), await
|
|
934
|
-
await e.documents(
|
|
935
|
-
documentId:
|
|
936
|
-
locale:
|
|
992
|
+
return c.fieldsDropped.length && u.push(
|
|
993
|
+
`Skipped ${c.fieldsDropped.length} field(s) no longer in the model: ${c.fieldsDropped.join(", ")}.`
|
|
994
|
+
), c.crossLocaleFields.length && u.push(
|
|
995
|
+
`${c.crossLocaleFields.join(", ")} are not localised, so they changed in every locale.`
|
|
996
|
+
), await me(async () => {
|
|
997
|
+
await e.documents(d).update({
|
|
998
|
+
documentId: l.relatedDocumentId,
|
|
999
|
+
locale: l.locale ?? void 0,
|
|
937
1000
|
// Explicit, though update() forces draft anyway: passing 'published'
|
|
938
1001
|
// here would publish, and that must never happen by accident.
|
|
939
1002
|
status: "draft",
|
|
940
|
-
data:
|
|
1003
|
+
data: f
|
|
941
1004
|
});
|
|
942
|
-
}), { documentId:
|
|
1005
|
+
}), { documentId: l.relatedDocumentId, warnings: u };
|
|
943
1006
|
}
|
|
944
1007
|
};
|
|
945
|
-
},
|
|
946
|
-
snapshot:
|
|
947
|
-
restore:
|
|
948
|
-
diff:
|
|
949
|
-
retention:
|
|
950
|
-
lock:
|
|
951
|
-
pin:
|
|
952
|
-
},
|
|
953
|
-
bootstrap:
|
|
954
|
-
destroy:
|
|
955
|
-
config:
|
|
956
|
-
contentTypes:
|
|
957
|
-
controllers:
|
|
958
|
-
routes:
|
|
959
|
-
services:
|
|
1008
|
+
}, nt = {
|
|
1009
|
+
snapshot: ue,
|
|
1010
|
+
restore: tt,
|
|
1011
|
+
diff: We,
|
|
1012
|
+
retention: xe,
|
|
1013
|
+
lock: Ke,
|
|
1014
|
+
pin: Ge
|
|
1015
|
+
}, it = {
|
|
1016
|
+
bootstrap: Te,
|
|
1017
|
+
destroy: Ne,
|
|
1018
|
+
config: Ie,
|
|
1019
|
+
contentTypes: Fe,
|
|
1020
|
+
controllers: Me,
|
|
1021
|
+
routes: Be,
|
|
1022
|
+
services: nt
|
|
960
1023
|
};
|
|
961
1024
|
export {
|
|
962
|
-
|
|
1025
|
+
it as default
|
|
963
1026
|
};
|