@prenta/core 7.7.0 → 7.7.2
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/CHANGELOG.md +12 -0
- package/dist/api/route-helpers/auth.d.ts +131 -0
- package/dist/api/route-helpers/auth.d.ts.map +1 -0
- package/dist/api/route-helpers/auth.js +505 -0
- package/dist/api/route-helpers/auth.js.map +1 -0
- package/dist/api/route-helpers/authoring.d.ts +32 -0
- package/dist/api/route-helpers/authoring.d.ts.map +1 -0
- package/dist/api/route-helpers/authoring.js +99 -0
- package/dist/api/route-helpers/authoring.js.map +1 -0
- package/dist/api/route-helpers/forms.d.ts +46 -0
- package/dist/api/route-helpers/forms.d.ts.map +1 -0
- package/dist/api/route-helpers/forms.js +435 -0
- package/dist/api/route-helpers/forms.js.map +1 -0
- package/dist/api/route-helpers/http.d.ts +87 -0
- package/dist/api/route-helpers/http.d.ts.map +1 -0
- package/dist/api/route-helpers/http.js +286 -0
- package/dist/api/route-helpers/http.js.map +1 -0
- package/dist/api/route-helpers/media.d.ts +37 -0
- package/dist/api/route-helpers/media.d.ts.map +1 -0
- package/dist/api/route-helpers/media.js +189 -0
- package/dist/api/route-helpers/media.js.map +1 -0
- package/dist/api/route-helpers/page-shape.d.ts +31 -0
- package/dist/api/route-helpers/page-shape.d.ts.map +1 -0
- package/dist/api/route-helpers/page-shape.js +98 -0
- package/dist/api/route-helpers/page-shape.js.map +1 -0
- package/dist/api/route-helpers.d.ts +8 -358
- package/dist/api/route-helpers.d.ts.map +1 -1
- package/dist/api/route-helpers.js +7 -1592
- package/dist/api/route-helpers.js.map +1 -1
- package/dist/api/routes/seo/handlers/analysis.d.ts +4 -0
- package/dist/api/routes/seo/handlers/analysis.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/analysis.js +131 -0
- package/dist/api/routes/seo/handlers/analysis.js.map +1 -0
- package/dist/api/routes/seo/handlers/audits.d.ts +5 -0
- package/dist/api/routes/seo/handlers/audits.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/audits.js +121 -0
- package/dist/api/routes/seo/handlers/audits.js.map +1 -0
- package/dist/api/routes/seo/handlers/content.d.ts +4 -0
- package/dist/api/routes/seo/handlers/content.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/content.js +330 -0
- package/dist/api/routes/seo/handlers/content.js.map +1 -0
- package/dist/api/routes/seo/handlers/issue-helpers.d.ts +37 -0
- package/dist/api/routes/seo/handlers/issue-helpers.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/issue-helpers.js +72 -0
- package/dist/api/routes/seo/handlers/issue-helpers.js.map +1 -0
- package/dist/api/routes/seo/handlers/issues-briefs.d.ts +7 -0
- package/dist/api/routes/seo/handlers/issues-briefs.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/issues-briefs.js +341 -0
- package/dist/api/routes/seo/handlers/issues-briefs.js.map +1 -0
- package/dist/api/routes/seo/handlers/issues-fixes.d.ts +4 -0
- package/dist/api/routes/seo/handlers/issues-fixes.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/issues-fixes.js +233 -0
- package/dist/api/routes/seo/handlers/issues-fixes.js.map +1 -0
- package/dist/api/routes/seo/handlers/issues-list.d.ts +7 -0
- package/dist/api/routes/seo/handlers/issues-list.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/issues-list.js +191 -0
- package/dist/api/routes/seo/handlers/issues-list.js.map +1 -0
- package/dist/api/routes/seo/handlers/links.d.ts +6 -0
- package/dist/api/routes/seo/handlers/links.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/links.js +383 -0
- package/dist/api/routes/seo/handlers/links.js.map +1 -0
- package/dist/api/routes/seo/handlers/overview.d.ts +4 -0
- package/dist/api/routes/seo/handlers/overview.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/overview.js +348 -0
- package/dist/api/routes/seo/handlers/overview.js.map +1 -0
- package/dist/api/routes/seo/handlers/pages.d.ts +2 -0
- package/dist/api/routes/seo/handlers/pages.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/pages.js +38 -0
- package/dist/api/routes/seo/handlers/pages.js.map +1 -0
- package/dist/api/routes/seo/handlers/proposals.d.ts +4 -0
- package/dist/api/routes/seo/handlers/proposals.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/proposals.js +93 -0
- package/dist/api/routes/seo/handlers/proposals.js.map +1 -0
- package/dist/api/routes/seo/handlers/public-files.d.ts +6 -0
- package/dist/api/routes/seo/handlers/public-files.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/public-files.js +328 -0
- package/dist/api/routes/seo/handlers/public-files.js.map +1 -0
- package/dist/api/routes/seo/handlers/shared.d.ts +24 -0
- package/dist/api/routes/seo/handlers/shared.d.ts.map +1 -0
- package/dist/api/routes/seo/handlers/shared.js +67 -0
- package/dist/api/routes/seo/handlers/shared.js.map +1 -0
- package/dist/api/routes/seo.d.ts +5 -5
- package/dist/api/routes/seo.d.ts.map +1 -1
- package/dist/api/routes/seo.js +56 -2610
- package/dist/api/routes/seo.js.map +1 -1
- package/dist/api/routes/users/handlers/api-keys.d.ts +4 -0
- package/dist/api/routes/users/handlers/api-keys.d.ts.map +1 -0
- package/dist/api/routes/users/handlers/api-keys.js +164 -0
- package/dist/api/routes/users/handlers/api-keys.js.map +1 -0
- package/dist/api/routes/users/handlers/invites.d.ts +7 -0
- package/dist/api/routes/users/handlers/invites.d.ts.map +1 -0
- package/dist/api/routes/users/handlers/invites.js +201 -0
- package/dist/api/routes/users/handlers/invites.js.map +1 -0
- package/dist/api/routes/users/handlers/users-admin.d.ts +7 -0
- package/dist/api/routes/users/handlers/users-admin.d.ts.map +1 -0
- package/dist/api/routes/users/handlers/users-admin.js +310 -0
- package/dist/api/routes/users/handlers/users-admin.js.map +1 -0
- package/dist/api/routes/users/handlers/users-detail.d.ts +16 -0
- package/dist/api/routes/users/handlers/users-detail.d.ts.map +1 -0
- package/dist/api/routes/users/handlers/users-detail.js +525 -0
- package/dist/api/routes/users/handlers/users-detail.js.map +1 -0
- package/dist/api/routes/users.d.ts +4 -0
- package/dist/api/routes/users.d.ts.map +1 -1
- package/dist/api/routes/users.js +38 -1177
- package/dist/api/routes/users.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { getPrentaConfig } from '../../../../config/runtime.js';
|
|
2
|
+
import { assertPlanFeature } from '../../../../plan/features.js';
|
|
3
|
+
import { json, errorResponse, internalError, asRecord, hasModel, modelNotAvailable, requireAuth, ADMIN_ROLES, requireRole, aiGenerateLimiter, checkRateLimitAsync, } from '../../../route-helpers.js';
|
|
4
|
+
import { db, planRequiredResponse, seoAiGenerationError } from './shared.js';
|
|
5
|
+
import { loadIssueFixContext } from './issue-helpers.js';
|
|
6
|
+
export async function postSeoIssuesBatchContentBriefs(request) {
|
|
7
|
+
try {
|
|
8
|
+
const auth = await requireAuth(request);
|
|
9
|
+
if (auth.error)
|
|
10
|
+
return auth.error;
|
|
11
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
12
|
+
if (roleErr)
|
|
13
|
+
return roleErr;
|
|
14
|
+
const batchPlanErr = assertPlanFeature('seo.batchContentBriefs');
|
|
15
|
+
if (batchPlanErr)
|
|
16
|
+
return planRequiredResponse(batchPlanErr);
|
|
17
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
18
|
+
return modelNotAvailable('SeoIssue');
|
|
19
|
+
if (!(await checkRateLimitAsync(aiGenerateLimiter, `ai-seo-batch-briefs:${auth.session.userId}`))) {
|
|
20
|
+
return errorResponse('AI rate limit reached. Try again in an hour.', 429);
|
|
21
|
+
}
|
|
22
|
+
const body = (await request.json().catch(() => ({})));
|
|
23
|
+
const severity = typeof body?.severity === 'string' ? body.severity : null;
|
|
24
|
+
const status = typeof body?.status === 'string' ? body.status : null;
|
|
25
|
+
const skipExisting = body?.skipExisting !== false;
|
|
26
|
+
const includeResearch = body?.includeResearch === true;
|
|
27
|
+
const competitorUrls = Array.isArray(body?.competitorUrls)
|
|
28
|
+
? body.competitorUrls.filter((u) => typeof u === 'string')
|
|
29
|
+
: undefined;
|
|
30
|
+
const issueIds = Array.isArray(body?.issueIds)
|
|
31
|
+
? body.issueIds.filter((id) => typeof id === 'string' && id.length > 0)
|
|
32
|
+
: null;
|
|
33
|
+
const where = {};
|
|
34
|
+
if (severity)
|
|
35
|
+
where.severity = severity;
|
|
36
|
+
if (status)
|
|
37
|
+
where.status = status;
|
|
38
|
+
else
|
|
39
|
+
where.status = { in: ['open', 'recurring'] };
|
|
40
|
+
let issues = await db().seoIssue.findMany({
|
|
41
|
+
where,
|
|
42
|
+
orderBy: { lastDetectedAt: 'desc' },
|
|
43
|
+
take: 500,
|
|
44
|
+
});
|
|
45
|
+
if (issueIds?.length) {
|
|
46
|
+
const idSet = new Set(issueIds);
|
|
47
|
+
issues = issues.filter((i) => idSet.has(i.id));
|
|
48
|
+
}
|
|
49
|
+
const { supportsContentBrief } = await import('../../../../seo/issue-content-brief.js');
|
|
50
|
+
const eligible = issues.filter((i) => supportsContentBrief(i.type));
|
|
51
|
+
const entityIds = [
|
|
52
|
+
...new Set(eligible
|
|
53
|
+
.map((i) => i.entityId)
|
|
54
|
+
.filter((id) => typeof id === 'string' && id.length > 0)),
|
|
55
|
+
];
|
|
56
|
+
const { documentToAuditEntity, frontEndContentCollectionTypes } = await import('../../../../seo/audit-runner.js');
|
|
57
|
+
const { applySeoOverrides, getSeoOverrides } = await import('../../../../seo/config-store.js');
|
|
58
|
+
const collectionTypes = frontEndContentCollectionTypes(getPrentaConfig());
|
|
59
|
+
const overrides = await getSeoOverrides(db()).catch(() => null);
|
|
60
|
+
const mergedConfig = applySeoOverrides(getPrentaConfig(), overrides);
|
|
61
|
+
const contexts = new Map();
|
|
62
|
+
if (entityIds.length > 0) {
|
|
63
|
+
const docs = await db().document.findMany({
|
|
64
|
+
where: { id: { in: entityIds }, deletedAt: null },
|
|
65
|
+
select: {
|
|
66
|
+
id: true,
|
|
67
|
+
collection: true,
|
|
68
|
+
title: true,
|
|
69
|
+
slug: true,
|
|
70
|
+
data: true,
|
|
71
|
+
structuredData: true,
|
|
72
|
+
plainText: true,
|
|
73
|
+
publishedAt: true,
|
|
74
|
+
updatedAt: true,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
for (const doc of docs) {
|
|
78
|
+
contexts.set(doc.id, {
|
|
79
|
+
collection: doc.collection,
|
|
80
|
+
entity: documentToAuditEntity(doc, collectionTypes, mergedConfig?.seo),
|
|
81
|
+
updatedAt: doc.updatedAt instanceof Date ? doc.updatedAt : new Date(doc.updatedAt),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const { batchGenerateContentBriefs } = await import('../../../../seo/issue-batch-briefs.js');
|
|
86
|
+
const result = await batchGenerateContentBriefs(eligible.map((i) => ({
|
|
87
|
+
id: i.id,
|
|
88
|
+
type: i.type,
|
|
89
|
+
title: i.title,
|
|
90
|
+
description: i.description,
|
|
91
|
+
recommendation: i.recommendation,
|
|
92
|
+
entityId: i.entityId,
|
|
93
|
+
entityType: i.entityType,
|
|
94
|
+
metadata: i.metadata,
|
|
95
|
+
})), contexts, { db: db(), userId: auth.session.userId, collection: 'pages' }, { skipExisting, includeResearch, competitorUrls });
|
|
96
|
+
return json({ data: result });
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
return internalError(err, 'seo/issues/batch-content-briefs');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export async function getSeoIssueContentBrief(request, params) {
|
|
103
|
+
try {
|
|
104
|
+
const auth = await requireAuth(request);
|
|
105
|
+
if (auth.error)
|
|
106
|
+
return auth.error;
|
|
107
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
108
|
+
return modelNotAvailable('SeoIssue');
|
|
109
|
+
const issue = await db().seoIssue.findUnique({ where: { id: params.id } });
|
|
110
|
+
if (!issue)
|
|
111
|
+
return errorResponse('Issue not found', 404);
|
|
112
|
+
const { supportsContentBrief, contentBriefFromMetadata } = await import('../../../../seo/issue-content-brief.js');
|
|
113
|
+
if (!supportsContentBrief(issue.type)) {
|
|
114
|
+
return json({ data: { supported: false, brief: null, stale: false } });
|
|
115
|
+
}
|
|
116
|
+
if (!issue.entityId) {
|
|
117
|
+
return json({ data: { supported: true, brief: null, stale: false } });
|
|
118
|
+
}
|
|
119
|
+
const loaded = await loadIssueFixContext(issue);
|
|
120
|
+
if ('error' in loaded)
|
|
121
|
+
return loaded.error;
|
|
122
|
+
const brief = contentBriefFromMetadata(issue.metadata, issue.type, issue.entityId, loaded.updatedAt);
|
|
123
|
+
const raw = asRecord(issue.metadata)?.contentBrief;
|
|
124
|
+
const stale = !!raw &&
|
|
125
|
+
typeof raw === 'object' &&
|
|
126
|
+
!Array.isArray(raw) &&
|
|
127
|
+
typeof raw.fingerprint === 'string' &&
|
|
128
|
+
!brief;
|
|
129
|
+
return json({ data: { supported: true, brief, stale } });
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
return internalError(err, 'seo/issues/:id/content-brief');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export async function postSeoIssueContentBrief(request, params) {
|
|
136
|
+
try {
|
|
137
|
+
const auth = await requireAuth(request);
|
|
138
|
+
if (auth.error)
|
|
139
|
+
return auth.error;
|
|
140
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
141
|
+
if (roleErr)
|
|
142
|
+
return roleErr;
|
|
143
|
+
const planErr = assertPlanFeature('seo.contentBrief');
|
|
144
|
+
if (planErr)
|
|
145
|
+
return planRequiredResponse(planErr);
|
|
146
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
147
|
+
return modelNotAvailable('SeoIssue');
|
|
148
|
+
if (!(await checkRateLimitAsync(aiGenerateLimiter, `ai-seo-content-brief:${auth.session.userId}`))) {
|
|
149
|
+
return errorResponse('AI rate limit reached. Try again in an hour.', 429);
|
|
150
|
+
}
|
|
151
|
+
const issue = await db().seoIssue.findUnique({ where: { id: params.id } });
|
|
152
|
+
if (!issue)
|
|
153
|
+
return errorResponse('Issue not found', 404);
|
|
154
|
+
const loaded = await loadIssueFixContext(issue);
|
|
155
|
+
if ('error' in loaded)
|
|
156
|
+
return loaded.error;
|
|
157
|
+
const { generateContentBrief, contentBriefToMetadata, supportsContentBrief } = await import('../../../../seo/issue-content-brief.js');
|
|
158
|
+
if (!supportsContentBrief(issue.type)) {
|
|
159
|
+
return errorResponse('This issue type does not support content briefs.', 422);
|
|
160
|
+
}
|
|
161
|
+
const result = await generateContentBrief({
|
|
162
|
+
id: issue.id,
|
|
163
|
+
type: issue.type,
|
|
164
|
+
title: issue.title,
|
|
165
|
+
description: issue.description,
|
|
166
|
+
recommendation: issue.recommendation,
|
|
167
|
+
entityId: issue.entityId,
|
|
168
|
+
}, { entity: loaded.entity, updatedAt: loaded.updatedAt }, { db: db(), userId: auth.session.userId, collection: loaded.collection });
|
|
169
|
+
if (!result.ok) {
|
|
170
|
+
return seoAiGenerationError(result);
|
|
171
|
+
}
|
|
172
|
+
const existingMeta = asRecord(issue.metadata);
|
|
173
|
+
await db().seoIssue.update({
|
|
174
|
+
where: { id: issue.id },
|
|
175
|
+
data: { metadata: { ...existingMeta, ...contentBriefToMetadata(result.brief) } },
|
|
176
|
+
});
|
|
177
|
+
return json({ data: { brief: result.brief } });
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
return internalError(err, 'seo/issues/:id/content-brief');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export async function getSeoIssueContentResearch(request, params) {
|
|
184
|
+
try {
|
|
185
|
+
const auth = await requireAuth(request);
|
|
186
|
+
if (auth.error)
|
|
187
|
+
return auth.error;
|
|
188
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
189
|
+
return modelNotAvailable('SeoIssue');
|
|
190
|
+
const issue = await db().seoIssue.findUnique({ where: { id: params.id } });
|
|
191
|
+
if (!issue)
|
|
192
|
+
return errorResponse('Issue not found', 404);
|
|
193
|
+
const { supportsContentResearch, contentResearchFromMetadata } = await import('../../../../seo/issue-content-research.js');
|
|
194
|
+
if (!supportsContentResearch(issue.type)) {
|
|
195
|
+
return json({ data: { supported: false, research: null, stale: false } });
|
|
196
|
+
}
|
|
197
|
+
if (!issue.entityId) {
|
|
198
|
+
return json({ data: { supported: true, research: null, stale: false } });
|
|
199
|
+
}
|
|
200
|
+
const loaded = await loadIssueFixContext(issue);
|
|
201
|
+
if ('error' in loaded)
|
|
202
|
+
return loaded.error;
|
|
203
|
+
const research = contentResearchFromMetadata(issue.metadata, issue.type, issue.entityId, loaded.updatedAt);
|
|
204
|
+
const raw = asRecord(issue.metadata)?.contentResearch;
|
|
205
|
+
const stale = !!raw &&
|
|
206
|
+
typeof raw === 'object' &&
|
|
207
|
+
!Array.isArray(raw) &&
|
|
208
|
+
typeof raw.fingerprint === 'string' &&
|
|
209
|
+
!research;
|
|
210
|
+
return json({ data: { supported: true, research, stale } });
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return internalError(err, 'seo/issues/:id/content-research');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export async function postSeoIssueContentResearch(request, params) {
|
|
217
|
+
try {
|
|
218
|
+
const auth = await requireAuth(request);
|
|
219
|
+
if (auth.error)
|
|
220
|
+
return auth.error;
|
|
221
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
222
|
+
if (roleErr)
|
|
223
|
+
return roleErr;
|
|
224
|
+
const researchPlanErr = assertPlanFeature('seo.contentResearch');
|
|
225
|
+
if (researchPlanErr)
|
|
226
|
+
return planRequiredResponse(researchPlanErr);
|
|
227
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
228
|
+
return modelNotAvailable('SeoIssue');
|
|
229
|
+
if (!(await checkRateLimitAsync(aiGenerateLimiter, `ai-seo-content-research:${auth.session.userId}`))) {
|
|
230
|
+
return errorResponse('AI rate limit reached. Try again in an hour.', 429);
|
|
231
|
+
}
|
|
232
|
+
const body = (await request.json().catch(() => ({})));
|
|
233
|
+
const competitorUrls = Array.isArray(body?.competitorUrls)
|
|
234
|
+
? body.competitorUrls.filter((u) => typeof u === 'string')
|
|
235
|
+
: undefined;
|
|
236
|
+
const issue = await db().seoIssue.findUnique({ where: { id: params.id } });
|
|
237
|
+
if (!issue)
|
|
238
|
+
return errorResponse('Issue not found', 404);
|
|
239
|
+
const loaded = await loadIssueFixContext(issue);
|
|
240
|
+
if ('error' in loaded)
|
|
241
|
+
return loaded.error;
|
|
242
|
+
const { generateContentResearch, contentResearchToMetadata, supportsContentResearch } = await import('../../../../seo/issue-content-research.js');
|
|
243
|
+
if (!supportsContentResearch(issue.type)) {
|
|
244
|
+
return errorResponse('This issue type does not support topic research.', 422);
|
|
245
|
+
}
|
|
246
|
+
const result = await generateContentResearch({
|
|
247
|
+
id: issue.id,
|
|
248
|
+
type: issue.type,
|
|
249
|
+
title: issue.title,
|
|
250
|
+
description: issue.description,
|
|
251
|
+
recommendation: issue.recommendation,
|
|
252
|
+
entityId: issue.entityId,
|
|
253
|
+
}, { entity: loaded.entity, updatedAt: loaded.updatedAt }, { db: db(), userId: auth.session.userId, collection: loaded.collection }, { competitorUrls });
|
|
254
|
+
if (!result.ok) {
|
|
255
|
+
return seoAiGenerationError(result);
|
|
256
|
+
}
|
|
257
|
+
const existingMeta = asRecord(issue.metadata);
|
|
258
|
+
await db().seoIssue.update({
|
|
259
|
+
where: { id: issue.id },
|
|
260
|
+
data: { metadata: { ...existingMeta, ...contentResearchToMetadata(result.research) } },
|
|
261
|
+
});
|
|
262
|
+
return json({ data: { research: result.research } });
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
return internalError(err, 'seo/issues/:id/content-research');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
export async function postSeoIssueResearchAndBrief(request, params) {
|
|
269
|
+
try {
|
|
270
|
+
const auth = await requireAuth(request);
|
|
271
|
+
if (auth.error)
|
|
272
|
+
return auth.error;
|
|
273
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
274
|
+
if (roleErr)
|
|
275
|
+
return roleErr;
|
|
276
|
+
const comboPlanErr = assertPlanFeature('seo.contentResearch');
|
|
277
|
+
if (comboPlanErr)
|
|
278
|
+
return planRequiredResponse(comboPlanErr);
|
|
279
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
280
|
+
return modelNotAvailable('SeoIssue');
|
|
281
|
+
if (!(await checkRateLimitAsync(aiGenerateLimiter, `ai-seo-research-brief:${auth.session.userId}`))) {
|
|
282
|
+
return errorResponse('AI rate limit reached. Try again in an hour.', 429);
|
|
283
|
+
}
|
|
284
|
+
const body = (await request.json().catch(() => ({})));
|
|
285
|
+
const competitorUrls = Array.isArray(body?.competitorUrls)
|
|
286
|
+
? body.competitorUrls.filter((u) => typeof u === 'string')
|
|
287
|
+
: undefined;
|
|
288
|
+
const issue = await db().seoIssue.findUnique({ where: { id: params.id } });
|
|
289
|
+
if (!issue)
|
|
290
|
+
return errorResponse('Issue not found', 404);
|
|
291
|
+
const loaded = await loadIssueFixContext(issue);
|
|
292
|
+
if ('error' in loaded)
|
|
293
|
+
return loaded.error;
|
|
294
|
+
const { generateContentResearch, contentResearchToMetadata, supportsContentResearch, contentResearchFromMetadata, } = await import('../../../../seo/issue-content-research.js');
|
|
295
|
+
const { generateContentBrief, contentBriefToMetadata, supportsContentBrief } = await import('../../../../seo/issue-content-brief.js');
|
|
296
|
+
if (!supportsContentBrief(issue.type)) {
|
|
297
|
+
return errorResponse('This issue type does not support content briefs.', 422);
|
|
298
|
+
}
|
|
299
|
+
const issueInput = {
|
|
300
|
+
id: issue.id,
|
|
301
|
+
type: issue.type,
|
|
302
|
+
title: issue.title,
|
|
303
|
+
description: issue.description,
|
|
304
|
+
recommendation: issue.recommendation,
|
|
305
|
+
entityId: issue.entityId,
|
|
306
|
+
};
|
|
307
|
+
const briefCtx = { entity: loaded.entity, updatedAt: loaded.updatedAt };
|
|
308
|
+
const deps = { db: db(), userId: auth.session.userId, collection: loaded.collection };
|
|
309
|
+
let research = supportsContentResearch(issue.type)
|
|
310
|
+
? contentResearchFromMetadata(issue.metadata, issue.type, issue.entityId, loaded.updatedAt)
|
|
311
|
+
: null;
|
|
312
|
+
if (supportsContentResearch(issue.type) && !research) {
|
|
313
|
+
const researchResult = await generateContentResearch(issueInput, briefCtx, deps, {
|
|
314
|
+
competitorUrls,
|
|
315
|
+
});
|
|
316
|
+
if (!researchResult.ok) {
|
|
317
|
+
return seoAiGenerationError(researchResult);
|
|
318
|
+
}
|
|
319
|
+
research = researchResult.research;
|
|
320
|
+
}
|
|
321
|
+
const briefResult = await generateContentBrief(issueInput, briefCtx, deps, { research });
|
|
322
|
+
if (!briefResult.ok) {
|
|
323
|
+
return seoAiGenerationError(briefResult);
|
|
324
|
+
}
|
|
325
|
+
const existingMeta = asRecord(issue.metadata);
|
|
326
|
+
const metadata = {
|
|
327
|
+
...existingMeta,
|
|
328
|
+
...contentBriefToMetadata(briefResult.brief),
|
|
329
|
+
...(research ? contentResearchToMetadata(research) : {}),
|
|
330
|
+
};
|
|
331
|
+
await db().seoIssue.update({
|
|
332
|
+
where: { id: issue.id },
|
|
333
|
+
data: { metadata },
|
|
334
|
+
});
|
|
335
|
+
return json({ data: { research, brief: briefResult.brief } });
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
return internalError(err, 'seo/issues/:id/research-and-brief');
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=issues-briefs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues-briefs.js","sourceRoot":"","sources":["../../../../../src/api/routes/seo/handlers/issues-briefs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function postSeoIssueSuggestFix(request: Request, params: Record<string, string>): Promise<Response>;
|
|
2
|
+
export declare function postSeoIssueApplyFix(request: Request, params: Record<string, string>): Promise<Response>;
|
|
3
|
+
export declare function postSeoIssueDismissFix(request: Request, params: Record<string, string>): Promise<Response>;
|
|
4
|
+
//# sourceMappingURL=issues-fixes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues-fixes.d.ts","sourceRoot":"","sources":["../../../../../src/api/routes/seo/handlers/issues-fixes.ts"],"names":[],"mappings":"AAoBA,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAyFnB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAwFnB;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CA4DnB"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { logEvent } from '../../../../security/audit.js';
|
|
2
|
+
import { hasPlanFeature } from '../../../../plan/features.js';
|
|
3
|
+
import { json, errorResponse, internalError, asRecord, hasModel, modelNotAvailable, requireAuth, WRITE_ROLES, ADMIN_ROLES, requireRole, aiGenerateLimiter, checkRateLimitAsync, } from '../../../route-helpers.js';
|
|
4
|
+
import { seoApplyFixBodySchema, validateBody } from '../../../validate-body.js';
|
|
5
|
+
import { db, seoLogger, planRequiredResponse, contentWriteGate } from './shared.js';
|
|
6
|
+
import { seoIssueToApi, loadIssueFixContext } from './issue-helpers.js';
|
|
7
|
+
export async function postSeoIssueSuggestFix(request, params) {
|
|
8
|
+
try {
|
|
9
|
+
const auth = await requireAuth(request);
|
|
10
|
+
if (auth.error)
|
|
11
|
+
return auth.error;
|
|
12
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
13
|
+
return modelNotAvailable('SeoIssue');
|
|
14
|
+
const issue = await db().seoIssue.findUnique({ where: { id: params.id } });
|
|
15
|
+
if (!issue)
|
|
16
|
+
return errorResponse('Issue not found', 404);
|
|
17
|
+
const loaded = await loadIssueFixContext(issue);
|
|
18
|
+
if ('error' in loaded)
|
|
19
|
+
return loaded.error;
|
|
20
|
+
const issueInput = {
|
|
21
|
+
type: issue.type,
|
|
22
|
+
title: issue.title,
|
|
23
|
+
recommendation: issue.recommendation,
|
|
24
|
+
entityType: issue.entityType,
|
|
25
|
+
entityId: issue.entityId,
|
|
26
|
+
};
|
|
27
|
+
const fixCtx = {
|
|
28
|
+
entity: loaded.entity,
|
|
29
|
+
siteSeo: loaded.siteSeo,
|
|
30
|
+
updatedAt: loaded.updatedAt,
|
|
31
|
+
metadata: asRecord(issue.metadata),
|
|
32
|
+
};
|
|
33
|
+
let aiDeps;
|
|
34
|
+
const { REDIRECT_FIX_TYPES } = await import('../../../../seo/issue-fix-redirect.js');
|
|
35
|
+
const { INSERT_LINK_FIX_TYPES } = await import('../../../../seo/issue-fix-link-insert.js');
|
|
36
|
+
if (REDIRECT_FIX_TYPES.has(issue.type) || INSERT_LINK_FIX_TYPES.has(issue.type)) {
|
|
37
|
+
aiDeps = {
|
|
38
|
+
db: db(),
|
|
39
|
+
userId: auth.session.userId,
|
|
40
|
+
collection: loaded.collection,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const adminErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
45
|
+
if (!adminErr &&
|
|
46
|
+
hasPlanFeature('seo.inlineApply') &&
|
|
47
|
+
(await checkRateLimitAsync(aiGenerateLimiter, `ai-seo-fix-suggest:${auth.session.userId}`))) {
|
|
48
|
+
aiDeps = {
|
|
49
|
+
db: db(),
|
|
50
|
+
userId: auth.session.userId,
|
|
51
|
+
collection: loaded.collection,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const { suggestIssueFix } = await import('../../../../seo/issue-fix.js');
|
|
56
|
+
const { pendingFixToMetadata, scoreFixBrandAlignment } = await import('../../../../seo/issue-fix-ai.js');
|
|
57
|
+
const result = await suggestIssueFix(issueInput, fixCtx, aiDeps);
|
|
58
|
+
const existingMeta = asRecord(issue.metadata);
|
|
59
|
+
let nextMetadata;
|
|
60
|
+
if (result.cacheForApply) {
|
|
61
|
+
// Deterministic brand-voice score stamped at generation (no LLM call)
|
|
62
|
+
// so the proposals inbox can badge it without re-scoring.
|
|
63
|
+
const { getAIConfig } = await import('../../../../ai/config-store.js');
|
|
64
|
+
const brandVoice = await getAIConfig(db())
|
|
65
|
+
.then((c) => c.settings.brandVoice)
|
|
66
|
+
.catch((err) => {
|
|
67
|
+
// Fail open: an unreadable AI config must not cost a working suggestion.
|
|
68
|
+
seoLogger.warn('AI config unavailable for brand alignment', {
|
|
69
|
+
error: err instanceof Error ? err.message : String(err),
|
|
70
|
+
});
|
|
71
|
+
return undefined;
|
|
72
|
+
});
|
|
73
|
+
nextMetadata = {
|
|
74
|
+
...existingMeta,
|
|
75
|
+
...pendingFixToMetadata(result.suggestion, {
|
|
76
|
+
brandAlignment: scoreFixBrandAlignment(result.suggestion.changes, brandVoice),
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
nextMetadata = { ...existingMeta, pendingFix: null };
|
|
82
|
+
}
|
|
83
|
+
await db().seoIssue.update({
|
|
84
|
+
where: { id: issue.id },
|
|
85
|
+
data: { metadata: nextMetadata },
|
|
86
|
+
});
|
|
87
|
+
return json({ data: { suggestion: result.suggestion } });
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
return internalError(err, 'seo/issues/:id/suggest-fix');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function postSeoIssueApplyFix(request, params) {
|
|
94
|
+
try {
|
|
95
|
+
const auth = await requireAuth(request);
|
|
96
|
+
if (auth.error)
|
|
97
|
+
return auth.error;
|
|
98
|
+
const roleErr = requireRole(auth.session.role, WRITE_ROLES);
|
|
99
|
+
if (roleErr)
|
|
100
|
+
return roleErr;
|
|
101
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
102
|
+
return modelNotAvailable('SeoIssue');
|
|
103
|
+
const parsed = await validateBody(request, seoApplyFixBodySchema);
|
|
104
|
+
if (!parsed.ok)
|
|
105
|
+
return parsed.error;
|
|
106
|
+
const body = parsed.data;
|
|
107
|
+
const [{ applyIssueFix, VERIFY_FAILED_MESSAGE }, { FAQ_HEADING_MAX, FAQ_MAX_ITEMS }] = await Promise.all([
|
|
108
|
+
import('../../../../seo/issue-fix-apply.js'),
|
|
109
|
+
import('../../../../seo/issue-fix-faq.js'),
|
|
110
|
+
]);
|
|
111
|
+
const fingerprint = typeof body?.fingerprint === 'string' ? body.fingerprint : undefined;
|
|
112
|
+
// `includeItems` / `headingOverride` only mean something for `add-section`
|
|
113
|
+
// fixes; they are validated for shape here and ignored by other branches.
|
|
114
|
+
let includeItems;
|
|
115
|
+
if (body?.includeItems !== undefined) {
|
|
116
|
+
const raw = body.includeItems;
|
|
117
|
+
if (!Array.isArray(raw) ||
|
|
118
|
+
raw.length > FAQ_MAX_ITEMS ||
|
|
119
|
+
!raw.every((n) => typeof n === 'number' && Number.isInteger(n) && n >= 0) ||
|
|
120
|
+
new Set(raw).size !== raw.length) {
|
|
121
|
+
return errorResponse(`includeItems must be an array of at most ${FAQ_MAX_ITEMS} distinct non-negative integers`, 400);
|
|
122
|
+
}
|
|
123
|
+
includeItems = raw;
|
|
124
|
+
}
|
|
125
|
+
let headingOverride;
|
|
126
|
+
if (body?.headingOverride !== undefined) {
|
|
127
|
+
// Trimmed length is the one that lands in the section, so it is the one measured.
|
|
128
|
+
const trimmed = typeof body.headingOverride === 'string' ? body.headingOverride.trim() : null;
|
|
129
|
+
if (trimmed === null || trimmed.length > FAQ_HEADING_MAX) {
|
|
130
|
+
return errorResponse(`headingOverride must be a string of at most ${FAQ_HEADING_MAX} characters`, 400);
|
|
131
|
+
}
|
|
132
|
+
headingOverride = trimmed;
|
|
133
|
+
}
|
|
134
|
+
const result = await applyIssueFix(db(), {
|
|
135
|
+
issueId: params.id,
|
|
136
|
+
fingerprint,
|
|
137
|
+
includeItems,
|
|
138
|
+
headingOverride,
|
|
139
|
+
actor: { userId: auth.session.userId, role: auth.session.role },
|
|
140
|
+
authorizeContentWrite: contentWriteGate(auth.session),
|
|
141
|
+
});
|
|
142
|
+
switch (result.status) {
|
|
143
|
+
case 'applied':
|
|
144
|
+
return json({
|
|
145
|
+
data: {
|
|
146
|
+
issue: seoIssueToApi(result.issue),
|
|
147
|
+
applied: result.applied,
|
|
148
|
+
...(result.sectionId ? { sectionId: result.sectionId } : {}),
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
case 'stale':
|
|
152
|
+
return errorResponse(result.reason, 409);
|
|
153
|
+
case 'verify-failed':
|
|
154
|
+
return json({ error: VERIFY_FAILED_MESSAGE, reason: result.reason }, 409);
|
|
155
|
+
case 'plan-required':
|
|
156
|
+
return planRequiredResponse(result.denial);
|
|
157
|
+
case 'not-found':
|
|
158
|
+
return errorResponse(result.reason, 404);
|
|
159
|
+
case 'not-allowed':
|
|
160
|
+
return errorResponse(result.reason, result.httpStatus);
|
|
161
|
+
case 'not-fixable':
|
|
162
|
+
return errorResponse(result.reason, result.httpStatus);
|
|
163
|
+
default: {
|
|
164
|
+
const _exhaustive = result;
|
|
165
|
+
return _exhaustive;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
return internalError(err, 'seo/issues/:id/apply-fix');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export async function postSeoIssueDismissFix(request, params) {
|
|
174
|
+
try {
|
|
175
|
+
const auth = await requireAuth(request);
|
|
176
|
+
if (auth.error)
|
|
177
|
+
return auth.error;
|
|
178
|
+
const roleErr = requireRole(auth.session.role, WRITE_ROLES);
|
|
179
|
+
if (roleErr)
|
|
180
|
+
return roleErr;
|
|
181
|
+
if (!hasModel(db(), 'seoIssue'))
|
|
182
|
+
return modelNotAvailable('SeoIssue');
|
|
183
|
+
const issue = await db().seoIssue.findUnique({ where: { id: params.id } });
|
|
184
|
+
if (!issue)
|
|
185
|
+
return errorResponse('Issue not found', 404);
|
|
186
|
+
const { pendingFixFromMetadata } = await import('../../../../seo/issue-fix-ai.js');
|
|
187
|
+
const cached = pendingFixFromMetadata(issue.metadata);
|
|
188
|
+
if (!cached)
|
|
189
|
+
return json({ data: { dismissed: false } });
|
|
190
|
+
// Sticky: remember which document state was rejected so batch
|
|
191
|
+
// generation does not recreate it until the document changes. The
|
|
192
|
+
// marker must match the entity-based live fingerprint the no-cache
|
|
193
|
+
// path computes, which differs from the cached one for insert-link.
|
|
194
|
+
const { dismissalFingerprintFor } = await import('../../../../seo/proposals.js');
|
|
195
|
+
const entityDoc = typeof issue.entityId === 'string'
|
|
196
|
+
? await db()
|
|
197
|
+
.document.findUnique({ where: { id: issue.entityId }, select: { updatedAt: true } })
|
|
198
|
+
.catch(() => null)
|
|
199
|
+
: null;
|
|
200
|
+
const dismissedFingerprint = dismissalFingerprintFor(issue, cached, entityDoc?.updatedAt ?? null);
|
|
201
|
+
await db().seoIssue.update({
|
|
202
|
+
where: { id: issue.id },
|
|
203
|
+
data: {
|
|
204
|
+
metadata: {
|
|
205
|
+
...asRecord(issue.metadata),
|
|
206
|
+
pendingFix: null,
|
|
207
|
+
dismissedFingerprint,
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
try {
|
|
212
|
+
await logEvent({
|
|
213
|
+
event: 'seo_fix_dismissed',
|
|
214
|
+
userId: auth.session.userId,
|
|
215
|
+
details: {
|
|
216
|
+
issueId: issue.id,
|
|
217
|
+
issueType: issue.type,
|
|
218
|
+
entityId: issue.entityId ?? null,
|
|
219
|
+
fingerprint: dismissedFingerprint,
|
|
220
|
+
source: cached.source,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
/* audit-log failures never block */
|
|
226
|
+
}
|
|
227
|
+
return json({ data: { dismissed: true } });
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
return internalError(err, 'seo/issues/:id/dismiss-fix');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=issues-fixes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues-fixes.js","sourceRoot":"","sources":["../../../../../src/api/routes/seo/handlers/issues-fixes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EACL,IAAI,EACJ,aAAa,EACb,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC/E,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAEvE,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAgB,EAChB,MAA8B;IAE9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,KAAK,CAAA;QACjC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC;YAAE,OAAO,iBAAiB,CAAC,UAAU,CAAC,CAAA;QAErE,MAAM,KAAK,GAAG,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAG,EAAE,EAAE,CAAC,CAAA;QAC3E,IAAI,CAAC,KAAK;YAAE,OAAO,aAAa,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAA;QAExD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAA;QAC/C,IAAI,OAAO,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC,KAAK,CAAA;QAE1C,MAAM,UAAU,GAAG;YACjB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAA;QACD,MAAM,MAAM,GAAG;YACb,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC;SACnC,CAAA;QAED,IAAI,MAAuE,CAAA;QAC3E,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,uCAAuC,CAAC,CAAA;QACpF,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,0CAA0C,CAAC,CAAA;QAC1F,IAAI,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAChF,MAAM,GAAG;gBACP,EAAE,EAAE,EAAE,EAAE;gBACR,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAA;QACH,CAAC;aAAM,CAAC;YACN,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;YAC5D,IACE,CAAC,QAAQ;gBACT,cAAc,CAAC,iBAAiB,CAAC;gBACjC,CAAC,MAAM,mBAAmB,CAAC,iBAAiB,EAAE,sBAAsB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,EAC3F,CAAC;gBACD,MAAM,GAAG;oBACP,EAAE,EAAE,EAAE,EAAE;oBACR,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;oBAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;iBAC9B,CAAA;YACH,CAAC;QACH,CAAC;QAED,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAA;QACxE,MAAM,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,GACpD,MAAM,MAAM,CAAC,iCAAiC,CAAC,CAAA;QACjD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAEhE,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QAC7C,IAAI,YAAqC,CAAA;QACzC,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACzB,sEAAsE;YACtE,0DAA0D;YAC1D,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAA;YACtE,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,EAAE,EAAE,CAAC;iBACvC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;iBAClC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACtB,yEAAyE;gBACzE,SAAS,CAAC,IAAI,CAAC,2CAA2C,EAAE;oBAC1D,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACxD,CAAC,CAAA;gBACF,OAAO,SAAS,CAAA;YAClB,CAAC,CAAC,CAAA;YACJ,YAAY,GAAG;gBACb,GAAG,YAAY;gBACf,GAAG,oBAAoB,CAAC,MAAM,CAAC,UAAU,EAAE;oBACzC,cAAc,EAAE,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC;iBAC9E,CAAC;aACH,CAAA;QACH,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,EAAE,GAAG,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;QACtD,CAAC;QAED,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YACzB,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;YACvB,IAAI,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE;SACjC,CAAC,CAAA;QAEF,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAC1D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,aAAa,CAAC,GAAG,EAAE,4BAA4B,CAAC,CAAA;IACzD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAgB,EAChB,MAA8B;IAE9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,KAAK,CAAA;QACjC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;QAC3D,IAAI,OAAO;YAAE,OAAO,OAAO,CAAA;QAC3B,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC;YAAE,OAAO,iBAAiB,CAAC,UAAU,CAAC,CAAA;QAErE,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAA;QACjE,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,MAAM,CAAC,KAAK,CAAA;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;QACxB,MAAM,CAAC,EAAE,aAAa,EAAE,qBAAqB,EAAE,EAAE,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,GAClF,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,MAAM,CAAC,oCAAoC,CAAC;YAC5C,MAAM,CAAC,kCAAkC,CAAC;SAC3C,CAAC,CAAA;QACJ,MAAM,WAAW,GAAG,OAAO,IAAI,EAAE,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;QACxF,2EAA2E;QAC3E,0EAA0E;QAC1E,IAAI,YAAkC,CAAA;QACtC,IAAI,IAAI,EAAE,YAAY,KAAK,SAAS,EAAE,CAAC;YACrC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAA;YAC7B,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBACnB,GAAG,CAAC,MAAM,GAAG,aAAa;gBAC1B,CAAC,GAAG,CAAC,KAAK,CACR,CAAC,CAAU,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CACpF;gBACD,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,EAChC,CAAC;gBACD,OAAO,aAAa,CAClB,4CAA4C,aAAa,iCAAiC,EAC1F,GAAG,CACJ,CAAA;YACH,CAAC;YACD,YAAY,GAAG,GAAG,CAAA;QACpB,CAAC;QACD,IAAI,eAAmC,CAAA;QACvC,IAAI,IAAI,EAAE,eAAe,KAAK,SAAS,EAAE,CAAC;YACxC,kFAAkF;YAClF,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;YAC7F,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;gBACzD,OAAO,aAAa,CAClB,+CAA+C,eAAe,aAAa,EAC3E,GAAG,CACJ,CAAA;YACH,CAAC;YACD,eAAe,GAAG,OAAO,CAAA;QAC3B,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,EAAE;YACvC,OAAO,EAAE,MAAM,CAAC,EAAG;YACnB,WAAW;YACX,YAAY;YACZ,eAAe;YACf,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YAC/D,qBAAqB,EAAE,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;SACtD,CAAC,CAAA;QAEF,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC;oBACV,IAAI,EAAE;wBACJ,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC;wBAClC,OAAO,EAAE,MAAM,CAAC,OAAO;wBACvB,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC7D;iBACF,CAAC,CAAA;YACJ,KAAK,OAAO;gBACV,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YAC1C,KAAK,eAAe;gBAClB,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAA;YAC3E,KAAK,eAAe;gBAClB,OAAO,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC5C,KAAK,WAAW;gBACd,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YAC1C,KAAK,aAAa;gBAChB,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;YACxD,KAAK,aAAa;gBAChB,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;YACxD,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,WAAW,GAAU,MAAM,CAAA;gBACjC,OAAO,WAAW,CAAA;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,aAAa,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAA;IACvD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAgB,EAChB,MAA8B;IAE9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,KAAK,CAAA;QACjC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;QAC3D,IAAI,OAAO;YAAE,OAAO,OAAO,CAAA;QAC3B,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC;YAAE,OAAO,iBAAiB,CAAC,UAAU,CAAC,CAAA;QAErE,MAAM,KAAK,GAAG,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAG,EAAE,EAAE,CAAC,CAAA;QAC3E,IAAI,CAAC,KAAK;YAAE,OAAO,aAAa,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAA;QAExD,MAAM,EAAE,sBAAsB,EAAE,GAAG,MAAM,MAAM,CAAC,iCAAiC,CAAC,CAAA;QAClF,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QACrD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QAExD,8DAA8D;QAC9D,kEAAkE;QAClE,mEAAmE;QACnE,oEAAoE;QACpE,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAA;QAChF,MAAM,SAAS,GACb,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;YAChC,CAAC,CAAC,MAAM,EAAE,EAAE;iBACP,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;iBACnF,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;YACtB,CAAC,CAAC,IAAI,CAAA;QACV,MAAM,oBAAoB,GAAG,uBAAuB,CAClD,KAAK,EACL,MAAM,EACN,SAAS,EAAE,SAAS,IAAI,IAAI,CAC7B,CAAA;QACD,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YACzB,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;YACvB,IAAI,EAAE;gBACJ,QAAQ,EAAE;oBACR,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC;oBAC3B,UAAU,EAAE,IAAI;oBAChB,oBAAoB;iBACrB;aACF;SACF,CAAC,CAAA;QACF,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC;gBACb,KAAK,EAAE,mBAAmB;gBAC1B,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC3B,OAAO,EAAE;oBACP,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,SAAS,EAAE,KAAK,CAAC,IAAI;oBACrB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;oBAChC,WAAW,EAAE,oBAAoB;oBACjC,MAAM,EAAE,MAAM,CAAC,MAAM;iBACtB;aACF,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,aAAa,CAAC,GAAG,EAAE,4BAA4B,CAAC,CAAA;IACzD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function getSeoIssues(request: Request): Promise<Response>;
|
|
2
|
+
export declare function getSeoIssuesExportBrief(request: Request): Promise<Response>;
|
|
3
|
+
export declare function getSeoIssue(request: Request, params: Record<string, string>): Promise<Response>;
|
|
4
|
+
export declare function postSeoIssueResolve(request: Request, params: Record<string, string>): Promise<Response>;
|
|
5
|
+
export declare function postSeoIssueIgnore(request: Request, params: Record<string, string>): Promise<Response>;
|
|
6
|
+
export declare function postSeoIssueReopen(request: Request, params: Record<string, string>): Promise<Response>;
|
|
7
|
+
//# sourceMappingURL=issues-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues-list.d.ts","sourceRoot":"","sources":["../../../../../src/api/routes/seo/handlers/issues-list.ts"],"names":[],"mappings":"AAYA,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2BtE;AAED,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6IjF;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAWnB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAEnB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAEnB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAEnB"}
|