Improve schema readability & maintainability

pull/12/head
v4ltages 5 months ago
parent ab6992709f
commit 0f5196053f
No known key found for this signature in database
GPG Key ID: DC7BC38E0DC642B
  1. 2
      drizzle.config.ts
  2. 1
      migrations/0002_real_korath.sql
  3. 281
      migrations/meta/0002_snapshot.json
  4. 7
      migrations/meta/_journal.json
  5. 2
      src/app/layout.tsx
  6. 2
      src/db/drizzle.ts
  7. 138
      src/db/schema.ts
  8. 14
      src/db/schema/index.ts
  9. 31
      src/db/schema/members.ts
  10. 42
      src/db/schema/relations.ts
  11. 2
      src/db/schema/schema.ts
  12. 11
      src/db/schema/teams.ts
  13. 46
      src/db/schema/tournaments.ts
  14. 10
      src/db/schema/types.ts
  15. 18
      src/db/schema/users.ts
  16. 7
      src/lib/fienta.ts
  17. 4
      src/types/database.ts

@ -1,7 +1,7 @@
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
schema: "./src/db/schema/schema.ts",
out: "./migrations",
dialect: "sqlite",
dbCredentials: {

@ -0,0 +1 @@
DROP INDEX `user_steam_id_unique`;

@ -0,0 +1,281 @@
{
"version": "6",
"dialect": "sqlite",
"id": "5d600618-0105-4104-b150-c63015ae55c7",
"prevId": "eae7a237-8469-4a6a-acac-1910adfc98ff",
"tables": {
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"steam_id": {
"name": "steam_id",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"first_name": {
"name": "first_name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"last_name": {
"name": "last_name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"ticket_id": {
"name": "ticket_id",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"ticket_type": {
"name": "ticket_type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"user_ticket_id_unique": {
"name": "user_ticket_id_unique",
"columns": [
"ticket_id"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"team": {
"name": "team",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"member": {
"name": "member",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"team_id": {
"name": "team_id",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"user_team_unique": {
"name": "user_team_unique",
"columns": [
"user_id",
"team_id"
],
"isUnique": true
}
},
"foreignKeys": {
"member_user_id_user_id_fk": {
"name": "member_user_id_user_id_fk",
"tableFrom": "member",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"member_team_id_team_id_fk": {
"name": "member_team_id_team_id_fk",
"tableFrom": "member",
"tableTo": "team",
"columnsFrom": [
"team_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tournament_team": {
"name": "tournament_team",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"tournament_id": {
"name": "tournament_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"team_id": {
"name": "team_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"registration_date": {
"name": "registration_date",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"tournament_team_unique": {
"name": "tournament_team_unique",
"columns": [
"tournament_id",
"team_id"
],
"isUnique": true
}
},
"foreignKeys": {
"tournament_team_tournament_id_tournament_id_fk": {
"name": "tournament_team_tournament_id_tournament_id_fk",
"tableFrom": "tournament_team",
"tableTo": "tournament",
"columnsFrom": [
"tournament_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"tournament_team_team_id_team_id_fk": {
"name": "tournament_team_team_id_team_id_fk",
"tableFrom": "tournament_team",
"tableTo": "team",
"columnsFrom": [
"team_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tournament": {
"name": "tournament",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

@ -15,6 +15,13 @@
"when": 1752632968857,
"tag": "0001_cool_ma_gnuci",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1754400044471,
"tag": "0002_real_korath",
"breakpoints": true
}
]
}

@ -17,8 +17,6 @@ const workSans = Work_Sans({
subsets: ["latin"],
});
// Commented out for now, because it doesn't work having client components in the layout file
export const metadata: Metadata = {
title: "TipiLAN 2025",
description: "TipiLAN 2025 – Eesti suurim tudengite korraldatud LAN!",

@ -1,6 +1,6 @@
import { drizzle } from "drizzle-orm/bun-sqlite";
import { Database } from "bun:sqlite";
import * as schema from "./schema";
import * as schema from "./schema/schema";
const sqlite = new Database("data/tipilan.db");
export const db = drizzle(sqlite, { schema });

@ -1,138 +0,0 @@
import {
sqliteTable,
text,
integer,
uniqueIndex,
} from "drizzle-orm/sqlite-core";
import { relations } from "drizzle-orm";
import { createId } from "@paralleldrive/cuid2";
// Roles enum equivalent
export const RoleEnum = {
VISITOR: "VISITOR",
PARTICIPANT: "PARTICIPANT",
TEAMMATE: "TEAMMATE",
CAPTAIN: "CAPTAIN",
ADMIN: "ADMIN",
} as const;
export type Role = (typeof RoleEnum)[keyof typeof RoleEnum];
// User table
export const users = sqliteTable("user", {
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
email: text("email").notNull(),
steamId: text("steam_id").unique(),
firstName: text("first_name").notNull(),
lastName: text("last_name").notNull(),
ticketId: text("ticket_id").unique(),
ticketType: text("ticket_type"),
});
// Team table
export const teams = sqliteTable("team", {
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
name: text("name").notNull(),
});
// Member table (join table for User and Team with role)
export const members = sqliteTable(
"member",
{
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
teamId: text("team_id").references(() => teams.id, { onDelete: "cascade" }),
role: text("role", {
enum: Object.values(RoleEnum) as [string, ...string[]],
}).notNull(),
},
(table) => {
return {
userTeamUnique: uniqueIndex("user_team_unique").on(
table.userId,
table.teamId,
),
};
},
);
// Tournament table
export const tournaments = sqliteTable("tournament", {
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
name: text("name").notNull(),
});
// TournamentTeam join table
export const tournamentTeams = sqliteTable(
"tournament_team",
{
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
tournamentId: text("tournament_id")
.notNull()
.references(() => tournaments.id, { onDelete: "cascade" }),
teamId: text("team_id")
.notNull()
.references(() => teams.id, { onDelete: "cascade" }),
registrationDate: integer("registration_date", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
},
(table) => {
return {
tournamentTeamUnique: uniqueIndex("tournament_team_unique").on(
table.tournamentId,
table.teamId,
),
};
},
);
// Relations
export const usersRelations = relations(users, ({ many }) => ({
members: many(members),
}));
export const teamsRelations = relations(teams, ({ many }) => ({
members: many(members),
tournamentTeams: many(tournamentTeams),
}));
export const membersRelations = relations(members, ({ one }) => ({
user: one(users, { fields: [members.userId], references: [users.id] }),
team: one(teams, { fields: [members.teamId], references: [teams.id] }),
}));
export const tournamentsRelations = relations(tournaments, ({ many }) => ({
tournamentTeams: many(tournamentTeams),
}));
export const tournamentTeamsRelations = relations(
tournamentTeams,
({ one }) => ({
tournament: one(tournaments, {
fields: [tournamentTeams.tournamentId],
references: [tournaments.id],
}),
team: one(teams, {
fields: [tournamentTeams.teamId],
references: [teams.id],
}),
}),
);

@ -0,0 +1,14 @@
// Export all types
export * from "./types";
// Export all tables
export * from "./users";
export * from "./teams";
export * from "./members";
export * from "./tournaments";
// Export session table
// export * from ./session";
// Export all relations
export * from "./relations";

@ -0,0 +1,31 @@
import { sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
import { createId } from "@paralleldrive/cuid2";
import { users } from "./users";
import { teams } from "./teams";
import { RoleEnum } from "./types";
// Member table (join table for User and Team with role)
export const members = sqliteTable(
"member",
{
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
teamId: text("team_id").references(() => teams.id, { onDelete: "cascade" }),
role: text("role", {
enum: Object.values(RoleEnum) as [string, ...string[]],
}).notNull(),
},
(table) => {
return {
userTeamUnique: uniqueIndex("user_team_unique").on(
table.userId,
table.teamId,
),
};
},
);

@ -0,0 +1,42 @@
import { relations } from "drizzle-orm";
import { users } from "./users";
import { teams } from "./teams";
import { members } from "./members";
import { tournaments, tournamentTeams } from "./tournaments";
// User relations
export const usersRelations = relations(users, ({ many }) => ({
members: many(members),
}));
// Team relations
export const teamsRelations = relations(teams, ({ many }) => ({
members: many(members),
tournamentTeams: many(tournamentTeams),
}));
// Member relations
export const membersRelations = relations(members, ({ one }) => ({
user: one(users, { fields: [members.userId], references: [users.id] }),
team: one(teams, { fields: [members.teamId], references: [teams.id] }),
}));
// Tournament relations
export const tournamentsRelations = relations(tournaments, ({ many }) => ({
tournamentTeams: many(tournamentTeams),
}));
// Tournament team relations
export const tournamentTeamsRelations = relations(
tournamentTeams,
({ one }) => ({
tournament: one(tournaments, {
fields: [tournamentTeams.tournamentId],
references: [tournaments.id],
}),
team: one(teams, {
fields: [tournamentTeams.teamId],
references: [teams.id],
}),
}),
);

@ -0,0 +1,2 @@
// Re-export all schema components from modular files
export * from "./index";

@ -0,0 +1,11 @@
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
import { createId } from "@paralleldrive/cuid2";
// Team table
export const teams = sqliteTable("team", {
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
name: text("name").notNull(),
});

@ -0,0 +1,46 @@
import {
sqliteTable,
text,
integer,
uniqueIndex,
} from "drizzle-orm/sqlite-core";
import { createId } from "@paralleldrive/cuid2";
import { teams } from "./teams";
// Tournament table
export const tournaments = sqliteTable("tournament", {
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
name: text("name").notNull(),
});
// TournamentTeam join table
export const tournamentTeams = sqliteTable(
"tournament_team",
{
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
tournamentId: text("tournament_id")
.notNull()
.references(() => tournaments.id, { onDelete: "cascade" }),
teamId: text("team_id")
.notNull()
.references(() => teams.id, { onDelete: "cascade" }),
registrationDate: integer("registration_date", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
},
(table) => {
return {
tournamentTeamUnique: uniqueIndex("tournament_team_unique").on(
table.tournamentId,
table.teamId,
),
};
},
);

@ -0,0 +1,10 @@
// Roles enum equivalent
export const RoleEnum = {
VISITOR: "VISITOR",
PARTICIPANT: "PARTICIPANT",
TEAMMATE: "TEAMMATE",
CAPTAIN: "CAPTAIN",
ADMIN: "ADMIN",
} as const;
export type Role = (typeof RoleEnum)[keyof typeof RoleEnum];

@ -0,0 +1,18 @@
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
import { createId } from "@paralleldrive/cuid2";
// User table
export const users = sqliteTable("user", {
id: text("id")
.primaryKey()
.notNull()
.$defaultFn(() => createId()),
email: text("email").notNull(),
// Other information
steamId: text("steam_id"),
firstName: text("first_name").notNull(),
lastName: text("last_name").notNull(),
ticketId: text("ticket_id").unique(),
ticketType: text("ticket_type"),
});

@ -1,7 +1,10 @@
import { db } from "@/db/drizzle";
import { users, teams, members, tournamentTeams } from "@/db/schema";
import { users } from "@/db/schema/users";
import { teams } from "@/db/schema/teams";
import { members } from "@/db/schema/members";
import { tournamentTeams } from "@/db/schema/tournaments";
import { eq, and, isNull } from "drizzle-orm";
import { RoleEnum, type Role } from "@/db/schema";
import { RoleEnum, type Role } from "@/db/schema/types";
// Types based on the Fienta API response
export interface FientaApiResponse {

@ -1,5 +1,7 @@
import type { InferSelectModel } from "drizzle-orm";
import { users, teams, members } from "@/db/schema";
import { users } from "@/db/schema/users";
import { teams } from "@/db/schema/teams";
import { members } from "@/db/schema/members";
// Base types from schema
export type User = InferSelectModel<typeof users>;

Loading…
Cancel
Save