91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
import { sql } from "drizzle-orm";
|
|
import { pgTable, text, varchar, integer, timestamp, boolean, json } from "drizzle-orm/pg-core";
|
|
import { createInsertSchema } from "drizzle-zod";
|
|
import { z } from "zod";
|
|
|
|
export const pixels = pgTable("pixels", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
x: integer("x").notNull(),
|
|
y: integer("y").notNull(),
|
|
color: varchar("color", { length: 7 }).notNull(), // hex color
|
|
userId: varchar("user_id").notNull(),
|
|
username: varchar("username").notNull(),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
});
|
|
|
|
export const canvasConfig = pgTable("canvas_config", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
canvasWidth: integer("canvas_width").notNull().default(100),
|
|
canvasHeight: integer("canvas_height").notNull().default(100),
|
|
defaultCooldown: integer("default_cooldown").notNull().default(5), // seconds
|
|
enableAutomaticEvents: boolean("enable_automatic_events").notNull().default(false),
|
|
eventDuration: integer("event_duration").notNull().default(30), // minutes
|
|
eventInterval: integer("event_interval").notNull().default(6), // hours
|
|
|
|
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
|
});
|
|
|
|
export const userCooldowns = pgTable("user_cooldowns", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
userId: varchar("user_id").notNull(),
|
|
lastPlacement: timestamp("last_placement").defaultNow().notNull(),
|
|
cooldownEnds: timestamp("cooldown_ends").notNull(),
|
|
});
|
|
|
|
export const insertPixelSchema = createInsertSchema(pixels).omit({
|
|
id: true,
|
|
createdAt: true,
|
|
});
|
|
|
|
export const insertCanvasConfigSchema = createInsertSchema(canvasConfig).omit({
|
|
id: true,
|
|
updatedAt: true,
|
|
});
|
|
|
|
export const insertUserCooldownSchema = createInsertSchema(userCooldowns).omit({
|
|
id: true,
|
|
lastPlacement: true,
|
|
});
|
|
|
|
export type Pixel = typeof pixels.$inferSelect;
|
|
export type InsertPixel = z.infer<typeof insertPixelSchema>;
|
|
export type CanvasConfig = typeof canvasConfig.$inferSelect;
|
|
export type InsertCanvasConfig = z.infer<typeof insertCanvasConfigSchema>;
|
|
export type UserCooldown = typeof userCooldowns.$inferSelect;
|
|
export type InsertUserCooldown = z.infer<typeof insertUserCooldownSchema>;
|
|
|
|
// WebSocket message types
|
|
export const wsMessageSchema = z.union([
|
|
z.object({
|
|
type: z.literal("pixel_placed"),
|
|
data: z.object({
|
|
x: z.number(),
|
|
y: z.number(),
|
|
color: z.string(),
|
|
userId: z.string(),
|
|
username: z.string(),
|
|
timestamp: z.string(),
|
|
}),
|
|
}),
|
|
z.object({
|
|
type: z.literal("user_count"),
|
|
data: z.object({
|
|
count: z.number(),
|
|
}),
|
|
}),
|
|
|
|
z.object({
|
|
type: z.literal("cooldown_update"),
|
|
data: z.object({
|
|
userId: z.string(),
|
|
remainingSeconds: z.number(),
|
|
}),
|
|
}),
|
|
]);
|
|
|
|
export type WSMessage =
|
|
| { type: "pixel_placed"; data: { x: number; y: number; color: string; userId: string; username: string; timestamp: string } }
|
|
| { type: "user_count"; data: { count: number } }
|
|
| { type: "cooldown_update"; data: { userId: string; remainingSeconds: number } }
|
|
| { type: "pixel_deleted"; data: { pixelId: string } }
|
|
| { type: "canvas_cleared"; data: {} }; |