import { type Document, Schema, model } from "mongoose"; import { z } from "zod"; // zod schemas for validation export const guildSchema = z.object({ guild_id: z.string(), suggestion_forum: z.string().optional(), bug_channel: z.string().optional(), commands_channel: z.string().optional(), highlights_channel: z.string().optional(), manager_roles: z.array(z.string()).default([]), }); export const userSchema = z.object({ user_id: z.string(), guild_id: z.string(), }); const CounterSchema = new Schema({ _id: String, sequence_value: { type: Number, default: 0 }, }); export const bugSchema = z.object({ bug_id: z.number(), user_id: z.string(), status: z.enum(["open", "closed"]).default("open"), game: z.enum(["wft", "gw", "ab"]), title: z.string(), description: z.string(), sent: z.boolean().default(false), message_id: z.string().optional(), thread_id: z.string().optional(), }); export const mediaSchema = z.object({ media_type: z.enum(["image", "video"]), data: z.instanceof(Buffer), user_id: z.string(), bug_id: z.string().optional(), }); // ts types export type GuildType = z.infer & Document; export type UserType = z.infer & Document; export type BugType = z.infer & Document; export type MediaType = z.infer & Document; // mongoose schemas const GuildSchema = new Schema( { guild_id: { type: String, required: true, unique: true }, suggestion_forum: String, bug_channel: String, commands_channel: String, highlights_channel: String, manager_roles: [String], }, { timestamps: true }, ); const UserSchema = new Schema( { user_id: { type: String, required: true }, guild_id: { type: String, required: true, ref: "Guild" }, }, { timestamps: true }, ); const BugSchema = new Schema( { bug_id: { type: Number, required: true, unique: true }, user_id: { type: String, required: true, ref: "User" }, status: { type: String, enum: ["open", "closed"], default: "open" }, title: { type: String, required: true }, game: { type: String, enum: ["wft", "gw", "ab"], required: true }, description: { type: String, required: true }, sent: { type: Boolean, default: false }, message_id: String, thread_id: String, }, { timestamps: true }, ); const MediaSchema = new Schema( { media_type: { type: String, enum: ["image", "video"], required: true }, data: { type: Buffer, required: true }, user_id: { type: String, required: true, ref: "User" }, bug_id: { type: String, ref: "Bug" }, }, { timestamps: true }, ); // compound indexes for better perf UserSchema.index({ user_id: 1, guild_id: 1 }, { unique: true }); // models export const GuildModel = model("Guild", GuildSchema); export const UserModel = model("User", UserSchema); export const BugModel = model("Bug", BugSchema); export const MediaModel = model("Media", MediaSchema); export const CounterModel = model("Counter", CounterSchema); // funcs export async function getNextBugId(): Promise { const counter = await CounterModel.findByIdAndUpdate( "bug_id", { $inc: { sequence_value: 1 } }, { new: true, upsert: true }, ); return counter.sequence_value; }