straplight 1.1.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/LICENSE +21 -0
- package/README.md +83 -0
- package/assets/icon.png +0 -0
- package/assets/screenshot.png +0 -0
- package/dist/_chunks/Settings-BLDXIWWB.js +166 -0
- package/dist/_chunks/Settings-CncfOILf.mjs +166 -0
- package/dist/_chunks/StraplightOverlay-DLEpUqQM.mjs +506 -0
- package/dist/_chunks/StraplightOverlay-dGd1yRZj.js +506 -0
- package/dist/_chunks/en-B4KWt_jN.js +4 -0
- package/dist/_chunks/en-Byx4XI2L.mjs +4 -0
- package/dist/_chunks/index-B2B6CFFk.mjs +82 -0
- package/dist/_chunks/index-CuzgWWKQ.js +103 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/StraplightOverlay.d.ts +1 -0
- package/dist/admin/src/components/StraplightPortal.d.ts +6 -0
- package/dist/admin/src/hooks/useStraplight.d.ts +23 -0
- package/dist/admin/src/hooks/useStraplightSettings.d.ts +6 -0
- package/dist/admin/src/index.d.ts +11 -0
- package/dist/admin/src/pages/Settings.d.ts +2 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/utils/getTranslation.d.ts +2 -0
- package/dist/server/index.js +263 -0
- package/dist/server/index.mjs +264 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +5 -0
- package/dist/server/src/content-types/index.d.ts +2 -0
- package/dist/server/src/controllers/controller.d.ts +7 -0
- package/dist/server/src/controllers/index.d.ts +14 -0
- package/dist/server/src/controllers/settings.d.ts +8 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +68 -0
- package/dist/server/src/middlewares/index.d.ts +2 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/admin/index.d.ts +12 -0
- package/dist/server/src/routes/content-api/index.d.ts +5 -0
- package/dist/server/src/routes/index.d.ts +18 -0
- package/dist/server/src/services/index.d.ts +15 -0
- package/dist/server/src/services/service.d.ts +7 -0
- package/dist/server/src/services/settings.d.ts +28 -0
- package/dist/server/src/utils/content-type-helpers.d.ts +4 -0
- package/package.json +85 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function StraplightOverlay(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export interface SearchResult {
|
|
3
|
+
id: number;
|
|
4
|
+
documentId: string;
|
|
5
|
+
label: string;
|
|
6
|
+
contentType: string;
|
|
7
|
+
uid: string;
|
|
8
|
+
fields: {
|
|
9
|
+
name: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}[];
|
|
12
|
+
}
|
|
13
|
+
export declare function useStraplight(): {
|
|
14
|
+
isOpen: boolean;
|
|
15
|
+
query: string;
|
|
16
|
+
setQuery: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
17
|
+
results: SearchResult[];
|
|
18
|
+
loading: boolean;
|
|
19
|
+
selectedIndex: number;
|
|
20
|
+
close: () => void;
|
|
21
|
+
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
22
|
+
navigateToResult: (result: SearchResult) => void;
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PLUGIN_ID = "straplight";
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const bootstrap = ({ strapi }) => {
|
|
3
|
+
};
|
|
4
|
+
const destroy = ({ strapi }) => {
|
|
5
|
+
};
|
|
6
|
+
const register = ({ strapi }) => {
|
|
7
|
+
};
|
|
8
|
+
const config = {
|
|
9
|
+
default: {},
|
|
10
|
+
validator() {
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const contentTypes = {};
|
|
14
|
+
const controller = ({ strapi }) => ({
|
|
15
|
+
async search(ctx) {
|
|
16
|
+
const q = typeof ctx.query.q === "string" ? ctx.query.q.trim() : "";
|
|
17
|
+
if (!q) {
|
|
18
|
+
ctx.body = { results: [] };
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const results = await strapi.plugin("straplight").service("service").search(q);
|
|
22
|
+
ctx.body = { results };
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const settings$1 = ({ strapi }) => ({
|
|
26
|
+
async getSettings(ctx) {
|
|
27
|
+
const settingsService = strapi.plugin("straplight").service("settings");
|
|
28
|
+
const currentSettings = await settingsService.getSettings();
|
|
29
|
+
const contentTypes2 = settingsService.getContentTypes();
|
|
30
|
+
ctx.body = { settings: currentSettings, contentTypes: contentTypes2 };
|
|
31
|
+
},
|
|
32
|
+
async updateSettings(ctx) {
|
|
33
|
+
const settingsService = strapi.plugin("straplight").service("settings");
|
|
34
|
+
const saved = await settingsService.setSettings(ctx.request.body);
|
|
35
|
+
ctx.body = { settings: saved };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const controllers = {
|
|
39
|
+
controller,
|
|
40
|
+
settings: settings$1
|
|
41
|
+
};
|
|
42
|
+
const middlewares = {};
|
|
43
|
+
const policies = {};
|
|
44
|
+
const contentAPIRoutes = () => ({
|
|
45
|
+
type: "content-api",
|
|
46
|
+
routes: []
|
|
47
|
+
});
|
|
48
|
+
const adminAPIRoutes = () => ({
|
|
49
|
+
type: "admin",
|
|
50
|
+
routes: [
|
|
51
|
+
{
|
|
52
|
+
method: "GET",
|
|
53
|
+
path: "/search",
|
|
54
|
+
handler: "controller.search",
|
|
55
|
+
config: {
|
|
56
|
+
policies: []
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
method: "GET",
|
|
61
|
+
path: "/settings",
|
|
62
|
+
handler: "settings.getSettings",
|
|
63
|
+
config: {
|
|
64
|
+
policies: []
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
method: "PUT",
|
|
69
|
+
path: "/settings",
|
|
70
|
+
handler: "settings.updateSettings",
|
|
71
|
+
config: {
|
|
72
|
+
policies: []
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
});
|
|
77
|
+
const routes = {
|
|
78
|
+
"content-api": contentAPIRoutes,
|
|
79
|
+
admin: adminAPIRoutes
|
|
80
|
+
};
|
|
81
|
+
const STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "text", "email", "uid", "richtext"]);
|
|
82
|
+
const MAIN_FIELD_CANDIDATES = ["title", "name", "slug", "email", "subject", "label"];
|
|
83
|
+
function getMainField(schema) {
|
|
84
|
+
const configured = schema.pluginOptions?.["content-manager"]?.mainField;
|
|
85
|
+
if (configured && schema.attributes?.[configured]) {
|
|
86
|
+
return configured;
|
|
87
|
+
}
|
|
88
|
+
for (const candidate of MAIN_FIELD_CANDIDATES) {
|
|
89
|
+
if (schema.attributes?.[candidate]) {
|
|
90
|
+
return candidate;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
94
|
+
if (STRING_FIELD_TYPES.has(attr.type)) {
|
|
95
|
+
return name;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function getSearchableFields(schema) {
|
|
101
|
+
const fields = [];
|
|
102
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
103
|
+
if (STRING_FIELD_TYPES.has(attr.type)) {
|
|
104
|
+
fields.push(name);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return fields;
|
|
108
|
+
}
|
|
109
|
+
function getRelationFields(schema) {
|
|
110
|
+
const fields = [];
|
|
111
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
112
|
+
const a = attr;
|
|
113
|
+
if (a.type === "relation" && a.target?.startsWith("api::")) {
|
|
114
|
+
fields.push(name);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return fields;
|
|
118
|
+
}
|
|
119
|
+
function resolveRelationValue(entry, fieldName, schema, strapi) {
|
|
120
|
+
const related = entry[fieldName];
|
|
121
|
+
if (!related) return null;
|
|
122
|
+
const item = Array.isArray(related) ? related[0] : related;
|
|
123
|
+
if (!item) return null;
|
|
124
|
+
const attr = schema.attributes?.[fieldName];
|
|
125
|
+
if (!attr?.target) return null;
|
|
126
|
+
const targetSchema = strapi.contentTypes[attr.target];
|
|
127
|
+
if (!targetSchema) return null;
|
|
128
|
+
const targetMainField = getMainField(targetSchema);
|
|
129
|
+
if (targetMainField && item[targetMainField]) {
|
|
130
|
+
return String(item[targetMainField]);
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const service = ({ strapi }) => ({
|
|
135
|
+
async search(query) {
|
|
136
|
+
const settings2 = await strapi.plugin("straplight").service("settings").getSettings();
|
|
137
|
+
const allResults = [];
|
|
138
|
+
const contentTypes2 = Object.entries(strapi.contentTypes).filter(([uid]) => uid.startsWith("api::")).filter(([uid]) => settings2.contentTypes[uid]?.enabled !== false);
|
|
139
|
+
const promises = contentTypes2.map(async ([uid, schema]) => {
|
|
140
|
+
try {
|
|
141
|
+
const searchableFields = getSearchableFields(schema);
|
|
142
|
+
if (searchableFields.length === 0) return;
|
|
143
|
+
const mainField = getMainField(schema) || searchableFields[0];
|
|
144
|
+
const filters = {
|
|
145
|
+
$or: searchableFields.map((field) => ({
|
|
146
|
+
[field]: { $containsi: query }
|
|
147
|
+
}))
|
|
148
|
+
};
|
|
149
|
+
const displayFields = settings2.contentTypes[uid]?.displayFields || [];
|
|
150
|
+
const relationFields = getRelationFields(schema);
|
|
151
|
+
const relationsToPopulate = displayFields.filter((f) => relationFields.includes(f));
|
|
152
|
+
const populate = {};
|
|
153
|
+
for (const rel of relationsToPopulate) {
|
|
154
|
+
const attr = schema.attributes?.[rel];
|
|
155
|
+
if (!attr?.target) continue;
|
|
156
|
+
const targetSchema = strapi.contentTypes[attr.target];
|
|
157
|
+
if (!targetSchema) continue;
|
|
158
|
+
const targetMainField = getMainField(targetSchema);
|
|
159
|
+
if (targetMainField) {
|
|
160
|
+
populate[rel] = { fields: [targetMainField] };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const entries = await strapi.documents(uid).findMany({
|
|
164
|
+
filters,
|
|
165
|
+
limit: 5,
|
|
166
|
+
...Object.keys(populate).length > 0 ? { populate } : {}
|
|
167
|
+
});
|
|
168
|
+
const displayName = schema.info?.displayName || schema.info?.singularName || uid;
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
const fields = displayFields.slice(0, 2).filter((f) => f !== mainField).map((f) => {
|
|
171
|
+
if (relationFields.includes(f)) {
|
|
172
|
+
const value = resolveRelationValue(entry, f, schema, strapi);
|
|
173
|
+
return value ? { name: f, value } : null;
|
|
174
|
+
}
|
|
175
|
+
const val = entry[f];
|
|
176
|
+
return val != null ? { name: f, value: String(val) } : null;
|
|
177
|
+
}).filter(Boolean);
|
|
178
|
+
allResults.push({
|
|
179
|
+
id: entry.id,
|
|
180
|
+
documentId: entry.documentId,
|
|
181
|
+
label: entry[mainField] || `#${entry.documentId}`,
|
|
182
|
+
contentType: displayName,
|
|
183
|
+
uid,
|
|
184
|
+
fields
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
await Promise.all(promises);
|
|
191
|
+
return allResults;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const DEFAULTS = {
|
|
195
|
+
debounceMs: 200,
|
|
196
|
+
minQueryLength: 1,
|
|
197
|
+
contentTypes: {}
|
|
198
|
+
};
|
|
199
|
+
const settings = ({ strapi }) => ({
|
|
200
|
+
async getSettings() {
|
|
201
|
+
const store = strapi.store({ type: "plugin", name: "straplight" });
|
|
202
|
+
const data = await store.get({ key: "settings" });
|
|
203
|
+
return {
|
|
204
|
+
...DEFAULTS,
|
|
205
|
+
...data || {}
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
async setSettings(newSettings) {
|
|
209
|
+
const validated = {
|
|
210
|
+
debounceMs: Math.max(0, Math.round(Number(newSettings.debounceMs) || DEFAULTS.debounceMs)),
|
|
211
|
+
minQueryLength: Math.max(1, Math.round(Number(newSettings.minQueryLength) || DEFAULTS.minQueryLength)),
|
|
212
|
+
contentTypes: {}
|
|
213
|
+
};
|
|
214
|
+
if (newSettings.contentTypes && typeof newSettings.contentTypes === "object") {
|
|
215
|
+
for (const [uid, ct] of Object.entries(newSettings.contentTypes)) {
|
|
216
|
+
if (!uid.startsWith("api::")) continue;
|
|
217
|
+
validated.contentTypes[uid] = {
|
|
218
|
+
enabled: ct.enabled !== false,
|
|
219
|
+
displayFields: Array.isArray(ct.displayFields) ? ct.displayFields.slice(0, 2) : []
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const store = strapi.store({ type: "plugin", name: "straplight" });
|
|
224
|
+
await store.set({ key: "settings", value: validated });
|
|
225
|
+
return validated;
|
|
226
|
+
},
|
|
227
|
+
getContentTypes() {
|
|
228
|
+
return Object.entries(strapi.contentTypes).filter(([uid]) => uid.startsWith("api::")).map(([uid, schema]) => ({
|
|
229
|
+
uid,
|
|
230
|
+
displayName: schema.info?.displayName || schema.info?.singularName || uid,
|
|
231
|
+
fields: Object.entries(schema.attributes || {}).filter(([, attr]) => {
|
|
232
|
+
const a = attr;
|
|
233
|
+
return STRING_FIELD_TYPES.has(a.type) || a.type === "relation" && a.target?.startsWith("api::");
|
|
234
|
+
}).map(([name, attr]) => {
|
|
235
|
+
const a = attr;
|
|
236
|
+
if (a.type === "relation") {
|
|
237
|
+
const targetSchema = strapi.contentTypes[a.target];
|
|
238
|
+
const targetName = targetSchema?.info?.displayName || a.target;
|
|
239
|
+
return { name, type: "relation", target: targetName };
|
|
240
|
+
}
|
|
241
|
+
return { name, type: a.type };
|
|
242
|
+
}),
|
|
243
|
+
mainField: getMainField(schema) || ""
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
const services = {
|
|
248
|
+
service,
|
|
249
|
+
settings
|
|
250
|
+
};
|
|
251
|
+
const index = {
|
|
252
|
+
register,
|
|
253
|
+
bootstrap,
|
|
254
|
+
destroy,
|
|
255
|
+
config,
|
|
256
|
+
controllers,
|
|
257
|
+
routes,
|
|
258
|
+
services,
|
|
259
|
+
contentTypes,
|
|
260
|
+
policies,
|
|
261
|
+
middlewares
|
|
262
|
+
};
|
|
263
|
+
module.exports = index;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
const bootstrap = ({ strapi }) => {
|
|
2
|
+
};
|
|
3
|
+
const destroy = ({ strapi }) => {
|
|
4
|
+
};
|
|
5
|
+
const register = ({ strapi }) => {
|
|
6
|
+
};
|
|
7
|
+
const config = {
|
|
8
|
+
default: {},
|
|
9
|
+
validator() {
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const contentTypes = {};
|
|
13
|
+
const controller = ({ strapi }) => ({
|
|
14
|
+
async search(ctx) {
|
|
15
|
+
const q = typeof ctx.query.q === "string" ? ctx.query.q.trim() : "";
|
|
16
|
+
if (!q) {
|
|
17
|
+
ctx.body = { results: [] };
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const results = await strapi.plugin("straplight").service("service").search(q);
|
|
21
|
+
ctx.body = { results };
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const settings$1 = ({ strapi }) => ({
|
|
25
|
+
async getSettings(ctx) {
|
|
26
|
+
const settingsService = strapi.plugin("straplight").service("settings");
|
|
27
|
+
const currentSettings = await settingsService.getSettings();
|
|
28
|
+
const contentTypes2 = settingsService.getContentTypes();
|
|
29
|
+
ctx.body = { settings: currentSettings, contentTypes: contentTypes2 };
|
|
30
|
+
},
|
|
31
|
+
async updateSettings(ctx) {
|
|
32
|
+
const settingsService = strapi.plugin("straplight").service("settings");
|
|
33
|
+
const saved = await settingsService.setSettings(ctx.request.body);
|
|
34
|
+
ctx.body = { settings: saved };
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const controllers = {
|
|
38
|
+
controller,
|
|
39
|
+
settings: settings$1
|
|
40
|
+
};
|
|
41
|
+
const middlewares = {};
|
|
42
|
+
const policies = {};
|
|
43
|
+
const contentAPIRoutes = () => ({
|
|
44
|
+
type: "content-api",
|
|
45
|
+
routes: []
|
|
46
|
+
});
|
|
47
|
+
const adminAPIRoutes = () => ({
|
|
48
|
+
type: "admin",
|
|
49
|
+
routes: [
|
|
50
|
+
{
|
|
51
|
+
method: "GET",
|
|
52
|
+
path: "/search",
|
|
53
|
+
handler: "controller.search",
|
|
54
|
+
config: {
|
|
55
|
+
policies: []
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
method: "GET",
|
|
60
|
+
path: "/settings",
|
|
61
|
+
handler: "settings.getSettings",
|
|
62
|
+
config: {
|
|
63
|
+
policies: []
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
method: "PUT",
|
|
68
|
+
path: "/settings",
|
|
69
|
+
handler: "settings.updateSettings",
|
|
70
|
+
config: {
|
|
71
|
+
policies: []
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
const routes = {
|
|
77
|
+
"content-api": contentAPIRoutes,
|
|
78
|
+
admin: adminAPIRoutes
|
|
79
|
+
};
|
|
80
|
+
const STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "text", "email", "uid", "richtext"]);
|
|
81
|
+
const MAIN_FIELD_CANDIDATES = ["title", "name", "slug", "email", "subject", "label"];
|
|
82
|
+
function getMainField(schema) {
|
|
83
|
+
const configured = schema.pluginOptions?.["content-manager"]?.mainField;
|
|
84
|
+
if (configured && schema.attributes?.[configured]) {
|
|
85
|
+
return configured;
|
|
86
|
+
}
|
|
87
|
+
for (const candidate of MAIN_FIELD_CANDIDATES) {
|
|
88
|
+
if (schema.attributes?.[candidate]) {
|
|
89
|
+
return candidate;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
93
|
+
if (STRING_FIELD_TYPES.has(attr.type)) {
|
|
94
|
+
return name;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function getSearchableFields(schema) {
|
|
100
|
+
const fields = [];
|
|
101
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
102
|
+
if (STRING_FIELD_TYPES.has(attr.type)) {
|
|
103
|
+
fields.push(name);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return fields;
|
|
107
|
+
}
|
|
108
|
+
function getRelationFields(schema) {
|
|
109
|
+
const fields = [];
|
|
110
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
111
|
+
const a = attr;
|
|
112
|
+
if (a.type === "relation" && a.target?.startsWith("api::")) {
|
|
113
|
+
fields.push(name);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return fields;
|
|
117
|
+
}
|
|
118
|
+
function resolveRelationValue(entry, fieldName, schema, strapi) {
|
|
119
|
+
const related = entry[fieldName];
|
|
120
|
+
if (!related) return null;
|
|
121
|
+
const item = Array.isArray(related) ? related[0] : related;
|
|
122
|
+
if (!item) return null;
|
|
123
|
+
const attr = schema.attributes?.[fieldName];
|
|
124
|
+
if (!attr?.target) return null;
|
|
125
|
+
const targetSchema = strapi.contentTypes[attr.target];
|
|
126
|
+
if (!targetSchema) return null;
|
|
127
|
+
const targetMainField = getMainField(targetSchema);
|
|
128
|
+
if (targetMainField && item[targetMainField]) {
|
|
129
|
+
return String(item[targetMainField]);
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const service = ({ strapi }) => ({
|
|
134
|
+
async search(query) {
|
|
135
|
+
const settings2 = await strapi.plugin("straplight").service("settings").getSettings();
|
|
136
|
+
const allResults = [];
|
|
137
|
+
const contentTypes2 = Object.entries(strapi.contentTypes).filter(([uid]) => uid.startsWith("api::")).filter(([uid]) => settings2.contentTypes[uid]?.enabled !== false);
|
|
138
|
+
const promises = contentTypes2.map(async ([uid, schema]) => {
|
|
139
|
+
try {
|
|
140
|
+
const searchableFields = getSearchableFields(schema);
|
|
141
|
+
if (searchableFields.length === 0) return;
|
|
142
|
+
const mainField = getMainField(schema) || searchableFields[0];
|
|
143
|
+
const filters = {
|
|
144
|
+
$or: searchableFields.map((field) => ({
|
|
145
|
+
[field]: { $containsi: query }
|
|
146
|
+
}))
|
|
147
|
+
};
|
|
148
|
+
const displayFields = settings2.contentTypes[uid]?.displayFields || [];
|
|
149
|
+
const relationFields = getRelationFields(schema);
|
|
150
|
+
const relationsToPopulate = displayFields.filter((f) => relationFields.includes(f));
|
|
151
|
+
const populate = {};
|
|
152
|
+
for (const rel of relationsToPopulate) {
|
|
153
|
+
const attr = schema.attributes?.[rel];
|
|
154
|
+
if (!attr?.target) continue;
|
|
155
|
+
const targetSchema = strapi.contentTypes[attr.target];
|
|
156
|
+
if (!targetSchema) continue;
|
|
157
|
+
const targetMainField = getMainField(targetSchema);
|
|
158
|
+
if (targetMainField) {
|
|
159
|
+
populate[rel] = { fields: [targetMainField] };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const entries = await strapi.documents(uid).findMany({
|
|
163
|
+
filters,
|
|
164
|
+
limit: 5,
|
|
165
|
+
...Object.keys(populate).length > 0 ? { populate } : {}
|
|
166
|
+
});
|
|
167
|
+
const displayName = schema.info?.displayName || schema.info?.singularName || uid;
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const fields = displayFields.slice(0, 2).filter((f) => f !== mainField).map((f) => {
|
|
170
|
+
if (relationFields.includes(f)) {
|
|
171
|
+
const value = resolveRelationValue(entry, f, schema, strapi);
|
|
172
|
+
return value ? { name: f, value } : null;
|
|
173
|
+
}
|
|
174
|
+
const val = entry[f];
|
|
175
|
+
return val != null ? { name: f, value: String(val) } : null;
|
|
176
|
+
}).filter(Boolean);
|
|
177
|
+
allResults.push({
|
|
178
|
+
id: entry.id,
|
|
179
|
+
documentId: entry.documentId,
|
|
180
|
+
label: entry[mainField] || `#${entry.documentId}`,
|
|
181
|
+
contentType: displayName,
|
|
182
|
+
uid,
|
|
183
|
+
fields
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
await Promise.all(promises);
|
|
190
|
+
return allResults;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
const DEFAULTS = {
|
|
194
|
+
debounceMs: 200,
|
|
195
|
+
minQueryLength: 1,
|
|
196
|
+
contentTypes: {}
|
|
197
|
+
};
|
|
198
|
+
const settings = ({ strapi }) => ({
|
|
199
|
+
async getSettings() {
|
|
200
|
+
const store = strapi.store({ type: "plugin", name: "straplight" });
|
|
201
|
+
const data = await store.get({ key: "settings" });
|
|
202
|
+
return {
|
|
203
|
+
...DEFAULTS,
|
|
204
|
+
...data || {}
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
async setSettings(newSettings) {
|
|
208
|
+
const validated = {
|
|
209
|
+
debounceMs: Math.max(0, Math.round(Number(newSettings.debounceMs) || DEFAULTS.debounceMs)),
|
|
210
|
+
minQueryLength: Math.max(1, Math.round(Number(newSettings.minQueryLength) || DEFAULTS.minQueryLength)),
|
|
211
|
+
contentTypes: {}
|
|
212
|
+
};
|
|
213
|
+
if (newSettings.contentTypes && typeof newSettings.contentTypes === "object") {
|
|
214
|
+
for (const [uid, ct] of Object.entries(newSettings.contentTypes)) {
|
|
215
|
+
if (!uid.startsWith("api::")) continue;
|
|
216
|
+
validated.contentTypes[uid] = {
|
|
217
|
+
enabled: ct.enabled !== false,
|
|
218
|
+
displayFields: Array.isArray(ct.displayFields) ? ct.displayFields.slice(0, 2) : []
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const store = strapi.store({ type: "plugin", name: "straplight" });
|
|
223
|
+
await store.set({ key: "settings", value: validated });
|
|
224
|
+
return validated;
|
|
225
|
+
},
|
|
226
|
+
getContentTypes() {
|
|
227
|
+
return Object.entries(strapi.contentTypes).filter(([uid]) => uid.startsWith("api::")).map(([uid, schema]) => ({
|
|
228
|
+
uid,
|
|
229
|
+
displayName: schema.info?.displayName || schema.info?.singularName || uid,
|
|
230
|
+
fields: Object.entries(schema.attributes || {}).filter(([, attr]) => {
|
|
231
|
+
const a = attr;
|
|
232
|
+
return STRING_FIELD_TYPES.has(a.type) || a.type === "relation" && a.target?.startsWith("api::");
|
|
233
|
+
}).map(([name, attr]) => {
|
|
234
|
+
const a = attr;
|
|
235
|
+
if (a.type === "relation") {
|
|
236
|
+
const targetSchema = strapi.contentTypes[a.target];
|
|
237
|
+
const targetName = targetSchema?.info?.displayName || a.target;
|
|
238
|
+
return { name, type: "relation", target: targetName };
|
|
239
|
+
}
|
|
240
|
+
return { name, type: a.type };
|
|
241
|
+
}),
|
|
242
|
+
mainField: getMainField(schema) || ""
|
|
243
|
+
}));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
const services = {
|
|
247
|
+
service,
|
|
248
|
+
settings
|
|
249
|
+
};
|
|
250
|
+
const index = {
|
|
251
|
+
register,
|
|
252
|
+
bootstrap,
|
|
253
|
+
destroy,
|
|
254
|
+
config,
|
|
255
|
+
controllers,
|
|
256
|
+
routes,
|
|
257
|
+
services,
|
|
258
|
+
contentTypes,
|
|
259
|
+
policies,
|
|
260
|
+
middlewares
|
|
261
|
+
};
|
|
262
|
+
export {
|
|
263
|
+
index as default
|
|
264
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
controller: ({ strapi }: {
|
|
3
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
4
|
+
}) => {
|
|
5
|
+
search(ctx: any): Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
settings: ({ strapi }: {
|
|
8
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
9
|
+
}) => {
|
|
10
|
+
getSettings(ctx: any): Promise<void>;
|
|
11
|
+
updateSettings(ctx: any): Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|