@qkix/strapi-plugin-rewind 0.3.0 → 0.4.1
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 +19 -4
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +167 -163
- package/dist/admin/src/diffRegistry.d.ts +16 -10
- package/dist/server/index.js +2 -2
- package/dist/server/index.mjs +442 -386
- 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"], E = [
|
|
|
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,131 +104,126 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
50
104
|
},
|
|
51
105
|
{}
|
|
52
106
|
);
|
|
53
|
-
},
|
|
54
|
-
const n = {},
|
|
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: N(o,
|
|
120
|
+
attributes: N(o, q),
|
|
67
121
|
components: n
|
|
68
122
|
};
|
|
69
|
-
}, N = (e, t) => Object.fromEntries(Object.entries(e ?? {}).filter(([n]) => !t.includes(n))),
|
|
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
|
|
92
|
-
return Array.isArray(n) ? n.map((o) =>
|
|
157
|
+
const i = (o) => M(e, t.component, o);
|
|
158
|
+
return Array.isArray(n) ? n.map((o) => i(o)) : i(n);
|
|
93
159
|
}
|
|
94
|
-
return t.type === "dynamiczone" && Array.isArray(n) ? n.map((
|
|
95
|
-
const o =
|
|
96
|
-
return
|
|
160
|
+
return t.type === "dynamiczone" && Array.isArray(n) ? n.map((i) => {
|
|
161
|
+
const o = i;
|
|
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
|
|
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(r, o), o;
|
|
116
|
-
};
|
|
117
|
-
return async (r, o) => {
|
|
118
|
-
if (!o) return null;
|
|
119
|
-
const s = [await n(r), "title", "name", "label"].filter(Boolean);
|
|
120
|
-
for (const i of s) {
|
|
121
|
-
const c = o[i];
|
|
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 = W(e), n = (i) => !!e.contentTypes[i]?.options?.draftAndPublish, r = (i) => !!(e.getModel(i)?.pluginOptions?.i18n?.localized ?? e.contentTypes[i]?.pluginOptions?.i18n?.localized), o = async (i, c, l) => {
|
|
129
|
-
const d = r(i) && l.filter(Boolean).length > 0 ? { locale: { $in: l.filter(Boolean) } } : {};
|
|
130
|
-
return e.db.query(i).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
|
});
|
|
@@ -189,97 +238,97 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
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
|
-
const
|
|
247
|
+
const i = `${n.uid}:${n.relatedDocumentId}:${n.locale ?? ""}`, o = t.get(i);
|
|
199
248
|
if (!o) {
|
|
200
|
-
t.set(
|
|
249
|
+
t.set(i, n);
|
|
201
250
|
continue;
|
|
202
251
|
}
|
|
203
|
-
const
|
|
204
|
-
t.set(
|
|
252
|
+
const r = C.indexOf(n.origin) < C.indexOf(o.origin);
|
|
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
|
-
},
|
|
213
|
-
const n = e.db.query(t),
|
|
214
|
-
if (
|
|
215
|
-
e.log.info(`[rewind] labelling ${
|
|
216
|
-
const o =
|
|
217
|
-
let
|
|
261
|
+
}, X = new ee(), me = (e) => X.run({ active: !0 }, e), ge = () => X.getStore()?.active === !0, ye = 200, he = async (e, t) => {
|
|
262
|
+
const n = e.db.query(t), i = await n.count({ where: { label: null } });
|
|
263
|
+
if (i === 0) return 0;
|
|
264
|
+
e.log.info(`[rewind] labelling ${i} older version(s)…`);
|
|
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
|
-
),
|
|
254
|
-
const n = () => e.plugin("rewind").service("snapshot"),
|
|
255
|
-
if (!
|
|
256
|
-
const
|
|
257
|
-
return !(!
|
|
302
|
+
), Ae(e, t);
|
|
303
|
+
const n = () => e.plugin("rewind").service("snapshot"), i = (o) => {
|
|
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 (!
|
|
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 T =
|
|
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,19 +338,19 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
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() {
|
|
296
345
|
const n = e.plugin("rewind").service("lock");
|
|
297
346
|
if (await n.acquire("prune", 30 * 60 * 1e3))
|
|
298
347
|
try {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
`[rewind] thinned ${
|
|
348
|
+
const i = await e.plugin("rewind").service("retention").prune();
|
|
349
|
+
i.deleted > 0 && e.log.info(
|
|
350
|
+
`[rewind] thinned ${i.deleted} version(s) across ${i.documents} document(s)`
|
|
302
351
|
);
|
|
303
|
-
} catch (
|
|
304
|
-
e.log.error("[rewind] thinning failed",
|
|
352
|
+
} catch (i) {
|
|
353
|
+
e.log.error("[rewind] thinning failed", i);
|
|
305
354
|
} finally {
|
|
306
355
|
await n.release("prune");
|
|
307
356
|
}
|
|
@@ -309,7 +358,7 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
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"], E = [
|
|
|
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;
|
|
@@ -371,21 +420,21 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
371
420
|
throw new Error(
|
|
372
421
|
'[rewind] config.contentTypes must be an array of content type uids, e.g. ["api::article.article"]'
|
|
373
422
|
);
|
|
374
|
-
const
|
|
375
|
-
if (
|
|
423
|
+
const i = t.filter((o) => !o.startsWith("api::"));
|
|
424
|
+
if (i.length > 0)
|
|
376
425
|
throw new Error(
|
|
377
|
-
`[rewind] config.contentTypes only supports api:: content types, received: ${
|
|
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"], E = [
|
|
|
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"], E = [
|
|
|
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,34 +526,34 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
477
526
|
"pinned",
|
|
478
527
|
"contentHash",
|
|
479
528
|
"createdAt"
|
|
480
|
-
],
|
|
529
|
+
], je = ({ strapi: e }) => ({
|
|
481
530
|
async find(t) {
|
|
482
|
-
const { contentType: n, documentId:
|
|
483
|
-
if (!n || !
|
|
531
|
+
const { contentType: n, documentId: i, locale: o, page: r = 1, pageSize: s = 10 } = t.query;
|
|
532
|
+
if (!n || !i)
|
|
484
533
|
return t.badRequest("contentType and documentId are required.");
|
|
485
|
-
const
|
|
534
|
+
const a = {
|
|
486
535
|
contentType: n,
|
|
487
|
-
relatedDocumentId:
|
|
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
|
|
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
|
-
t.body = { data: (await
|
|
556
|
+
t.body = { data: (await U(e, [n]))[0] };
|
|
508
557
|
},
|
|
509
558
|
async diff(t) {
|
|
510
559
|
const { against: n } = t.query;
|
|
@@ -514,9 +563,9 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
514
563
|
t.body = {
|
|
515
564
|
data: await e.plugin("rewind").service("diff").between(Number(t.params.id), n ? Number(n) : void 0)
|
|
516
565
|
};
|
|
517
|
-
} catch (
|
|
566
|
+
} catch (i) {
|
|
518
567
|
return t.badRequest(
|
|
519
|
-
|
|
568
|
+
i instanceof Error ? i.message : "Could not compare these versions."
|
|
520
569
|
);
|
|
521
570
|
}
|
|
522
571
|
},
|
|
@@ -542,8 +591,8 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
542
591
|
t.body = {
|
|
543
592
|
data: await e.plugin("rewind").service("pin").set(Number(t.params.id), n)
|
|
544
593
|
};
|
|
545
|
-
} catch (
|
|
546
|
-
return t.notFound(
|
|
594
|
+
} catch (i) {
|
|
595
|
+
return t.notFound(i instanceof Error ? i.message : "No such version.");
|
|
547
596
|
}
|
|
548
597
|
},
|
|
549
598
|
async restore(t) {
|
|
@@ -555,23 +604,23 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
555
604
|
return t.badRequest(n instanceof Error ? n.message : "Restore failed.");
|
|
556
605
|
}
|
|
557
606
|
}
|
|
558
|
-
}),
|
|
559
|
-
const n = [...new Set(t.map((
|
|
560
|
-
if (n.length === 0) return t.map((
|
|
561
|
-
const
|
|
562
|
-
return t.map((
|
|
563
|
-
const s = o.get(
|
|
607
|
+
}), U = async (e, t) => {
|
|
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);
|
|
564
613
|
return {
|
|
565
|
-
...
|
|
614
|
+
...r,
|
|
566
615
|
user: s ? {
|
|
567
616
|
id: s.id,
|
|
568
617
|
name: [s.firstname, s.lastname].filter(Boolean).join(" ") || s.email
|
|
569
618
|
} : null
|
|
570
619
|
};
|
|
571
620
|
});
|
|
572
|
-
},
|
|
573
|
-
version:
|
|
574
|
-
},
|
|
621
|
+
}, Me = {
|
|
622
|
+
version: je
|
|
623
|
+
}, Ce = {
|
|
575
624
|
type: "admin",
|
|
576
625
|
routes: [
|
|
577
626
|
{
|
|
@@ -611,33 +660,33 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
611
660
|
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
612
661
|
}
|
|
613
662
|
]
|
|
614
|
-
},
|
|
615
|
-
admin:
|
|
616
|
-
},
|
|
617
|
-
const n =
|
|
618
|
-
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)
|
|
619
668
|
return [
|
|
620
669
|
{ op: "removed", value: e },
|
|
621
670
|
{ op: "added", value: t }
|
|
622
671
|
];
|
|
623
672
|
const o = Array.from(
|
|
624
673
|
{ length: n.length + 1 },
|
|
625
|
-
() => new Array(
|
|
674
|
+
() => new Array(i.length + 1).fill(0)
|
|
626
675
|
);
|
|
627
|
-
for (let
|
|
628
|
-
for (let
|
|
629
|
-
o[
|
|
630
|
-
const
|
|
631
|
-
const
|
|
632
|
-
|
|
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 });
|
|
633
682
|
};
|
|
634
|
-
let
|
|
635
|
-
for (;
|
|
636
|
-
n[
|
|
637
|
-
for (;
|
|
638
|
-
for (;
|
|
639
|
-
return
|
|
640
|
-
},
|
|
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([
|
|
641
690
|
"id",
|
|
642
691
|
"__component",
|
|
643
692
|
"type",
|
|
@@ -645,22 +694,22 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
645
694
|
"level",
|
|
646
695
|
"language",
|
|
647
696
|
"url"
|
|
648
|
-
]), 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]) => !
|
|
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) => {
|
|
649
698
|
const n = [];
|
|
650
|
-
return e.forEach((
|
|
651
|
-
if (
|
|
652
|
-
n.push(
|
|
699
|
+
return e.forEach((i, o) => {
|
|
700
|
+
if (i.op !== "equal") {
|
|
701
|
+
n.push(i);
|
|
653
702
|
return;
|
|
654
703
|
}
|
|
655
|
-
const
|
|
656
|
-
if (
|
|
657
|
-
n.push(
|
|
704
|
+
const r = i.value.match(/\S+\s*/g) ?? [];
|
|
705
|
+
if (r.length <= t * 2 + 1) {
|
|
706
|
+
n.push(i);
|
|
658
707
|
return;
|
|
659
708
|
}
|
|
660
|
-
const s = o === 0,
|
|
661
|
-
|
|
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("") });
|
|
662
711
|
}), n;
|
|
663
|
-
},
|
|
712
|
+
}, Ye = /* @__PURE__ */ new Set([
|
|
664
713
|
"string",
|
|
665
714
|
"text",
|
|
666
715
|
"integer",
|
|
@@ -675,18 +724,18 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
675
724
|
"uid",
|
|
676
725
|
"email",
|
|
677
726
|
"password"
|
|
678
|
-
]),
|
|
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 }) => {
|
|
679
728
|
const t = (o) => o && {
|
|
680
729
|
id: o.id,
|
|
681
730
|
label: o.label ?? null,
|
|
682
731
|
createdAt: o.createdAt,
|
|
683
732
|
origin: o.origin
|
|
684
733
|
}, n = async (o) => {
|
|
685
|
-
const
|
|
686
|
-
if (!
|
|
687
|
-
return
|
|
688
|
-
},
|
|
689
|
-
const [
|
|
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;
|
|
737
|
+
}, i = async (o) => {
|
|
738
|
+
const [r] = await e.db.query(D).findMany({
|
|
690
739
|
where: {
|
|
691
740
|
contentType: o.contentType,
|
|
692
741
|
relatedDocumentId: o.relatedDocumentId,
|
|
@@ -696,7 +745,7 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
696
745
|
orderBy: { id: "desc" },
|
|
697
746
|
limit: 1
|
|
698
747
|
});
|
|
699
|
-
return
|
|
748
|
+
return r ?? null;
|
|
700
749
|
};
|
|
701
750
|
return {
|
|
702
751
|
/**
|
|
@@ -707,123 +756,123 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
707
756
|
* did this edit do?". The other question, "what would restoring this
|
|
708
757
|
* change?", is what `restore.preview()` already answers.
|
|
709
758
|
*/
|
|
710
|
-
async between(o,
|
|
711
|
-
const s = await n(o),
|
|
712
|
-
if (
|
|
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))
|
|
713
762
|
throw new Error("Those two versions are not of the same document and locale.");
|
|
714
|
-
let
|
|
715
|
-
|
|
716
|
-
const
|
|
717
|
-
for (const
|
|
718
|
-
const
|
|
719
|
-
if (JSON.stringify(
|
|
720
|
-
const
|
|
721
|
-
if (
|
|
722
|
-
T.push({ field:
|
|
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 });
|
|
723
772
|
continue;
|
|
724
773
|
}
|
|
725
|
-
const I =
|
|
774
|
+
const I = Y(R(h)), O = Y(R(b));
|
|
726
775
|
if (I === O) {
|
|
727
|
-
T.push({ field:
|
|
776
|
+
T.push({ field: p, type: m, customField: v, kind: A, before: void 0, after: void 0 });
|
|
728
777
|
continue;
|
|
729
778
|
}
|
|
730
779
|
T.push({
|
|
731
|
-
field:
|
|
732
|
-
type:
|
|
733
|
-
customField:
|
|
780
|
+
field: p,
|
|
781
|
+
type: m,
|
|
782
|
+
customField: v,
|
|
734
783
|
kind: A,
|
|
735
784
|
// Collapsed here rather than in the panel: sending an entire article
|
|
736
785
|
// to highlight one word wastes the payload as well as the reader's
|
|
737
786
|
// attention.
|
|
738
|
-
spans:
|
|
787
|
+
spans: Pe(Le(I, O))
|
|
739
788
|
});
|
|
740
789
|
}
|
|
741
|
-
for (const
|
|
742
|
-
const
|
|
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)));
|
|
743
792
|
A.length === 0 && I.length === 0 || T.push({
|
|
744
|
-
field:
|
|
745
|
-
type:
|
|
793
|
+
field: p,
|
|
794
|
+
type: c?.[p]?.type ?? "relation",
|
|
746
795
|
kind: "changed",
|
|
747
796
|
linked: A,
|
|
748
797
|
unlinked: I
|
|
749
798
|
});
|
|
750
799
|
}
|
|
751
800
|
return {
|
|
752
|
-
from:
|
|
753
|
-
to: t(
|
|
801
|
+
from: l ? t(l) : null,
|
|
802
|
+
to: t(d),
|
|
754
803
|
changes: T,
|
|
755
804
|
identical: T.length === 0
|
|
756
805
|
};
|
|
757
806
|
}
|
|
758
807
|
};
|
|
759
|
-
},
|
|
808
|
+
}, Ke = ({ strapi: e }) => {
|
|
760
809
|
const t = (n) => ({
|
|
761
810
|
type: "plugin",
|
|
762
811
|
name: "rewind",
|
|
763
812
|
key: `lock:${n}`
|
|
764
813
|
});
|
|
765
814
|
return {
|
|
766
|
-
async acquire(n,
|
|
815
|
+
async acquire(n, i) {
|
|
767
816
|
const o = await e.store.get(t(n));
|
|
768
817
|
return o && o.expires > Date.now() ? !1 : (await e.store.set({
|
|
769
818
|
...t(n),
|
|
770
|
-
value: { expires: Date.now() +
|
|
819
|
+
value: { expires: Date.now() + i }
|
|
771
820
|
}), !0);
|
|
772
821
|
},
|
|
773
822
|
async release(n) {
|
|
774
823
|
await e.store.set({ ...t(n), value: { expires: 0 } });
|
|
775
824
|
}
|
|
776
825
|
};
|
|
777
|
-
},
|
|
826
|
+
}, Ge = ({ strapi: e }) => ({
|
|
778
827
|
async set(t, n) {
|
|
779
828
|
if (!Number.isInteger(t))
|
|
780
829
|
throw new Error("A version id is required.");
|
|
781
|
-
const
|
|
782
|
-
if (!
|
|
830
|
+
const i = await e.db.query(D).findOne({ where: { id: t }, select: ["id", "pinned"] });
|
|
831
|
+
if (!i)
|
|
783
832
|
throw new Error(`No version with id ${t}`);
|
|
784
|
-
return !!
|
|
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 });
|
|
785
834
|
}
|
|
786
|
-
}),
|
|
835
|
+
}), J = {
|
|
787
836
|
keepAllDays: 7,
|
|
788
837
|
dailyUntilDays: 30,
|
|
789
838
|
maxAgeDays: 365,
|
|
790
839
|
keepAnchors: !0
|
|
791
|
-
},
|
|
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) => {
|
|
792
841
|
const t = new Date(Date.UTC(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate())), n = t.getUTCDay() || 7;
|
|
793
842
|
t.setUTCDate(t.getUTCDate() + 4 - n);
|
|
794
|
-
const
|
|
843
|
+
const i = new Date(Date.UTC(t.getUTCFullYear(), 0, 1)), o = Math.ceil(((t.getTime() - i.getTime()) / Z + 1) / 7);
|
|
795
844
|
return `${t.getUTCFullYear()}-W${o}`;
|
|
796
|
-
},
|
|
797
|
-
const
|
|
798
|
-
(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()
|
|
799
848
|
);
|
|
800
|
-
for (const s of
|
|
801
|
-
if (s.pinned || n.keepAnchors &&
|
|
802
|
-
const
|
|
803
|
-
if (
|
|
804
|
-
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) {
|
|
805
854
|
o.push(s.id);
|
|
806
855
|
continue;
|
|
807
856
|
}
|
|
808
|
-
const
|
|
809
|
-
|
|
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);
|
|
810
859
|
}
|
|
811
860
|
return o;
|
|
812
|
-
},
|
|
813
|
-
const t = () => e.db.query(
|
|
861
|
+
}, H = 500, xe = ({ strapi: e }) => {
|
|
862
|
+
const t = () => e.db.query(D), n = async (i) => {
|
|
814
863
|
const o = await t().findMany({
|
|
815
864
|
select: ["contentType", "relatedDocumentId", "locale"],
|
|
816
|
-
where: { createdAt: { $lt:
|
|
817
|
-
}),
|
|
865
|
+
where: { createdAt: { $lt: i } }
|
|
866
|
+
}), r = /* @__PURE__ */ new Map();
|
|
818
867
|
for (const s of o) {
|
|
819
|
-
const
|
|
820
|
-
|
|
868
|
+
const a = `${s.contentType}|${s.relatedDocumentId}|${s.locale ?? ""}`;
|
|
869
|
+
r.has(a) || r.set(a, {
|
|
821
870
|
contentType: s.contentType,
|
|
822
871
|
relatedDocumentId: s.relatedDocumentId,
|
|
823
872
|
locale: s.locale ?? null
|
|
824
873
|
});
|
|
825
874
|
}
|
|
826
|
-
return [...
|
|
875
|
+
return [...r.values()];
|
|
827
876
|
};
|
|
828
877
|
return {
|
|
829
878
|
/**
|
|
@@ -832,38 +881,43 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
832
881
|
* Returns what it did rather than logging and forgetting, so the cron can
|
|
833
882
|
* report it and a test can assert on it.
|
|
834
883
|
*/
|
|
835
|
-
async prune(
|
|
836
|
-
const o = e.config.get("plugin::rewind"),
|
|
884
|
+
async prune(i = /* @__PURE__ */ new Date()) {
|
|
885
|
+
const o = e.config.get("plugin::rewind"), r = { ...J, ...o?.retention };
|
|
837
886
|
if (o?.retention?.enabled === !1)
|
|
838
887
|
return { scanned: 0, deleted: 0, documents: 0 };
|
|
839
|
-
const s = new Date(
|
|
840
|
-
let
|
|
841
|
-
for (const
|
|
842
|
-
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({
|
|
843
892
|
select: ["id", "createdAt", "origin", "pinned"],
|
|
844
893
|
where: {
|
|
845
|
-
contentType:
|
|
846
|
-
relatedDocumentId:
|
|
847
|
-
locale:
|
|
894
|
+
contentType: c.contentType,
|
|
895
|
+
relatedDocumentId: c.relatedDocumentId,
|
|
896
|
+
locale: c.locale
|
|
848
897
|
}
|
|
849
898
|
});
|
|
850
|
-
|
|
851
|
-
const
|
|
852
|
-
for (let
|
|
853
|
-
const
|
|
854
|
-
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;
|
|
855
904
|
}
|
|
856
905
|
}
|
|
857
|
-
return { scanned:
|
|
906
|
+
return { scanned: l, deleted: d, documents: a.length };
|
|
858
907
|
}
|
|
859
908
|
};
|
|
860
|
-
},
|
|
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 }) => {
|
|
861
915
|
const t = (s) => e.getModel(s), n = async (s) => {
|
|
862
|
-
const
|
|
863
|
-
if (!
|
|
916
|
+
const a = await e.db.query(D).findOne({ where: { id: s } });
|
|
917
|
+
if (!a)
|
|
864
918
|
throw new Error(`No version with id ${s}`);
|
|
865
|
-
return
|
|
866
|
-
},
|
|
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({
|
|
867
921
|
where: {
|
|
868
922
|
documentId: s.documentId,
|
|
869
923
|
...s.locale ? { locale: s.locale } : {}
|
|
@@ -879,21 +933,21 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
879
933
|
* version of a document quietly rewrites a field in every other language.
|
|
880
934
|
*/
|
|
881
935
|
async preview(s) {
|
|
882
|
-
const
|
|
883
|
-
for (const [
|
|
884
|
-
if (
|
|
885
|
-
for (const
|
|
886
|
-
await
|
|
887
|
-
const O =
|
|
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() : [];
|
|
888
942
|
return {
|
|
889
|
-
fieldsToRestore:
|
|
890
|
-
fieldsKeptAsIs:
|
|
943
|
+
fieldsToRestore: m.filter((w) => w in u),
|
|
944
|
+
fieldsKeptAsIs: p,
|
|
891
945
|
fieldsDropped: T,
|
|
892
|
-
crossLocaleFields:
|
|
893
|
-
unsupportedFields:
|
|
946
|
+
crossLocaleFields: v,
|
|
947
|
+
unsupportedFields: b,
|
|
894
948
|
brokenRelations: A,
|
|
895
949
|
missingMedia: I,
|
|
896
|
-
affectedLocales: O.map((
|
|
950
|
+
affectedLocales: O.map((w) => w.code)
|
|
897
951
|
};
|
|
898
952
|
},
|
|
899
953
|
/**
|
|
@@ -902,69 +956,71 @@ const Q = ["id", "documentId", "locale"], E = [
|
|
|
902
956
|
* The document lands in "modified" and publishing stays a deliberate act -
|
|
903
957
|
* restoring must never change what the public sees on its own.
|
|
904
958
|
*/
|
|
905
|
-
async apply(s,
|
|
906
|
-
const
|
|
959
|
+
async apply(s, a) {
|
|
960
|
+
const l = await n(s), d = l.contentType, c = await this.preview(s), u = [];
|
|
907
961
|
await e.plugin("rewind").service("snapshot").capture(
|
|
908
962
|
{
|
|
909
|
-
uid:
|
|
910
|
-
relatedDocumentId:
|
|
911
|
-
locale:
|
|
963
|
+
uid: d,
|
|
964
|
+
relatedDocumentId: l.relatedDocumentId,
|
|
965
|
+
locale: l.locale,
|
|
912
966
|
origin: "restore",
|
|
913
967
|
before: null
|
|
914
968
|
},
|
|
915
|
-
|
|
969
|
+
a
|
|
916
970
|
);
|
|
917
|
-
const
|
|
918
|
-
for (const
|
|
919
|
-
|
|
971
|
+
const f = {}, g = l.data ?? {}, y = l.relations ?? {};
|
|
972
|
+
for (const p of c.fieldsToRestore)
|
|
973
|
+
f[p] = g[p];
|
|
920
974
|
const T = new Set(
|
|
921
|
-
[...
|
|
922
|
-
(
|
|
975
|
+
[...c.brokenRelations, ...c.missingMedia].map(
|
|
976
|
+
(p) => `${p.targetUid}:${p.documentId ?? p.id}`
|
|
923
977
|
)
|
|
924
978
|
);
|
|
925
|
-
for (const [
|
|
926
|
-
if (
|
|
927
|
-
const
|
|
928
|
-
(
|
|
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}`)
|
|
929
983
|
);
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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
|
|
934
990
|
);
|
|
935
991
|
}
|
|
936
|
-
return
|
|
937
|
-
`Skipped ${
|
|
938
|
-
),
|
|
939
|
-
`${
|
|
940
|
-
), await
|
|
941
|
-
await e.documents(
|
|
942
|
-
documentId:
|
|
943
|
-
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,
|
|
944
1000
|
// Explicit, though update() forces draft anyway: passing 'published'
|
|
945
1001
|
// here would publish, and that must never happen by accident.
|
|
946
1002
|
status: "draft",
|
|
947
|
-
data:
|
|
1003
|
+
data: f
|
|
948
1004
|
});
|
|
949
|
-
}), { documentId:
|
|
1005
|
+
}), { documentId: l.relatedDocumentId, warnings: u };
|
|
950
1006
|
}
|
|
951
1007
|
};
|
|
952
|
-
},
|
|
953
|
-
snapshot:
|
|
954
|
-
restore:
|
|
955
|
-
diff:
|
|
956
|
-
retention:
|
|
957
|
-
lock:
|
|
958
|
-
pin:
|
|
959
|
-
},
|
|
960
|
-
bootstrap:
|
|
961
|
-
destroy:
|
|
962
|
-
config:
|
|
963
|
-
contentTypes:
|
|
964
|
-
controllers:
|
|
965
|
-
routes:
|
|
966
|
-
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
|
|
967
1023
|
};
|
|
968
1024
|
export {
|
|
969
|
-
|
|
1025
|
+
it as default
|
|
970
1026
|
};
|