all repos — stealth-developers @ c8ef8710d5919aa49227dc7f3a76d059ae90b5b4

src/database/schemas.ts (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
import { type Document, Schema, model } from "mongoose";
import { z } from "zod";

import config from "../config";
const uniqueProjects = Object.keys(config.data.projects) as [
	string,
	...string[],
];

// 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"),
	project: z.enum(uniqueProjects),
	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<typeof guildSchema> & Document;
export type UserType = z.infer<typeof userSchema> & Document;
export type BugType = z.infer<typeof bugSchema> & Document;
export type MediaType = z.infer<typeof mediaSchema> & 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 },
		project: { type: String, enum: uniqueProjects, 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<GuildType>("Guild", GuildSchema);
export const UserModel = model<UserType>("User", UserSchema);
export const BugModel = model<BugType>("Bug", BugSchema);
export const MediaModel = model<MediaType>("Media", MediaSchema);
export const CounterModel = model("Counter", CounterSchema);

// funcs
export async function getNextBugId(): Promise<number> {
	const counter = await CounterModel.findByIdAndUpdate(
		"bug_id",
		{ $inc: { sequence_value: 1 } },
		{ new: true, upsert: true },
	);
	return counter.sequence_value;
}