@newcms/database 0.2.0 → 0.3.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/dist/index.cjs +155 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -1
- package/dist/index.d.ts +69 -1
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2692,4 +2692,72 @@ declare class RevisionRepository {
|
|
|
2692
2692
|
private hasChanges;
|
|
2693
2693
|
}
|
|
2694
2694
|
|
|
2695
|
-
|
|
2695
|
+
interface CreateCommentInput {
|
|
2696
|
+
commentPostId: number;
|
|
2697
|
+
commentAuthor: string;
|
|
2698
|
+
commentAuthorEmail?: string;
|
|
2699
|
+
commentAuthorUrl?: string;
|
|
2700
|
+
commentAuthorIp?: string;
|
|
2701
|
+
commentContent: string;
|
|
2702
|
+
commentType?: string;
|
|
2703
|
+
commentParent?: number;
|
|
2704
|
+
userId?: number;
|
|
2705
|
+
commentApproved?: string;
|
|
2706
|
+
}
|
|
2707
|
+
interface CommentRow {
|
|
2708
|
+
commentId: number;
|
|
2709
|
+
commentPostId: number;
|
|
2710
|
+
commentAuthor: string;
|
|
2711
|
+
commentAuthorEmail: string;
|
|
2712
|
+
commentAuthorUrl: string;
|
|
2713
|
+
commentAuthorIp: string;
|
|
2714
|
+
commentDate: Date;
|
|
2715
|
+
commentDateGmt: Date;
|
|
2716
|
+
commentContent: string;
|
|
2717
|
+
commentKarma: number;
|
|
2718
|
+
commentApproved: string;
|
|
2719
|
+
commentAgent: string;
|
|
2720
|
+
commentType: string;
|
|
2721
|
+
commentParent: number;
|
|
2722
|
+
userId: number;
|
|
2723
|
+
}
|
|
2724
|
+
interface CommentModerationRules {
|
|
2725
|
+
blockedWords?: string[];
|
|
2726
|
+
moderationWords?: string[];
|
|
2727
|
+
maxLinks?: number;
|
|
2728
|
+
floodSeconds?: number;
|
|
2729
|
+
}
|
|
2730
|
+
declare class CommentRepository {
|
|
2731
|
+
private db;
|
|
2732
|
+
readonly meta: MetaRepository;
|
|
2733
|
+
constructor(db: Database);
|
|
2734
|
+
create(input: CreateCommentInput): Promise<CommentRow>;
|
|
2735
|
+
getById(id: number): Promise<CommentRow | undefined>;
|
|
2736
|
+
getByPost(postId: number, status?: string): Promise<CommentRow[]>;
|
|
2737
|
+
/**
|
|
2738
|
+
* Get threaded comments — returns flat list, client builds the tree
|
|
2739
|
+
* using commentParent references.
|
|
2740
|
+
*/
|
|
2741
|
+
getThreaded(postId: number, _depth?: number): Promise<CommentRow[]>;
|
|
2742
|
+
approve(id: number): Promise<boolean>;
|
|
2743
|
+
unapprove(id: number): Promise<boolean>;
|
|
2744
|
+
spam(id: number): Promise<boolean>;
|
|
2745
|
+
trash(id: number): Promise<boolean>;
|
|
2746
|
+
deletePermanently(id: number): Promise<boolean>;
|
|
2747
|
+
/**
|
|
2748
|
+
* Check moderation rules before approving a comment.
|
|
2749
|
+
*/
|
|
2750
|
+
moderate(content: string, _authorEmail: string, rules: CommentModerationRules): 'approve' | 'hold' | 'spam';
|
|
2751
|
+
/**
|
|
2752
|
+
* Check for duplicate comment.
|
|
2753
|
+
*/
|
|
2754
|
+
isDuplicate(postId: number, authorEmail: string, content: string): Promise<boolean>;
|
|
2755
|
+
/**
|
|
2756
|
+
* Check flood protection — has user commented too recently?
|
|
2757
|
+
*/
|
|
2758
|
+
isFlooding(authorIp: string, floodSeconds: number): Promise<boolean>;
|
|
2759
|
+
countByStatus(postId?: number): Promise<Record<string, number>>;
|
|
2760
|
+
private updatePostCommentCount;
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
export { type CacheGroupConfig, type CommentModerationRules, CommentRepository, type CommentRow, type CreateCommentInput, type CreatePostInput, type CreateTermInput, type Database, type DatabaseConfig, type MetaColumnNames, type MetaEntry, MetaRepository, type MetaTableColumns, ObjectCache, type ObjectCacheConfig, OptionsRepository, PostRepository, type PostRow, RevisionRepository, TaxonomyRepository, type TermRow, type UpdatePostInput, commentmeta, comments, createConnection, links, options, postmeta, posts, scheduledEvents, sessions, termRelationships, termTaxonomy, termmeta, terms, usermeta, users };
|
package/dist/index.d.ts
CHANGED
|
@@ -2692,4 +2692,72 @@ declare class RevisionRepository {
|
|
|
2692
2692
|
private hasChanges;
|
|
2693
2693
|
}
|
|
2694
2694
|
|
|
2695
|
-
|
|
2695
|
+
interface CreateCommentInput {
|
|
2696
|
+
commentPostId: number;
|
|
2697
|
+
commentAuthor: string;
|
|
2698
|
+
commentAuthorEmail?: string;
|
|
2699
|
+
commentAuthorUrl?: string;
|
|
2700
|
+
commentAuthorIp?: string;
|
|
2701
|
+
commentContent: string;
|
|
2702
|
+
commentType?: string;
|
|
2703
|
+
commentParent?: number;
|
|
2704
|
+
userId?: number;
|
|
2705
|
+
commentApproved?: string;
|
|
2706
|
+
}
|
|
2707
|
+
interface CommentRow {
|
|
2708
|
+
commentId: number;
|
|
2709
|
+
commentPostId: number;
|
|
2710
|
+
commentAuthor: string;
|
|
2711
|
+
commentAuthorEmail: string;
|
|
2712
|
+
commentAuthorUrl: string;
|
|
2713
|
+
commentAuthorIp: string;
|
|
2714
|
+
commentDate: Date;
|
|
2715
|
+
commentDateGmt: Date;
|
|
2716
|
+
commentContent: string;
|
|
2717
|
+
commentKarma: number;
|
|
2718
|
+
commentApproved: string;
|
|
2719
|
+
commentAgent: string;
|
|
2720
|
+
commentType: string;
|
|
2721
|
+
commentParent: number;
|
|
2722
|
+
userId: number;
|
|
2723
|
+
}
|
|
2724
|
+
interface CommentModerationRules {
|
|
2725
|
+
blockedWords?: string[];
|
|
2726
|
+
moderationWords?: string[];
|
|
2727
|
+
maxLinks?: number;
|
|
2728
|
+
floodSeconds?: number;
|
|
2729
|
+
}
|
|
2730
|
+
declare class CommentRepository {
|
|
2731
|
+
private db;
|
|
2732
|
+
readonly meta: MetaRepository;
|
|
2733
|
+
constructor(db: Database);
|
|
2734
|
+
create(input: CreateCommentInput): Promise<CommentRow>;
|
|
2735
|
+
getById(id: number): Promise<CommentRow | undefined>;
|
|
2736
|
+
getByPost(postId: number, status?: string): Promise<CommentRow[]>;
|
|
2737
|
+
/**
|
|
2738
|
+
* Get threaded comments — returns flat list, client builds the tree
|
|
2739
|
+
* using commentParent references.
|
|
2740
|
+
*/
|
|
2741
|
+
getThreaded(postId: number, _depth?: number): Promise<CommentRow[]>;
|
|
2742
|
+
approve(id: number): Promise<boolean>;
|
|
2743
|
+
unapprove(id: number): Promise<boolean>;
|
|
2744
|
+
spam(id: number): Promise<boolean>;
|
|
2745
|
+
trash(id: number): Promise<boolean>;
|
|
2746
|
+
deletePermanently(id: number): Promise<boolean>;
|
|
2747
|
+
/**
|
|
2748
|
+
* Check moderation rules before approving a comment.
|
|
2749
|
+
*/
|
|
2750
|
+
moderate(content: string, _authorEmail: string, rules: CommentModerationRules): 'approve' | 'hold' | 'spam';
|
|
2751
|
+
/**
|
|
2752
|
+
* Check for duplicate comment.
|
|
2753
|
+
*/
|
|
2754
|
+
isDuplicate(postId: number, authorEmail: string, content: string): Promise<boolean>;
|
|
2755
|
+
/**
|
|
2756
|
+
* Check flood protection — has user commented too recently?
|
|
2757
|
+
*/
|
|
2758
|
+
isFlooding(authorIp: string, floodSeconds: number): Promise<boolean>;
|
|
2759
|
+
countByStatus(postId?: number): Promise<Record<string, number>>;
|
|
2760
|
+
private updatePostCommentCount;
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
export { type CacheGroupConfig, type CommentModerationRules, CommentRepository, type CommentRow, type CreateCommentInput, type CreatePostInput, type CreateTermInput, type Database, type DatabaseConfig, type MetaColumnNames, type MetaEntry, MetaRepository, type MetaTableColumns, ObjectCache, type ObjectCacheConfig, OptionsRepository, PostRepository, type PostRow, RevisionRepository, TaxonomyRepository, type TermRow, type UpdatePostInput, commentmeta, comments, createConnection, links, options, postmeta, posts, scheduledEvents, sessions, termRelationships, termTaxonomy, termmeta, terms, usermeta, users };
|
package/dist/index.js
CHANGED
|
@@ -1503,7 +1503,161 @@ var RevisionRepository = class {
|
|
|
1503
1503
|
return false;
|
|
1504
1504
|
}
|
|
1505
1505
|
};
|
|
1506
|
+
|
|
1507
|
+
// src/repositories/comment-repository.ts
|
|
1508
|
+
import { eq as eq6, and as and5, sql as sql5 } from "drizzle-orm";
|
|
1509
|
+
var CommentRepository = class {
|
|
1510
|
+
constructor(db) {
|
|
1511
|
+
this.db = db;
|
|
1512
|
+
const metaCols = {
|
|
1513
|
+
metaId: commentmeta.metaId,
|
|
1514
|
+
objectId: commentmeta.commentId,
|
|
1515
|
+
metaKey: commentmeta.metaKey,
|
|
1516
|
+
metaValue: commentmeta.metaValue,
|
|
1517
|
+
metaValueJson: commentmeta.metaValueJson
|
|
1518
|
+
};
|
|
1519
|
+
const colNames = {
|
|
1520
|
+
table: "commentmeta",
|
|
1521
|
+
sql: { metaId: "meta_id", objectId: "comment_id", metaKey: "meta_key", metaValue: "meta_value", metaValueJson: "meta_value_json" },
|
|
1522
|
+
ts: { metaId: "metaId", objectId: "commentId", metaKey: "metaKey", metaValue: "metaValue", metaValueJson: "metaValueJson" }
|
|
1523
|
+
};
|
|
1524
|
+
this.meta = new MetaRepository(db, commentmeta, metaCols, colNames);
|
|
1525
|
+
}
|
|
1526
|
+
db;
|
|
1527
|
+
meta;
|
|
1528
|
+
async create(input) {
|
|
1529
|
+
const now = /* @__PURE__ */ new Date();
|
|
1530
|
+
const [row] = await this.db.insert(comments).values({
|
|
1531
|
+
commentPostId: input.commentPostId,
|
|
1532
|
+
commentAuthor: input.commentAuthor,
|
|
1533
|
+
commentAuthorEmail: input.commentAuthorEmail ?? "",
|
|
1534
|
+
commentAuthorUrl: input.commentAuthorUrl ?? "",
|
|
1535
|
+
commentAuthorIp: input.commentAuthorIp ?? "",
|
|
1536
|
+
commentContent: input.commentContent,
|
|
1537
|
+
commentType: input.commentType ?? "comment",
|
|
1538
|
+
commentParent: input.commentParent ?? 0,
|
|
1539
|
+
userId: input.userId ?? 0,
|
|
1540
|
+
commentApproved: input.commentApproved ?? "1",
|
|
1541
|
+
commentDate: now,
|
|
1542
|
+
commentDateGmt: now
|
|
1543
|
+
}).returning();
|
|
1544
|
+
await this.updatePostCommentCount(input.commentPostId);
|
|
1545
|
+
return row;
|
|
1546
|
+
}
|
|
1547
|
+
async getById(id) {
|
|
1548
|
+
const rows = await this.db.select().from(comments).where(eq6(comments.commentId, id)).limit(1);
|
|
1549
|
+
return rows[0];
|
|
1550
|
+
}
|
|
1551
|
+
async getByPost(postId, status) {
|
|
1552
|
+
const conditions = [eq6(comments.commentPostId, postId)];
|
|
1553
|
+
if (status) conditions.push(eq6(comments.commentApproved, status));
|
|
1554
|
+
return await this.db.select().from(comments).where(and5(...conditions)).orderBy(comments.commentDate);
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Get threaded comments — returns flat list, client builds the tree
|
|
1558
|
+
* using commentParent references.
|
|
1559
|
+
*/
|
|
1560
|
+
async getThreaded(postId, _depth = 5) {
|
|
1561
|
+
return this.getByPost(postId, "1");
|
|
1562
|
+
}
|
|
1563
|
+
async approve(id) {
|
|
1564
|
+
const comment = await this.getById(id);
|
|
1565
|
+
if (!comment) return false;
|
|
1566
|
+
await this.db.update(comments).set({ commentApproved: "1" }).where(eq6(comments.commentId, id));
|
|
1567
|
+
await this.updatePostCommentCount(comment.commentPostId);
|
|
1568
|
+
return true;
|
|
1569
|
+
}
|
|
1570
|
+
async unapprove(id) {
|
|
1571
|
+
const comment = await this.getById(id);
|
|
1572
|
+
if (!comment) return false;
|
|
1573
|
+
await this.db.update(comments).set({ commentApproved: "0" }).where(eq6(comments.commentId, id));
|
|
1574
|
+
await this.updatePostCommentCount(comment.commentPostId);
|
|
1575
|
+
return true;
|
|
1576
|
+
}
|
|
1577
|
+
async spam(id) {
|
|
1578
|
+
const comment = await this.getById(id);
|
|
1579
|
+
if (!comment) return false;
|
|
1580
|
+
await this.db.update(comments).set({ commentApproved: "spam" }).where(eq6(comments.commentId, id));
|
|
1581
|
+
await this.updatePostCommentCount(comment.commentPostId);
|
|
1582
|
+
return true;
|
|
1583
|
+
}
|
|
1584
|
+
async trash(id) {
|
|
1585
|
+
const comment = await this.getById(id);
|
|
1586
|
+
if (!comment) return false;
|
|
1587
|
+
await this.meta.update(id, "_trash_status", comment.commentApproved);
|
|
1588
|
+
await this.db.update(comments).set({ commentApproved: "trash" }).where(eq6(comments.commentId, id));
|
|
1589
|
+
await this.updatePostCommentCount(comment.commentPostId);
|
|
1590
|
+
return true;
|
|
1591
|
+
}
|
|
1592
|
+
async deletePermanently(id) {
|
|
1593
|
+
const comment = await this.getById(id);
|
|
1594
|
+
if (!comment) return false;
|
|
1595
|
+
await this.meta.deleteAllForObject(id);
|
|
1596
|
+
const result = await this.db.delete(comments).where(eq6(comments.commentId, id)).returning({ commentId: comments.commentId });
|
|
1597
|
+
if (result.length > 0) await this.updatePostCommentCount(comment.commentPostId);
|
|
1598
|
+
return result.length > 0;
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Check moderation rules before approving a comment.
|
|
1602
|
+
*/
|
|
1603
|
+
moderate(content, _authorEmail, rules) {
|
|
1604
|
+
const lower = content.toLowerCase();
|
|
1605
|
+
if (rules.blockedWords) {
|
|
1606
|
+
for (const word of rules.blockedWords) {
|
|
1607
|
+
if (lower.includes(word.toLowerCase())) return "spam";
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
if (rules.moderationWords) {
|
|
1611
|
+
for (const word of rules.moderationWords) {
|
|
1612
|
+
if (lower.includes(word.toLowerCase())) return "hold";
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
if (rules.maxLinks !== void 0) {
|
|
1616
|
+
const linkCount = (content.match(/https?:\/\//g) ?? []).length;
|
|
1617
|
+
if (linkCount > rules.maxLinks) return "hold";
|
|
1618
|
+
}
|
|
1619
|
+
return "approve";
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Check for duplicate comment.
|
|
1623
|
+
*/
|
|
1624
|
+
async isDuplicate(postId, authorEmail, content) {
|
|
1625
|
+
const rows = await this.db.select({ commentId: comments.commentId }).from(comments).where(
|
|
1626
|
+
and5(
|
|
1627
|
+
eq6(comments.commentPostId, postId),
|
|
1628
|
+
eq6(comments.commentAuthorEmail, authorEmail),
|
|
1629
|
+
eq6(comments.commentContent, content)
|
|
1630
|
+
)
|
|
1631
|
+
).limit(1);
|
|
1632
|
+
return rows.length > 0;
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Check flood protection — has user commented too recently?
|
|
1636
|
+
*/
|
|
1637
|
+
async isFlooding(authorIp, floodSeconds) {
|
|
1638
|
+
const cutoff = new Date(Date.now() - floodSeconds * 1e3);
|
|
1639
|
+
const rows = await this.db.select({ commentId: comments.commentId }).from(comments).where(
|
|
1640
|
+
and5(
|
|
1641
|
+
eq6(comments.commentAuthorIp, authorIp),
|
|
1642
|
+
sql5`${comments.commentDate} > ${cutoff}`
|
|
1643
|
+
)
|
|
1644
|
+
).limit(1);
|
|
1645
|
+
return rows.length > 0;
|
|
1646
|
+
}
|
|
1647
|
+
async countByStatus(postId) {
|
|
1648
|
+
const conditions = postId !== void 0 ? [eq6(comments.commentPostId, postId)] : [];
|
|
1649
|
+
const rows = await this.db.select({ status: comments.commentApproved, count: sql5`count(*)::int` }).from(comments).where(conditions.length > 0 ? and5(...conditions) : void 0).groupBy(comments.commentApproved);
|
|
1650
|
+
const result = {};
|
|
1651
|
+
for (const row of rows) result[row.status] = row.count;
|
|
1652
|
+
return result;
|
|
1653
|
+
}
|
|
1654
|
+
async updatePostCommentCount(postId) {
|
|
1655
|
+
const [result] = await this.db.select({ count: sql5`count(*)::int` }).from(comments).where(and5(eq6(comments.commentPostId, postId), eq6(comments.commentApproved, "1")));
|
|
1656
|
+
await this.db.update(posts).set({ commentCount: result.count }).where(eq6(posts.id, postId));
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1506
1659
|
export {
|
|
1660
|
+
CommentRepository,
|
|
1507
1661
|
MetaRepository,
|
|
1508
1662
|
ObjectCache,
|
|
1509
1663
|
OptionsRepository,
|