@rankmyseo/storage 0.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 +201 -0
- package/dist/index.cjs +641 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +605 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import "server-only";
|
|
3
|
+
|
|
4
|
+
// src/factory.ts
|
|
5
|
+
import "server-only";
|
|
6
|
+
|
|
7
|
+
// src/sqlite-store.ts
|
|
8
|
+
import "server-only";
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
import Database from "better-sqlite3";
|
|
11
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
12
|
+
import { and, eq, gte, lte } from "drizzle-orm";
|
|
13
|
+
|
|
14
|
+
// src/schema/sqlite.ts
|
|
15
|
+
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
16
|
+
var projects = sqliteTable("rms_projects", {
|
|
17
|
+
id: text("id").primaryKey(),
|
|
18
|
+
tenantId: text("tenant_id").notNull(),
|
|
19
|
+
name: text("name").notNull(),
|
|
20
|
+
domain: text("domain").notNull(),
|
|
21
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
22
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
|
|
23
|
+
});
|
|
24
|
+
var keywords = sqliteTable("rms_keywords", {
|
|
25
|
+
id: text("id").primaryKey(),
|
|
26
|
+
tenantId: text("tenant_id").notNull(),
|
|
27
|
+
projectId: text("project_id").notNull(),
|
|
28
|
+
text: text("text").notNull(),
|
|
29
|
+
country: text("country").notNull(),
|
|
30
|
+
device: text("device").notNull(),
|
|
31
|
+
tags: text("tags").notNull(),
|
|
32
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
|
|
33
|
+
});
|
|
34
|
+
var rankSnapshots = sqliteTable("rms_rank_snapshots", {
|
|
35
|
+
id: text("id").primaryKey(),
|
|
36
|
+
tenantId: text("tenant_id").notNull(),
|
|
37
|
+
projectId: text("project_id").notNull(),
|
|
38
|
+
keywordId: text("keyword_id").notNull(),
|
|
39
|
+
position: integer("position"),
|
|
40
|
+
url: text("url"),
|
|
41
|
+
source: text("source").notNull(),
|
|
42
|
+
device: text("device").notNull(),
|
|
43
|
+
country: text("country").notNull(),
|
|
44
|
+
capturedAt: integer("captured_at", { mode: "timestamp_ms" }).notNull(),
|
|
45
|
+
serpFeatures: text("serp_features")
|
|
46
|
+
});
|
|
47
|
+
var audits = sqliteTable("rms_audits", {
|
|
48
|
+
id: text("id").primaryKey(),
|
|
49
|
+
tenantId: text("tenant_id").notNull(),
|
|
50
|
+
projectId: text("project_id").notNull(),
|
|
51
|
+
url: text("url").notNull(),
|
|
52
|
+
score: integer("score").notNull(),
|
|
53
|
+
checks: text("checks").notNull().default("[]"),
|
|
54
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
|
|
55
|
+
});
|
|
56
|
+
var reports = sqliteTable("rms_reports", {
|
|
57
|
+
id: text("id").primaryKey(),
|
|
58
|
+
tenantId: text("tenant_id").notNull(),
|
|
59
|
+
projectId: text("project_id").notNull(),
|
|
60
|
+
title: text("title").notNull(),
|
|
61
|
+
from: integer("from", { mode: "timestamp_ms" }).notNull(),
|
|
62
|
+
to: integer("to", { mode: "timestamp_ms" }).notNull(),
|
|
63
|
+
summary: text("summary"),
|
|
64
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
|
|
65
|
+
});
|
|
66
|
+
var dashboardConfigs = sqliteTable("rms_dashboard_configs", {
|
|
67
|
+
id: text("id").primaryKey(),
|
|
68
|
+
tenantId: text("tenant_id").notNull(),
|
|
69
|
+
projectId: text("project_id").notNull(),
|
|
70
|
+
widgets: text("widgets").notNull(),
|
|
71
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
|
|
72
|
+
});
|
|
73
|
+
var blogPosts = sqliteTable("rms_blog_posts", {
|
|
74
|
+
id: text("id").primaryKey(),
|
|
75
|
+
tenantId: text("tenant_id").notNull(),
|
|
76
|
+
projectId: text("project_id").notNull(),
|
|
77
|
+
title: text("title").notNull(),
|
|
78
|
+
slug: text("slug").notNull(),
|
|
79
|
+
content: text("content").notNull().default(""),
|
|
80
|
+
targetKeyword: text("target_keyword").notNull().default(""),
|
|
81
|
+
intent: text("intent").notNull().default("informational"),
|
|
82
|
+
metaTitle: text("meta_title").notNull().default(""),
|
|
83
|
+
metaDescription: text("meta_description").notNull().default(""),
|
|
84
|
+
status: text("status").notNull().default("draft"),
|
|
85
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
86
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
|
|
87
|
+
});
|
|
88
|
+
var sqliteSchema = {
|
|
89
|
+
projects,
|
|
90
|
+
keywords,
|
|
91
|
+
rankSnapshots,
|
|
92
|
+
audits,
|
|
93
|
+
reports,
|
|
94
|
+
dashboardConfigs,
|
|
95
|
+
blogPosts
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/sqlite-store.ts
|
|
99
|
+
function mapBlogRow(row) {
|
|
100
|
+
return {
|
|
101
|
+
id: row.id,
|
|
102
|
+
tenantId: row.tenantId,
|
|
103
|
+
projectId: row.projectId,
|
|
104
|
+
title: row.title,
|
|
105
|
+
slug: row.slug,
|
|
106
|
+
content: row.content,
|
|
107
|
+
targetKeyword: row.targetKeyword,
|
|
108
|
+
intent: row.intent,
|
|
109
|
+
metaTitle: row.metaTitle,
|
|
110
|
+
metaDescription: row.metaDescription,
|
|
111
|
+
status: row.status,
|
|
112
|
+
createdAt: row.createdAt,
|
|
113
|
+
updatedAt: row.updatedAt
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function parseChecks(raw) {
|
|
117
|
+
if (!raw) return [];
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(raw);
|
|
120
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
121
|
+
} catch {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function parseSummary(raw) {
|
|
126
|
+
if (!raw) return void 0;
|
|
127
|
+
try {
|
|
128
|
+
return JSON.parse(raw);
|
|
129
|
+
} catch {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function mapAuditRow(row) {
|
|
134
|
+
return {
|
|
135
|
+
id: row.id,
|
|
136
|
+
tenantId: row.tenantId,
|
|
137
|
+
projectId: row.projectId,
|
|
138
|
+
url: row.url,
|
|
139
|
+
score: row.score,
|
|
140
|
+
checks: parseChecks(row.checks),
|
|
141
|
+
createdAt: row.createdAt
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function mapReportRow(row) {
|
|
145
|
+
return {
|
|
146
|
+
id: row.id,
|
|
147
|
+
tenantId: row.tenantId,
|
|
148
|
+
projectId: row.projectId,
|
|
149
|
+
title: row.title,
|
|
150
|
+
from: row.from,
|
|
151
|
+
to: row.to,
|
|
152
|
+
summary: parseSummary(row.summary),
|
|
153
|
+
createdAt: row.createdAt
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function parseTags(raw) {
|
|
157
|
+
try {
|
|
158
|
+
const parsed = JSON.parse(raw);
|
|
159
|
+
return Array.isArray(parsed) ? parsed.filter((t) => typeof t === "string") : [];
|
|
160
|
+
} catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function migrate(sqlite) {
|
|
165
|
+
sqlite.exec(`
|
|
166
|
+
CREATE TABLE IF NOT EXISTS rms_projects (
|
|
167
|
+
id TEXT PRIMARY KEY,
|
|
168
|
+
tenant_id TEXT NOT NULL,
|
|
169
|
+
name TEXT NOT NULL,
|
|
170
|
+
domain TEXT NOT NULL,
|
|
171
|
+
created_at INTEGER NOT NULL,
|
|
172
|
+
updated_at INTEGER NOT NULL
|
|
173
|
+
);
|
|
174
|
+
CREATE TABLE IF NOT EXISTS rms_keywords (
|
|
175
|
+
id TEXT PRIMARY KEY,
|
|
176
|
+
tenant_id TEXT NOT NULL,
|
|
177
|
+
project_id TEXT NOT NULL,
|
|
178
|
+
text TEXT NOT NULL,
|
|
179
|
+
country TEXT NOT NULL,
|
|
180
|
+
device TEXT NOT NULL,
|
|
181
|
+
tags TEXT NOT NULL,
|
|
182
|
+
created_at INTEGER NOT NULL
|
|
183
|
+
);
|
|
184
|
+
CREATE TABLE IF NOT EXISTS rms_rank_snapshots (
|
|
185
|
+
id TEXT PRIMARY KEY,
|
|
186
|
+
tenant_id TEXT NOT NULL,
|
|
187
|
+
project_id TEXT NOT NULL,
|
|
188
|
+
keyword_id TEXT NOT NULL,
|
|
189
|
+
position INTEGER,
|
|
190
|
+
url TEXT,
|
|
191
|
+
source TEXT NOT NULL,
|
|
192
|
+
device TEXT NOT NULL,
|
|
193
|
+
country TEXT NOT NULL,
|
|
194
|
+
captured_at INTEGER NOT NULL,
|
|
195
|
+
serp_features TEXT
|
|
196
|
+
);
|
|
197
|
+
CREATE TABLE IF NOT EXISTS rms_audits (
|
|
198
|
+
id TEXT PRIMARY KEY,
|
|
199
|
+
tenant_id TEXT NOT NULL,
|
|
200
|
+
project_id TEXT NOT NULL,
|
|
201
|
+
url TEXT NOT NULL,
|
|
202
|
+
score INTEGER NOT NULL,
|
|
203
|
+
checks TEXT NOT NULL DEFAULT '[]',
|
|
204
|
+
created_at INTEGER NOT NULL
|
|
205
|
+
);
|
|
206
|
+
CREATE TABLE IF NOT EXISTS rms_reports (
|
|
207
|
+
id TEXT PRIMARY KEY,
|
|
208
|
+
tenant_id TEXT NOT NULL,
|
|
209
|
+
project_id TEXT NOT NULL,
|
|
210
|
+
title TEXT NOT NULL,
|
|
211
|
+
"from" INTEGER NOT NULL,
|
|
212
|
+
"to" INTEGER NOT NULL,
|
|
213
|
+
summary TEXT,
|
|
214
|
+
created_at INTEGER NOT NULL
|
|
215
|
+
);
|
|
216
|
+
CREATE TABLE IF NOT EXISTS rms_dashboard_configs (
|
|
217
|
+
id TEXT PRIMARY KEY,
|
|
218
|
+
tenant_id TEXT NOT NULL,
|
|
219
|
+
project_id TEXT NOT NULL,
|
|
220
|
+
widgets TEXT NOT NULL,
|
|
221
|
+
updated_at INTEGER NOT NULL
|
|
222
|
+
);
|
|
223
|
+
CREATE TABLE IF NOT EXISTS rms_blog_posts (
|
|
224
|
+
id TEXT PRIMARY KEY,
|
|
225
|
+
tenant_id TEXT NOT NULL,
|
|
226
|
+
project_id TEXT NOT NULL,
|
|
227
|
+
title TEXT NOT NULL,
|
|
228
|
+
slug TEXT NOT NULL,
|
|
229
|
+
content TEXT NOT NULL DEFAULT '',
|
|
230
|
+
target_keyword TEXT NOT NULL DEFAULT '',
|
|
231
|
+
intent TEXT NOT NULL DEFAULT 'informational',
|
|
232
|
+
meta_title TEXT NOT NULL DEFAULT '',
|
|
233
|
+
meta_description TEXT NOT NULL DEFAULT '',
|
|
234
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
235
|
+
created_at INTEGER NOT NULL,
|
|
236
|
+
updated_at INTEGER NOT NULL
|
|
237
|
+
);
|
|
238
|
+
`);
|
|
239
|
+
try {
|
|
240
|
+
sqlite.exec(`ALTER TABLE rms_audits ADD COLUMN checks TEXT NOT NULL DEFAULT '[]'`);
|
|
241
|
+
} catch {
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
sqlite.exec(`ALTER TABLE rms_reports ADD COLUMN summary TEXT`);
|
|
245
|
+
} catch {
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function createSqliteStore(databasePath) {
|
|
249
|
+
const sqlite = new Database(databasePath);
|
|
250
|
+
migrate(sqlite);
|
|
251
|
+
const db = drizzle(sqlite, { schema: sqliteSchema });
|
|
252
|
+
const keywordScopeWhere = (scope) => and(
|
|
253
|
+
eq(keywords.tenantId, scope.tenantId),
|
|
254
|
+
eq(keywords.projectId, scope.projectId)
|
|
255
|
+
);
|
|
256
|
+
return {
|
|
257
|
+
projects: {
|
|
258
|
+
async create(input) {
|
|
259
|
+
const now = /* @__PURE__ */ new Date();
|
|
260
|
+
const row = {
|
|
261
|
+
...input,
|
|
262
|
+
createdAt: now,
|
|
263
|
+
updatedAt: now
|
|
264
|
+
};
|
|
265
|
+
db.insert(projects).values({
|
|
266
|
+
id: row.id,
|
|
267
|
+
tenantId: row.tenantId,
|
|
268
|
+
name: row.name,
|
|
269
|
+
domain: row.domain,
|
|
270
|
+
createdAt: row.createdAt,
|
|
271
|
+
updatedAt: row.updatedAt
|
|
272
|
+
}).run();
|
|
273
|
+
return row;
|
|
274
|
+
},
|
|
275
|
+
async getById(scope, id) {
|
|
276
|
+
const rows = db.select().from(projects).where(and(eq(projects.tenantId, scope.tenantId), eq(projects.id, id))).all();
|
|
277
|
+
const row = rows[0];
|
|
278
|
+
if (!row) return void 0;
|
|
279
|
+
return {
|
|
280
|
+
id: row.id,
|
|
281
|
+
tenantId: row.tenantId,
|
|
282
|
+
name: row.name,
|
|
283
|
+
domain: row.domain,
|
|
284
|
+
createdAt: row.createdAt,
|
|
285
|
+
updatedAt: row.updatedAt
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
async list(scope) {
|
|
289
|
+
return db.select().from(projects).where(eq(projects.tenantId, scope.tenantId)).all().map((row) => ({
|
|
290
|
+
id: row.id,
|
|
291
|
+
tenantId: row.tenantId,
|
|
292
|
+
name: row.name,
|
|
293
|
+
domain: row.domain,
|
|
294
|
+
createdAt: row.createdAt,
|
|
295
|
+
updatedAt: row.updatedAt
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
keywords: {
|
|
300
|
+
async create(input) {
|
|
301
|
+
const row = {
|
|
302
|
+
id: randomUUID(),
|
|
303
|
+
...input,
|
|
304
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
305
|
+
};
|
|
306
|
+
db.insert(keywords).values({
|
|
307
|
+
id: row.id,
|
|
308
|
+
tenantId: row.tenantId,
|
|
309
|
+
projectId: row.projectId,
|
|
310
|
+
text: row.text,
|
|
311
|
+
country: row.country,
|
|
312
|
+
device: row.device,
|
|
313
|
+
tags: JSON.stringify(row.tags),
|
|
314
|
+
createdAt: row.createdAt
|
|
315
|
+
}).run();
|
|
316
|
+
return row;
|
|
317
|
+
},
|
|
318
|
+
async getById(scope, id) {
|
|
319
|
+
const rows = db.select().from(keywords).where(and(keywordScopeWhere(scope), eq(keywords.id, id))).all();
|
|
320
|
+
const row = rows[0];
|
|
321
|
+
if (!row) return void 0;
|
|
322
|
+
return {
|
|
323
|
+
id: row.id,
|
|
324
|
+
tenantId: row.tenantId,
|
|
325
|
+
projectId: row.projectId,
|
|
326
|
+
text: row.text,
|
|
327
|
+
country: row.country,
|
|
328
|
+
device: row.device,
|
|
329
|
+
tags: parseTags(row.tags),
|
|
330
|
+
createdAt: row.createdAt
|
|
331
|
+
};
|
|
332
|
+
},
|
|
333
|
+
async list(scope) {
|
|
334
|
+
return db.select().from(keywords).where(keywordScopeWhere(scope)).all().map((row) => ({
|
|
335
|
+
id: row.id,
|
|
336
|
+
tenantId: row.tenantId,
|
|
337
|
+
projectId: row.projectId,
|
|
338
|
+
text: row.text,
|
|
339
|
+
country: row.country,
|
|
340
|
+
device: row.device,
|
|
341
|
+
tags: parseTags(row.tags),
|
|
342
|
+
createdAt: row.createdAt
|
|
343
|
+
}));
|
|
344
|
+
},
|
|
345
|
+
async delete(scope, id) {
|
|
346
|
+
const result = db.delete(keywords).where(and(keywordScopeWhere(scope), eq(keywords.id, id))).run();
|
|
347
|
+
return result.changes > 0;
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
snapshots: {
|
|
351
|
+
async append(input) {
|
|
352
|
+
const row = {
|
|
353
|
+
id: randomUUID(),
|
|
354
|
+
...input
|
|
355
|
+
};
|
|
356
|
+
db.insert(rankSnapshots).values({
|
|
357
|
+
id: row.id,
|
|
358
|
+
tenantId: row.tenantId,
|
|
359
|
+
projectId: row.projectId,
|
|
360
|
+
keywordId: row.keywordId,
|
|
361
|
+
position: row.position,
|
|
362
|
+
url: row.url,
|
|
363
|
+
source: row.source,
|
|
364
|
+
device: row.device,
|
|
365
|
+
country: row.country,
|
|
366
|
+
capturedAt: row.capturedAt,
|
|
367
|
+
serpFeatures: row.serpFeatures ? JSON.stringify(row.serpFeatures) : null
|
|
368
|
+
}).run();
|
|
369
|
+
return row;
|
|
370
|
+
},
|
|
371
|
+
async listByRange(query) {
|
|
372
|
+
const conditions = [
|
|
373
|
+
eq(rankSnapshots.tenantId, query.tenantId),
|
|
374
|
+
eq(rankSnapshots.projectId, query.projectId),
|
|
375
|
+
gte(rankSnapshots.capturedAt, query.from),
|
|
376
|
+
lte(rankSnapshots.capturedAt, query.to)
|
|
377
|
+
];
|
|
378
|
+
if (query.keywordId) {
|
|
379
|
+
conditions.push(eq(rankSnapshots.keywordId, query.keywordId));
|
|
380
|
+
}
|
|
381
|
+
return db.select().from(rankSnapshots).where(and(...conditions)).all().map((row) => ({
|
|
382
|
+
id: row.id,
|
|
383
|
+
tenantId: row.tenantId,
|
|
384
|
+
projectId: row.projectId,
|
|
385
|
+
keywordId: row.keywordId,
|
|
386
|
+
position: row.position,
|
|
387
|
+
url: row.url,
|
|
388
|
+
source: row.source,
|
|
389
|
+
device: row.device,
|
|
390
|
+
country: row.country,
|
|
391
|
+
capturedAt: row.capturedAt,
|
|
392
|
+
serpFeatures: row.serpFeatures ? JSON.parse(row.serpFeatures) : void 0
|
|
393
|
+
}));
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
audits: {
|
|
397
|
+
async create(input) {
|
|
398
|
+
const row = { ...input, checks: input.checks ?? [], createdAt: /* @__PURE__ */ new Date() };
|
|
399
|
+
db.insert(audits).values({
|
|
400
|
+
id: row.id,
|
|
401
|
+
tenantId: row.tenantId,
|
|
402
|
+
projectId: row.projectId,
|
|
403
|
+
url: row.url,
|
|
404
|
+
score: row.score,
|
|
405
|
+
checks: JSON.stringify(row.checks),
|
|
406
|
+
createdAt: row.createdAt
|
|
407
|
+
}).run();
|
|
408
|
+
return row;
|
|
409
|
+
},
|
|
410
|
+
async getById(scope, id) {
|
|
411
|
+
const rows = db.select().from(audits).where(
|
|
412
|
+
and(
|
|
413
|
+
eq(audits.tenantId, scope.tenantId),
|
|
414
|
+
eq(audits.projectId, scope.projectId),
|
|
415
|
+
eq(audits.id, id)
|
|
416
|
+
)
|
|
417
|
+
).all();
|
|
418
|
+
const row = rows[0];
|
|
419
|
+
if (!row) return void 0;
|
|
420
|
+
return mapAuditRow(row);
|
|
421
|
+
},
|
|
422
|
+
async list(scope) {
|
|
423
|
+
return db.select().from(audits).where(
|
|
424
|
+
and(
|
|
425
|
+
eq(audits.tenantId, scope.tenantId),
|
|
426
|
+
eq(audits.projectId, scope.projectId)
|
|
427
|
+
)
|
|
428
|
+
).all().map(mapAuditRow);
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
reports: {
|
|
432
|
+
async create(input) {
|
|
433
|
+
const row = { ...input, createdAt: /* @__PURE__ */ new Date() };
|
|
434
|
+
db.insert(reports).values({
|
|
435
|
+
id: row.id,
|
|
436
|
+
tenantId: row.tenantId,
|
|
437
|
+
projectId: row.projectId,
|
|
438
|
+
title: row.title,
|
|
439
|
+
from: row.from,
|
|
440
|
+
to: row.to,
|
|
441
|
+
summary: row.summary ? JSON.stringify(row.summary) : null,
|
|
442
|
+
createdAt: row.createdAt
|
|
443
|
+
}).run();
|
|
444
|
+
return row;
|
|
445
|
+
},
|
|
446
|
+
async getById(scope, id) {
|
|
447
|
+
const rows = db.select().from(reports).where(
|
|
448
|
+
and(
|
|
449
|
+
eq(reports.tenantId, scope.tenantId),
|
|
450
|
+
eq(reports.projectId, scope.projectId),
|
|
451
|
+
eq(reports.id, id)
|
|
452
|
+
)
|
|
453
|
+
).all();
|
|
454
|
+
const row = rows[0];
|
|
455
|
+
if (!row) return void 0;
|
|
456
|
+
return mapReportRow(row);
|
|
457
|
+
},
|
|
458
|
+
async list(scope) {
|
|
459
|
+
return db.select().from(reports).where(
|
|
460
|
+
and(
|
|
461
|
+
eq(reports.tenantId, scope.tenantId),
|
|
462
|
+
eq(reports.projectId, scope.projectId)
|
|
463
|
+
)
|
|
464
|
+
).all().map(mapReportRow);
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
dashboard: {
|
|
468
|
+
async get(scope) {
|
|
469
|
+
const rows = db.select().from(dashboardConfigs).where(
|
|
470
|
+
and(
|
|
471
|
+
eq(dashboardConfigs.tenantId, scope.tenantId),
|
|
472
|
+
eq(dashboardConfigs.projectId, scope.projectId)
|
|
473
|
+
)
|
|
474
|
+
).all();
|
|
475
|
+
const row = rows[0];
|
|
476
|
+
if (!row) return void 0;
|
|
477
|
+
return {
|
|
478
|
+
id: row.id,
|
|
479
|
+
tenantId: row.tenantId,
|
|
480
|
+
projectId: row.projectId,
|
|
481
|
+
widgets: JSON.parse(row.widgets),
|
|
482
|
+
updatedAt: row.updatedAt
|
|
483
|
+
};
|
|
484
|
+
},
|
|
485
|
+
async upsert(config) {
|
|
486
|
+
db.insert(dashboardConfigs).values({
|
|
487
|
+
id: config.id,
|
|
488
|
+
tenantId: config.tenantId,
|
|
489
|
+
projectId: config.projectId,
|
|
490
|
+
widgets: JSON.stringify(config.widgets),
|
|
491
|
+
updatedAt: config.updatedAt
|
|
492
|
+
}).onConflictDoUpdate({
|
|
493
|
+
target: dashboardConfigs.id,
|
|
494
|
+
set: {
|
|
495
|
+
widgets: JSON.stringify(config.widgets),
|
|
496
|
+
updatedAt: config.updatedAt
|
|
497
|
+
}
|
|
498
|
+
}).run();
|
|
499
|
+
return config;
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
blog: {
|
|
503
|
+
async create(input) {
|
|
504
|
+
const now = /* @__PURE__ */ new Date();
|
|
505
|
+
const row = { ...input, createdAt: now, updatedAt: now };
|
|
506
|
+
db.insert(blogPosts).values({
|
|
507
|
+
id: row.id,
|
|
508
|
+
tenantId: row.tenantId,
|
|
509
|
+
projectId: row.projectId,
|
|
510
|
+
title: row.title,
|
|
511
|
+
slug: row.slug,
|
|
512
|
+
content: row.content,
|
|
513
|
+
targetKeyword: row.targetKeyword,
|
|
514
|
+
intent: row.intent,
|
|
515
|
+
metaTitle: row.metaTitle,
|
|
516
|
+
metaDescription: row.metaDescription,
|
|
517
|
+
status: row.status,
|
|
518
|
+
createdAt: row.createdAt,
|
|
519
|
+
updatedAt: row.updatedAt
|
|
520
|
+
}).run();
|
|
521
|
+
return row;
|
|
522
|
+
},
|
|
523
|
+
async getById(scope, id) {
|
|
524
|
+
const rows = db.select().from(blogPosts).where(
|
|
525
|
+
and(
|
|
526
|
+
eq(blogPosts.tenantId, scope.tenantId),
|
|
527
|
+
eq(blogPosts.projectId, scope.projectId),
|
|
528
|
+
eq(blogPosts.id, id)
|
|
529
|
+
)
|
|
530
|
+
).all();
|
|
531
|
+
const row = rows[0];
|
|
532
|
+
return row ? mapBlogRow(row) : void 0;
|
|
533
|
+
},
|
|
534
|
+
async list(scope) {
|
|
535
|
+
return db.select().from(blogPosts).where(
|
|
536
|
+
and(
|
|
537
|
+
eq(blogPosts.tenantId, scope.tenantId),
|
|
538
|
+
eq(blogPosts.projectId, scope.projectId)
|
|
539
|
+
)
|
|
540
|
+
).all().map(mapBlogRow);
|
|
541
|
+
},
|
|
542
|
+
async update(scope, id, patch) {
|
|
543
|
+
const rows = db.select().from(blogPosts).where(
|
|
544
|
+
and(
|
|
545
|
+
eq(blogPosts.tenantId, scope.tenantId),
|
|
546
|
+
eq(blogPosts.projectId, scope.projectId),
|
|
547
|
+
eq(blogPosts.id, id)
|
|
548
|
+
)
|
|
549
|
+
).all();
|
|
550
|
+
const existing = rows[0];
|
|
551
|
+
if (!existing) return void 0;
|
|
552
|
+
const merged = mapBlogRow(existing);
|
|
553
|
+
const next = {
|
|
554
|
+
...merged,
|
|
555
|
+
...patch,
|
|
556
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
557
|
+
};
|
|
558
|
+
db.update(blogPosts).set({
|
|
559
|
+
title: next.title,
|
|
560
|
+
slug: next.slug,
|
|
561
|
+
content: next.content,
|
|
562
|
+
targetKeyword: next.targetKeyword,
|
|
563
|
+
intent: next.intent,
|
|
564
|
+
metaTitle: next.metaTitle,
|
|
565
|
+
metaDescription: next.metaDescription,
|
|
566
|
+
status: next.status,
|
|
567
|
+
updatedAt: next.updatedAt
|
|
568
|
+
}).where(
|
|
569
|
+
and(
|
|
570
|
+
eq(blogPosts.tenantId, scope.tenantId),
|
|
571
|
+
eq(blogPosts.projectId, scope.projectId),
|
|
572
|
+
eq(blogPosts.id, id)
|
|
573
|
+
)
|
|
574
|
+
).run();
|
|
575
|
+
return next;
|
|
576
|
+
},
|
|
577
|
+
async delete(scope, id) {
|
|
578
|
+
const result = db.delete(blogPosts).where(
|
|
579
|
+
and(
|
|
580
|
+
eq(blogPosts.tenantId, scope.tenantId),
|
|
581
|
+
eq(blogPosts.projectId, scope.projectId),
|
|
582
|
+
eq(blogPosts.id, id)
|
|
583
|
+
)
|
|
584
|
+
).run();
|
|
585
|
+
return result.changes > 0;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/factory.ts
|
|
592
|
+
function createStore(databaseUrl) {
|
|
593
|
+
if (databaseUrl.startsWith("postgres") || databaseUrl.startsWith("mysql")) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
"Postgres and MySQL adapters are not implemented in M0. Use sqlite:// or :memory:"
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const path = databaseUrl.startsWith("sqlite://") ? databaseUrl.slice("sqlite://".length) : databaseUrl;
|
|
599
|
+
return createSqliteStore(path);
|
|
600
|
+
}
|
|
601
|
+
export {
|
|
602
|
+
createSqliteStore,
|
|
603
|
+
createStore
|
|
604
|
+
};
|
|
605
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/factory.ts","../src/sqlite-store.ts","../src/schema/sqlite.ts"],"sourcesContent":["import \"server-only\";\n\nexport { createStore } from \"./factory.js\";\nexport { createSqliteStore } from \"./sqlite-store.js\";\n","import \"server-only\";\n\nimport { createSqliteStore } from \"./sqlite-store.js\";\n\nexport function createStore(databaseUrl: string) {\n if (\n databaseUrl.startsWith(\"postgres\") ||\n databaseUrl.startsWith(\"mysql\")\n ) {\n throw new Error(\n \"Postgres and MySQL adapters are not implemented in M0. Use sqlite:// or :memory:\",\n );\n }\n\n const path = databaseUrl.startsWith(\"sqlite://\")\n ? databaseUrl.slice(\"sqlite://\".length)\n : databaseUrl;\n\n return createSqliteStore(path);\n}\n","import \"server-only\";\n\nimport { randomUUID } from \"node:crypto\";\nimport Database from \"better-sqlite3\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { and, eq, gte, lte } from \"drizzle-orm\";\nimport type {\n Audit,\n AuditCheckResult,\n BlogPost,\n CreateKeywordInput,\n CreateRankSnapshotInput,\n DashboardConfig,\n Keyword,\n KeywordIntent,\n Project,\n RankSnapshot,\n RankStore,\n Report,\n ReportSummary,\n SnapshotRangeQuery,\n TenantScope,\n} from \"@rankmyseo/core\";\nimport {\n audits,\n blogPosts,\n dashboardConfigs,\n keywords,\n projects,\n rankSnapshots,\n reports,\n sqliteSchema,\n} from \"./schema/sqlite.js\";\n\nfunction mapBlogRow(row: {\n id: string;\n tenantId: string;\n projectId: string;\n title: string;\n slug: string;\n content: string;\n targetKeyword: string;\n intent: string;\n metaTitle: string;\n metaDescription: string;\n status: string;\n createdAt: Date;\n updatedAt: Date;\n}): BlogPost {\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n slug: row.slug,\n content: row.content,\n targetKeyword: row.targetKeyword,\n intent: row.intent as KeywordIntent,\n metaTitle: row.metaTitle,\n metaDescription: row.metaDescription,\n status: row.status as BlogPost[\"status\"],\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nfunction parseChecks(raw: string | null | undefined): AuditCheckResult[] {\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as AuditCheckResult[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction parseSummary(raw: string | null | undefined): ReportSummary | undefined {\n if (!raw) return undefined;\n try {\n return JSON.parse(raw) as ReportSummary;\n } catch {\n return undefined;\n }\n}\n\nfunction mapAuditRow(row: {\n id: string;\n tenantId: string;\n projectId: string;\n url: string;\n score: number;\n checks: string | null;\n createdAt: Date;\n}): Audit {\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n url: row.url,\n score: row.score,\n checks: parseChecks(row.checks),\n createdAt: row.createdAt,\n };\n}\n\nfunction mapReportRow(row: {\n id: string;\n tenantId: string;\n projectId: string;\n title: string;\n from: Date;\n to: Date;\n summary: string | null;\n createdAt: Date;\n}): Report {\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n from: row.from,\n to: row.to,\n summary: parseSummary(row.summary),\n createdAt: row.createdAt,\n };\n}\n\nfunction parseTags(raw: string): string[] {\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed.filter((t): t is string => typeof t === \"string\") : [];\n } catch {\n return [];\n }\n}\n\nfunction migrate(sqlite: Database.Database) {\n sqlite.exec(`\n CREATE TABLE IF NOT EXISTS rms_projects (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n name TEXT NOT NULL,\n domain TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_keywords (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n text TEXT NOT NULL,\n country TEXT NOT NULL,\n device TEXT NOT NULL,\n tags TEXT NOT NULL,\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_rank_snapshots (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n keyword_id TEXT NOT NULL,\n position INTEGER,\n url TEXT,\n source TEXT NOT NULL,\n device TEXT NOT NULL,\n country TEXT NOT NULL,\n captured_at INTEGER NOT NULL,\n serp_features TEXT\n );\n CREATE TABLE IF NOT EXISTS rms_audits (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n url TEXT NOT NULL,\n score INTEGER NOT NULL,\n checks TEXT NOT NULL DEFAULT '[]',\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_reports (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n title TEXT NOT NULL,\n \"from\" INTEGER NOT NULL,\n \"to\" INTEGER NOT NULL,\n summary TEXT,\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_dashboard_configs (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n widgets TEXT NOT NULL,\n updated_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS rms_blog_posts (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n title TEXT NOT NULL,\n slug TEXT NOT NULL,\n content TEXT NOT NULL DEFAULT '',\n target_keyword TEXT NOT NULL DEFAULT '',\n intent TEXT NOT NULL DEFAULT 'informational',\n meta_title TEXT NOT NULL DEFAULT '',\n meta_description TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'draft',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n `);\n try {\n sqlite.exec(`ALTER TABLE rms_audits ADD COLUMN checks TEXT NOT NULL DEFAULT '[]'`);\n } catch {\n /* column exists */\n }\n try {\n sqlite.exec(`ALTER TABLE rms_reports ADD COLUMN summary TEXT`);\n } catch {\n /* column exists */\n }\n}\n\nexport function createSqliteStore(databasePath: string): RankStore {\n const sqlite = new Database(databasePath);\n migrate(sqlite);\n const db = drizzle(sqlite, { schema: sqliteSchema });\n\n const keywordScopeWhere = (scope: TenantScope) =>\n and(\n eq(keywords.tenantId, scope.tenantId),\n eq(keywords.projectId, scope.projectId),\n );\n\n return {\n projects: {\n async create(input) {\n const now = new Date();\n const row: Project = {\n ...input,\n createdAt: now,\n updatedAt: now,\n };\n db.insert(projects)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n name: row.name,\n domain: row.domain,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(projects)\n .where(and(eq(projects.tenantId, scope.tenantId), eq(projects.id, id)))\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return {\n id: row.id,\n tenantId: row.tenantId,\n name: row.name,\n domain: row.domain,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n },\n async list(scope) {\n return db\n .select()\n .from(projects)\n .where(eq(projects.tenantId, scope.tenantId))\n .all()\n .map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n name: row.name,\n domain: row.domain,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n }));\n },\n },\n\n keywords: {\n async create(input: CreateKeywordInput) {\n const row: Keyword = {\n id: randomUUID(),\n ...input,\n createdAt: new Date(),\n };\n db.insert(keywords)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n text: row.text,\n country: row.country,\n device: row.device,\n tags: JSON.stringify(row.tags),\n createdAt: row.createdAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(keywords)\n .where(and(keywordScopeWhere(scope), eq(keywords.id, id)))\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n text: row.text,\n country: row.country,\n device: row.device as Keyword[\"device\"],\n tags: parseTags(row.tags),\n createdAt: row.createdAt,\n };\n },\n async list(scope) {\n return db\n .select()\n .from(keywords)\n .where(keywordScopeWhere(scope))\n .all()\n .map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n text: row.text,\n country: row.country,\n device: row.device as Keyword[\"device\"],\n tags: parseTags(row.tags),\n createdAt: row.createdAt,\n }));\n },\n async delete(scope, id) {\n const result = db\n .delete(keywords)\n .where(and(keywordScopeWhere(scope), eq(keywords.id, id)))\n .run();\n return result.changes > 0;\n },\n },\n\n snapshots: {\n async append(input: CreateRankSnapshotInput) {\n const row: RankSnapshot = {\n id: randomUUID(),\n ...input,\n };\n db.insert(rankSnapshots)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n keywordId: row.keywordId,\n position: row.position,\n url: row.url,\n source: row.source,\n device: row.device,\n country: row.country,\n capturedAt: row.capturedAt,\n serpFeatures: row.serpFeatures\n ? JSON.stringify(row.serpFeatures)\n : null,\n })\n .run();\n return row;\n },\n async listByRange(query: SnapshotRangeQuery) {\n const conditions = [\n eq(rankSnapshots.tenantId, query.tenantId),\n eq(rankSnapshots.projectId, query.projectId),\n gte(rankSnapshots.capturedAt, query.from),\n lte(rankSnapshots.capturedAt, query.to),\n ];\n if (query.keywordId) {\n conditions.push(eq(rankSnapshots.keywordId, query.keywordId));\n }\n\n return db\n .select()\n .from(rankSnapshots)\n .where(and(...conditions))\n .all()\n .map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n keywordId: row.keywordId,\n position: row.position,\n url: row.url,\n source: row.source,\n device: row.device as RankSnapshot[\"device\"],\n country: row.country,\n capturedAt: row.capturedAt,\n serpFeatures: row.serpFeatures\n ? (JSON.parse(row.serpFeatures) as Record<string, unknown>)\n : undefined,\n }));\n },\n },\n\n audits: {\n async create(input) {\n const row: Audit = { ...input, checks: input.checks ?? [], createdAt: new Date() };\n db.insert(audits)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n url: row.url,\n score: row.score,\n checks: JSON.stringify(row.checks),\n createdAt: row.createdAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(audits)\n .where(\n and(\n eq(audits.tenantId, scope.tenantId),\n eq(audits.projectId, scope.projectId),\n eq(audits.id, id),\n ),\n )\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return mapAuditRow(row);\n },\n async list(scope) {\n return db\n .select()\n .from(audits)\n .where(\n and(\n eq(audits.tenantId, scope.tenantId),\n eq(audits.projectId, scope.projectId),\n ),\n )\n .all()\n .map(mapAuditRow);\n },\n },\n\n reports: {\n async create(input) {\n const row: Report = { ...input, createdAt: new Date() };\n db.insert(reports)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n from: row.from,\n to: row.to,\n summary: row.summary ? JSON.stringify(row.summary) : null,\n createdAt: row.createdAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(reports)\n .where(\n and(\n eq(reports.tenantId, scope.tenantId),\n eq(reports.projectId, scope.projectId),\n eq(reports.id, id),\n ),\n )\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return mapReportRow(row);\n },\n async list(scope) {\n return db\n .select()\n .from(reports)\n .where(\n and(\n eq(reports.tenantId, scope.tenantId),\n eq(reports.projectId, scope.projectId),\n ),\n )\n .all()\n .map(mapReportRow);\n },\n },\n\n dashboard: {\n async get(scope) {\n const rows = db\n .select()\n .from(dashboardConfigs)\n .where(\n and(\n eq(dashboardConfigs.tenantId, scope.tenantId),\n eq(dashboardConfigs.projectId, scope.projectId),\n ),\n )\n .all();\n const row = rows[0];\n if (!row) return undefined;\n return {\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n widgets: JSON.parse(row.widgets) as DashboardConfig[\"widgets\"],\n updatedAt: row.updatedAt,\n };\n },\n async upsert(config) {\n db.insert(dashboardConfigs)\n .values({\n id: config.id,\n tenantId: config.tenantId,\n projectId: config.projectId,\n widgets: JSON.stringify(config.widgets),\n updatedAt: config.updatedAt,\n })\n .onConflictDoUpdate({\n target: dashboardConfigs.id,\n set: {\n widgets: JSON.stringify(config.widgets),\n updatedAt: config.updatedAt,\n },\n })\n .run();\n return config;\n },\n },\n\n blog: {\n async create(input) {\n const now = new Date();\n const row: BlogPost = { ...input, createdAt: now, updatedAt: now };\n db.insert(blogPosts)\n .values({\n id: row.id,\n tenantId: row.tenantId,\n projectId: row.projectId,\n title: row.title,\n slug: row.slug,\n content: row.content,\n targetKeyword: row.targetKeyword,\n intent: row.intent,\n metaTitle: row.metaTitle,\n metaDescription: row.metaDescription,\n status: row.status,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })\n .run();\n return row;\n },\n async getById(scope, id) {\n const rows = db\n .select()\n .from(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .all();\n const row = rows[0];\n return row ? mapBlogRow(row) : undefined;\n },\n async list(scope) {\n return db\n .select()\n .from(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n ),\n )\n .all()\n .map(mapBlogRow);\n },\n async update(scope, id, patch) {\n const rows = db\n .select()\n .from(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .all();\n const existing = rows[0];\n if (!existing) return undefined;\n\n const merged = mapBlogRow(existing);\n const next: BlogPost = {\n ...merged,\n ...patch,\n updatedAt: new Date(),\n };\n\n db.update(blogPosts)\n .set({\n title: next.title,\n slug: next.slug,\n content: next.content,\n targetKeyword: next.targetKeyword,\n intent: next.intent,\n metaTitle: next.metaTitle,\n metaDescription: next.metaDescription,\n status: next.status,\n updatedAt: next.updatedAt,\n })\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .run();\n\n return next;\n },\n async delete(scope, id) {\n const result = db\n .delete(blogPosts)\n .where(\n and(\n eq(blogPosts.tenantId, scope.tenantId),\n eq(blogPosts.projectId, scope.projectId),\n eq(blogPosts.id, id),\n ),\n )\n .run();\n return result.changes > 0;\n },\n },\n };\n}\n","import { sqliteTable, text, integer } from \"drizzle-orm/sqlite-core\";\n\nexport const projects = sqliteTable(\"rms_projects\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n name: text(\"name\").notNull(),\n domain: text(\"domain\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const keywords = sqliteTable(\"rms_keywords\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n text: text(\"text\").notNull(),\n country: text(\"country\").notNull(),\n device: text(\"device\").notNull(),\n tags: text(\"tags\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const rankSnapshots = sqliteTable(\"rms_rank_snapshots\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n keywordId: text(\"keyword_id\").notNull(),\n position: integer(\"position\"),\n url: text(\"url\"),\n source: text(\"source\").notNull(),\n device: text(\"device\").notNull(),\n country: text(\"country\").notNull(),\n capturedAt: integer(\"captured_at\", { mode: \"timestamp_ms\" }).notNull(),\n serpFeatures: text(\"serp_features\"),\n});\n\nexport const audits = sqliteTable(\"rms_audits\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n url: text(\"url\").notNull(),\n score: integer(\"score\").notNull(),\n checks: text(\"checks\").notNull().default(\"[]\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const reports = sqliteTable(\"rms_reports\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n title: text(\"title\").notNull(),\n from: integer(\"from\", { mode: \"timestamp_ms\" }).notNull(),\n to: integer(\"to\", { mode: \"timestamp_ms\" }).notNull(),\n summary: text(\"summary\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const dashboardConfigs = sqliteTable(\"rms_dashboard_configs\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n widgets: text(\"widgets\").notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const blogPosts = sqliteTable(\"rms_blog_posts\", {\n id: text(\"id\").primaryKey(),\n tenantId: text(\"tenant_id\").notNull(),\n projectId: text(\"project_id\").notNull(),\n title: text(\"title\").notNull(),\n slug: text(\"slug\").notNull(),\n content: text(\"content\").notNull().default(\"\"),\n targetKeyword: text(\"target_keyword\").notNull().default(\"\"),\n intent: text(\"intent\").notNull().default(\"informational\"),\n metaTitle: text(\"meta_title\").notNull().default(\"\"),\n metaDescription: text(\"meta_description\").notNull().default(\"\"),\n status: text(\"status\").notNull().default(\"draft\"),\n createdAt: integer(\"created_at\", { mode: \"timestamp_ms\" }).notNull(),\n updatedAt: integer(\"updated_at\", { mode: \"timestamp_ms\" }).notNull(),\n});\n\nexport const sqliteSchema = {\n projects,\n keywords,\n rankSnapshots,\n audits,\n reports,\n dashboardConfigs,\n blogPosts,\n};\n"],"mappings":"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","names":[]}
|