117 lines
2.9 KiB
TypeScript
117 lines
2.9 KiB
TypeScript
import { Schema, model, Document, Types } from "mongoose";
|
|
|
|
export interface IRevision {
|
|
content: string;
|
|
author: Types.ObjectId;
|
|
message: string;
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface IComment {
|
|
author: Types.ObjectId;
|
|
content: string;
|
|
parentComment?: Types.ObjectId;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
resolved: boolean;
|
|
}
|
|
|
|
export interface IArticle extends Document {
|
|
slug: string;
|
|
title: string;
|
|
description: string;
|
|
content: string; // Current content in markdown
|
|
category: string;
|
|
tags: string[];
|
|
|
|
// Access control
|
|
accessLevel: "public" | "member" | "cohort" | "admin";
|
|
cohorts?: string[]; // Specific cohorts if accessLevel is 'cohort'
|
|
|
|
// Metadata
|
|
author: Types.ObjectId;
|
|
contributors: Types.ObjectId[];
|
|
views: number;
|
|
likes: number;
|
|
|
|
// Editing
|
|
status: "draft" | "published" | "archived";
|
|
lockedBy?: Types.ObjectId; // If someone is currently editing
|
|
lockedAt?: Date;
|
|
|
|
// History
|
|
revisions: IRevision[];
|
|
comments: IComment[];
|
|
|
|
// Timestamps
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
publishedAt?: Date;
|
|
}
|
|
|
|
const revisionSchema = new Schema<IRevision>({
|
|
content: { type: String, required: true },
|
|
author: { type: Schema.Types.ObjectId, ref: "User", required: true },
|
|
message: { type: String, required: true },
|
|
createdAt: { type: Date, default: Date.now },
|
|
});
|
|
|
|
const commentSchema = new Schema<IComment>(
|
|
{
|
|
author: { type: Schema.Types.ObjectId, ref: "User", required: true },
|
|
content: { type: String, required: true },
|
|
parentComment: { type: Schema.Types.ObjectId, ref: "Comment" },
|
|
resolved: { type: Boolean, default: false },
|
|
},
|
|
{
|
|
timestamps: true,
|
|
},
|
|
);
|
|
|
|
const articleSchema = new Schema<IArticle>(
|
|
{
|
|
slug: { type: String, required: true, unique: true },
|
|
title: { type: String, required: true },
|
|
description: { type: String, required: true },
|
|
content: { type: String, required: true },
|
|
category: { type: String, required: true },
|
|
tags: [{ type: String }],
|
|
|
|
accessLevel: {
|
|
type: String,
|
|
enum: ["public", "member", "cohort", "admin"],
|
|
default: "public",
|
|
},
|
|
cohorts: [{ type: String }],
|
|
|
|
author: { type: Schema.Types.ObjectId, ref: "User", required: true },
|
|
contributors: [{ type: Schema.Types.ObjectId, ref: "User" }],
|
|
views: { type: Number, default: 0 },
|
|
likes: { type: Number, default: 0 },
|
|
|
|
status: {
|
|
type: String,
|
|
enum: ["draft", "published", "archived"],
|
|
default: "draft",
|
|
},
|
|
lockedBy: { type: Schema.Types.ObjectId, ref: "User" },
|
|
lockedAt: Date,
|
|
|
|
revisions: [revisionSchema],
|
|
comments: [commentSchema],
|
|
|
|
publishedAt: Date,
|
|
},
|
|
{
|
|
timestamps: true,
|
|
},
|
|
);
|
|
|
|
// Indexes for better query performance
|
|
articleSchema.index({ slug: 1 });
|
|
articleSchema.index({ accessLevel: 1, status: 1 });
|
|
articleSchema.index({ tags: 1 });
|
|
articleSchema.index({ category: 1 });
|
|
articleSchema.index({ author: 1 });
|
|
|
|
export const Article = model<IArticle>("Article", articleSchema);
|