Introduced New DB
This commit is contained in:
10
db/index.ts
Normal file
10
db/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { drizzle } from "drizzle-orm/node-postgres"
|
||||
import { Pool } from "pg"
|
||||
import {secrets} from "../src/utils/secrets";
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: secrets.DATABASE_URL,
|
||||
max: 10, // je nach Last
|
||||
})
|
||||
|
||||
export const db = drizzle(pool)
|
||||
24
db/schema/accounts.ts
Normal file
24
db/schema/accounts.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
export const accounts = pgTable("accounts", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
number: text("number").notNull(),
|
||||
label: text("label").notNull(),
|
||||
|
||||
description: text("description"),
|
||||
})
|
||||
|
||||
export type Account = typeof accounts.$inferSelect
|
||||
export type NewAccount = typeof accounts.$inferInsert
|
||||
83
db/schema/auth_profiles.ts
Normal file
83
db/schema/auth_profiles.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
text,
|
||||
timestamp,
|
||||
date,
|
||||
boolean,
|
||||
bigint,
|
||||
doublePrecision,
|
||||
jsonb,
|
||||
} from "drizzle-orm/pg-core"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const authProfiles = pgTable("auth_profiles", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
userId: uuid("user_id").references(() => authUsers.id),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" }).notNull(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
firstName: text("first_name").notNull(),
|
||||
lastName: text("last_name").notNull(),
|
||||
|
||||
fullName: text("full_name").generatedAlwaysAs(
|
||||
`((first_name || ' ') || last_name)`
|
||||
),
|
||||
|
||||
mobileTel: text("mobile_tel"),
|
||||
fixedTel: text("fixed_tel"),
|
||||
salutation: text("salutation"),
|
||||
employeeNumber: text("employee_number"),
|
||||
|
||||
weeklyWorkingHours: doublePrecision("weekly_working_hours").default(0),
|
||||
annualPaidLeaveDays: bigint("annual_paid_leave_days", { mode: "number" }),
|
||||
|
||||
weeklyRegularWorkingHours: jsonb("weekly_regular_working_hours").default("{}"),
|
||||
|
||||
clothingSizeTop: text("clothing_size_top"),
|
||||
clothingSizeBottom: text("clothing_size_bottom"),
|
||||
clothingSizeShoe: text("clothing_size_shoe"),
|
||||
|
||||
emailSignature: text("email_signature").default("<p>Mit freundlichen Grüßen</p>"),
|
||||
|
||||
birthday: date("birthday"),
|
||||
entryDate: date("entry_date").defaultNow(),
|
||||
|
||||
automaticHourCorrections: jsonb("automatic_hour_corrections").default("[]"),
|
||||
|
||||
recreationDaysCompensation: boolean("recreation_days_compensation")
|
||||
.notNull()
|
||||
.default(true),
|
||||
|
||||
customerForPortal: bigint("customer_for_portal", { mode: "number" }),
|
||||
|
||||
pinnedOnNavigation: jsonb("pinned_on_navigation").notNull().default("[]"),
|
||||
|
||||
email: text("email"),
|
||||
tokenId: text("token_id"),
|
||||
|
||||
weeklyWorkingDays: doublePrecision("weekly_working_days"),
|
||||
|
||||
oldProfileId: uuid("old_profile_id"),
|
||||
tempConfig: jsonb("temp_config"),
|
||||
|
||||
stateCode: text("state_code").default("DE-NI"),
|
||||
|
||||
contractType: text("contract_type"),
|
||||
position: text("position"),
|
||||
qualification: text("qualification"),
|
||||
|
||||
addressStreet: text("address_street"),
|
||||
addressZip: text("address_zip"),
|
||||
addressCity: text("address_city"),
|
||||
|
||||
active: boolean("active").notNull().default(true),
|
||||
})
|
||||
|
||||
export type AuthProfile = typeof authProfiles.$inferSelect
|
||||
export type NewAuthProfile = typeof authProfiles.$inferInsert
|
||||
23
db/schema/auth_role_permisssions.ts
Normal file
23
db/schema/auth_role_permisssions.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core"
|
||||
import { authRoles } from "./auth_roles"
|
||||
|
||||
export const authRolePermissions = pgTable(
|
||||
"auth_role_permissions",
|
||||
{
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
roleId: uuid("role_id")
|
||||
.notNull()
|
||||
.references(() => authRoles.id),
|
||||
|
||||
permission: text("permission").notNull(),
|
||||
},
|
||||
(table) => ({
|
||||
primaryKey: [table.roleId, table.permission],
|
||||
})
|
||||
)
|
||||
|
||||
export type AuthRolePermission = typeof authRolePermissions.$inferSelect
|
||||
export type NewAuthRolePermission = typeof authRolePermissions.$inferInsert
|
||||
19
db/schema/auth_roles.ts
Normal file
19
db/schema/auth_roles.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { pgTable, uuid, text, timestamp, bigint } from "drizzle-orm/pg-core"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const authRoles = pgTable("auth_roles", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
|
||||
createdBy: uuid("created_by").references(() => authUsers.id),
|
||||
tenantId: bigint("tenant_id", {mode: "number"}),
|
||||
})
|
||||
|
||||
export type AuthRole = typeof authRoles.$inferSelect
|
||||
export type NewAuthRole = typeof authRoles.$inferInsert
|
||||
22
db/schema/auth_tenant_users.ts
Normal file
22
db/schema/auth_tenant_users.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { pgTable, uuid, bigint, timestamp } from "drizzle-orm/pg-core"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const authTenantUsers = pgTable(
|
||||
"auth_tenant_users",
|
||||
{
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" }).notNull(),
|
||||
userId: uuid("user_id").notNull(),
|
||||
|
||||
createdBy: uuid("created_by").references(() => authUsers.id),
|
||||
},
|
||||
(table) => ({
|
||||
primaryKey: [table.tenantId, table.userId],
|
||||
})
|
||||
)
|
||||
|
||||
export type AuthTenantUser = typeof authTenantUsers.$inferSelect
|
||||
export type NewAuthTenantUser = typeof authTenantUsers.$inferInsert
|
||||
30
db/schema/auth_user_roles.ts
Normal file
30
db/schema/auth_user_roles.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { pgTable, uuid, bigint, timestamp } from "drizzle-orm/pg-core"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { authRoles } from "./auth_roles"
|
||||
|
||||
export const authUserRoles = pgTable(
|
||||
"auth_user_roles",
|
||||
{
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => authUsers.id),
|
||||
|
||||
roleId: uuid("role_id")
|
||||
.notNull()
|
||||
.references(() => authRoles.id),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" }).notNull(),
|
||||
|
||||
createdBy: uuid("created_by").references(() => authUsers.id),
|
||||
},
|
||||
(table) => ({
|
||||
primaryKey: [table.userId, table.roleId, table.tenantId],
|
||||
})
|
||||
)
|
||||
|
||||
export type AuthUserRole = typeof authUserRoles.$inferSelect
|
||||
export type NewAuthUserRole = typeof authUserRoles.$inferInsert
|
||||
22
db/schema/auth_users.ts
Normal file
22
db/schema/auth_users.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { pgTable, uuid, text, boolean, timestamp } from "drizzle-orm/pg-core"
|
||||
|
||||
export const authUsers = pgTable("auth_users", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
email: text("email").notNull(),
|
||||
passwordHash: text("password_hash").notNull(),
|
||||
|
||||
multiTenant: boolean("multi_tenant").notNull().default(true),
|
||||
mustChangePassword: boolean("must_change_password").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
|
||||
ported: boolean("ported").notNull().default(true),
|
||||
})
|
||||
|
||||
export type AuthUser = typeof authUsers.$inferSelect
|
||||
export type NewAuthUser = typeof authUsers.$inferInsert
|
||||
52
db/schema/bankaccounts.ts
Normal file
52
db/schema/bankaccounts.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
doublePrecision,
|
||||
boolean,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const bankaccounts = pgTable("bankaccounts", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name"),
|
||||
iban: text("iban").notNull(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
bankId: text("bankId").notNull(),
|
||||
ownerName: text("ownerName"),
|
||||
|
||||
accountId: text("accountId").notNull(),
|
||||
|
||||
balance: doublePrecision("balance"),
|
||||
|
||||
expired: boolean("expired").notNull().default(false),
|
||||
|
||||
datevNumber: text("datevNumber"),
|
||||
|
||||
syncedAt: timestamp("synced_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
})
|
||||
|
||||
export type BankAccount = typeof bankaccounts.$inferSelect
|
||||
export type NewBankAccount = typeof bankaccounts.$inferInsert
|
||||
30
db/schema/bankrequisitions.ts
Normal file
30
db/schema/bankrequisitions.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const bankrequisitions = pgTable("bankrequisitions", {
|
||||
id: uuid("id").primaryKey(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
institutionId: text("institutionId"),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id),
|
||||
|
||||
status: text("status"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type BankRequisition = typeof bankrequisitions.$inferSelect
|
||||
export type NewBankRequisition = typeof bankrequisitions.$inferInsert
|
||||
70
db/schema/bankstatements.ts
Normal file
70
db/schema/bankstatements.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
doublePrecision,
|
||||
boolean,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { bankaccounts } from "./bankaccounts"
|
||||
import { createddocuments } from "./createddocuments"
|
||||
import { tenants } from "./tenants"
|
||||
import { incominginvoices } from "./incominginvoices"
|
||||
import { contracts } from "./contracts"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const bankstatements = pgTable("bankstatements", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
account: bigint("account", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => bankaccounts.id),
|
||||
|
||||
date: text("date").notNull(),
|
||||
|
||||
credIban: text("credIban"),
|
||||
credName: text("credName"),
|
||||
|
||||
text: text("text"),
|
||||
amount: doublePrecision("amount").notNull(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
debIban: text("debIban"),
|
||||
debName: text("debName"),
|
||||
gocardlessId: text("gocardlessId"),
|
||||
currency: text("currency"),
|
||||
valueDate: text("valueDate"),
|
||||
|
||||
incomingInvoice: bigint("incomingInvoice", { mode: "number" }).references(
|
||||
() => incominginvoices.id
|
||||
),
|
||||
|
||||
mandateId: text("mandateId"),
|
||||
|
||||
contract: bigint("contract", { mode: "number" }).references(
|
||||
() => contracts.id
|
||||
),
|
||||
|
||||
createdDocument: bigint("createdDocument", { mode: "number" }).references(
|
||||
() => createddocuments.id
|
||||
),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type BankStatement = typeof bankstatements.$inferSelect
|
||||
export type NewBankStatement = typeof bankstatements.$inferInsert
|
||||
27
db/schema/checkexecutions.ts
Normal file
27
db/schema/checkexecutions.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { checks } from "./checks"
|
||||
|
||||
export const checkexecutions = pgTable("checkexecutions", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
check: uuid("check").references(() => checks.id),
|
||||
|
||||
executedAt: timestamp("executed_at"),
|
||||
|
||||
// ❌ executed_by removed (was 0_profiles)
|
||||
|
||||
description: text("description"),
|
||||
})
|
||||
|
||||
export type CheckExecution = typeof checkexecutions.$inferSelect
|
||||
export type NewCheckExecution = typeof checkexecutions.$inferInsert
|
||||
52
db/schema/checks.ts
Normal file
52
db/schema/checks.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
bigint,
|
||||
boolean,
|
||||
jsonb,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { vehicles } from "./vehicles"
|
||||
import { inventoryItems } from "./inventoryitems"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const checks = pgTable("checks", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
vehicle: bigint("vehicle", { mode: "number" })
|
||||
.references(() => vehicles.id),
|
||||
|
||||
// ❌ profile removed (old 0_profiles reference)
|
||||
|
||||
inventoryItem: bigint("inventoryitem", { mode: "number" })
|
||||
.references(() => inventoryItems.id),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name"),
|
||||
type: text("type"),
|
||||
|
||||
distance: bigint("distance", { mode: "number" }).default(1),
|
||||
|
||||
distanceUnit: text("distanceUnit").default("days"),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Check = typeof checks.$inferSelect
|
||||
export type NewCheck = typeof checks.$inferInsert
|
||||
32
db/schema/citys.ts
Normal file
32
db/schema/citys.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
jsonb,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
export const citys = pgTable("citys", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
name: text("name"),
|
||||
short: text("short"),
|
||||
long: text("long"),
|
||||
|
||||
geometry: jsonb("geometry"),
|
||||
|
||||
zip: bigint("zip", { mode: "number" }),
|
||||
|
||||
districtCode: bigint("districtCode", { mode: "number" }),
|
||||
|
||||
countryName: text("countryName"),
|
||||
countryCode: bigint("countryCode", { mode: "number" }),
|
||||
|
||||
districtName: text("districtName"),
|
||||
|
||||
geopoint: text("geopoint"),
|
||||
})
|
||||
|
||||
export type City = typeof citys.$inferSelect
|
||||
export type NewCity = typeof citys.$inferInsert
|
||||
66
db/schema/contacts.ts
Normal file
66
db/schema/contacts.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
date,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { customers } from "./customers"
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const contacts = pgTable(
|
||||
"contacts",
|
||||
{
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
firstName: text("firstName"),
|
||||
lastName: text("lastName"),
|
||||
email: text("email"),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).notNull(),
|
||||
|
||||
phoneMobile: text("phoneMobile"),
|
||||
phoneHome: text("phoneHome"),
|
||||
|
||||
heroId: text("heroId"),
|
||||
role: text("role"),
|
||||
|
||||
fullName: text("fullName"),
|
||||
|
||||
salutation: text("salutation"),
|
||||
|
||||
vendor: bigint("vendor", { mode: "number" }), // vendors folgt separat
|
||||
|
||||
active: boolean("active").notNull().default(true),
|
||||
|
||||
birthday: date("birthday"),
|
||||
notes: text("notes"),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
title: text("title"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
}
|
||||
)
|
||||
|
||||
export type Contact = typeof contacts.$inferSelect
|
||||
export type NewContact = typeof contacts.$inferInsert
|
||||
76
db/schema/contracts.ts
Normal file
76
db/schema/contracts.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { contacts } from "./contacts"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const contracts = pgTable(
|
||||
"contracts",
|
||||
{
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).notNull(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
customer: bigint("customer", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => customers.id),
|
||||
|
||||
notes: text("notes"),
|
||||
|
||||
active: boolean("active").notNull().default(true),
|
||||
recurring: boolean("recurring").notNull().default(false),
|
||||
|
||||
rhythm: jsonb("rhythm"),
|
||||
|
||||
startDate: timestamp("startDate", { withTimezone: true }),
|
||||
endDate: timestamp("endDate", { withTimezone: true }),
|
||||
signDate: timestamp("signDate", { withTimezone: true }),
|
||||
|
||||
duration: text("duration"),
|
||||
|
||||
contact: bigint("contact", { mode: "number" }).references(
|
||||
() => contacts.id
|
||||
),
|
||||
|
||||
bankingIban: text("bankingIban"),
|
||||
bankingBIC: text("bankingBIC"),
|
||||
bankingName: text("bankingName"),
|
||||
bankingOwner: text("bankingOwner"),
|
||||
sepaRef: text("sepaRef"),
|
||||
sepaDate: timestamp("sepaDate", { withTimezone: true }),
|
||||
|
||||
paymentType: text("paymentType"),
|
||||
invoiceDispatch: text("invoiceDispatch"),
|
||||
|
||||
ownFields: jsonb("ownFields").notNull().default({}),
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
contractNumber: text("contractNumber"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
}
|
||||
)
|
||||
|
||||
export type Contract = typeof contracts.$inferSelect
|
||||
export type NewContract = typeof contracts.$inferInsert
|
||||
50
db/schema/costcentres.ts
Normal file
50
db/schema/costcentres.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
jsonb,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { inventoryItems } from "./inventoryitems"
|
||||
import { projects } from "./projects"
|
||||
import { vehicles } from "./vehicles"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const costcentres = pgTable("costcentres", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
number: text("number").notNull(),
|
||||
name: text("name").notNull(),
|
||||
|
||||
vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id),
|
||||
|
||||
project: bigint("project", { mode: "number" }).references(() => projects.id),
|
||||
|
||||
inventoryitem: bigint("inventoryitem", { mode: "number" }).references(
|
||||
() => inventoryItems.id
|
||||
),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type CostCentre = typeof costcentres.$inferSelect
|
||||
export type NewCostCentre = typeof costcentres.$inferInsert
|
||||
21
db/schema/countrys.ts
Normal file
21
db/schema/countrys.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
export const countrys = pgTable("countrys", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
})
|
||||
|
||||
export type Country = typeof countrys.$inferSelect
|
||||
export type NewCountry = typeof countrys.$inferInsert
|
||||
121
db/schema/createddocuments.ts
Normal file
121
db/schema/createddocuments.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
boolean,
|
||||
smallint,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { contacts } from "./contacts"
|
||||
import { contracts } from "./contracts"
|
||||
import { letterheads } from "./letterheads"
|
||||
import { projects } from "./projects"
|
||||
import { plants } from "./plants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const createddocuments = pgTable("createddocuments", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
type: text("type").notNull().default("INVOICE"),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
contact: bigint("contact", { mode: "number" }).references(
|
||||
() => contacts.id
|
||||
),
|
||||
|
||||
address: jsonb("address"),
|
||||
project: bigint("project", { mode: "number" }).references(
|
||||
() => projects.id
|
||||
),
|
||||
|
||||
documentNumber: text("documentNumber"),
|
||||
documentDate: text("documentDate"),
|
||||
|
||||
state: text("state").notNull().default("Entwurf"),
|
||||
|
||||
info: jsonb("info"),
|
||||
|
||||
createdBy: uuid("createdBy").references(() => authUsers.id),
|
||||
|
||||
title: text("title"),
|
||||
description: text("description"),
|
||||
|
||||
startText: text("startText"),
|
||||
endText: text("endText"),
|
||||
|
||||
rows: jsonb("rows").default([]),
|
||||
|
||||
deliveryDateType: text("deliveryDateType"),
|
||||
paymentDays: smallint("paymentDays"),
|
||||
deliveryDate: text("deliveryDate"),
|
||||
|
||||
contactPerson: uuid("contactPerson"),
|
||||
|
||||
serialConfig: jsonb("serialConfig").default({}),
|
||||
|
||||
linkedDocument: bigint("linkedDocument", { mode: "number" }).references(
|
||||
() => createddocuments.id
|
||||
),
|
||||
|
||||
agriculture: jsonb("agriculture"),
|
||||
|
||||
letterhead: bigint("letterhead", { mode: "number" }).references(
|
||||
() => letterheads.id
|
||||
),
|
||||
|
||||
advanceInvoiceResolved: boolean("advanceInvoiceResolved")
|
||||
.notNull()
|
||||
.default(false),
|
||||
|
||||
usedAdvanceInvoices: jsonb("usedAdvanceInvoices").notNull().default([]),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
deliveryDateEnd: text("deliveryDateEnd"),
|
||||
|
||||
plant: bigint("plant", { mode: "number" }).references(() => plants.id),
|
||||
|
||||
taxType: text("taxType"),
|
||||
|
||||
customSurchargePercentage: smallint("customSurchargePercentage")
|
||||
.notNull()
|
||||
.default(0),
|
||||
|
||||
report: jsonb("report").notNull().default({}),
|
||||
|
||||
availableInPortal: boolean("availableInPortal")
|
||||
.notNull()
|
||||
.default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
created_by: uuid("created_by").references(() => authUsers.id),
|
||||
|
||||
payment_type: text("payment_type").default("transfer"),
|
||||
|
||||
contract: bigint("contract", { mode: "number" }).references(
|
||||
() => contracts.id
|
||||
),
|
||||
})
|
||||
|
||||
export type CreatedDocument = typeof createddocuments.$inferSelect
|
||||
export type NewCreatedDocument = typeof createddocuments.$inferInsert
|
||||
43
db/schema/createdletters.ts
Normal file
43
db/schema/createdletters.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
bigint,
|
||||
text,
|
||||
jsonb,
|
||||
boolean,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { vendors } from "./vendors"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const createdletters = pgTable("createdletters", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
|
||||
|
||||
contentJson: jsonb("content_json").default([]),
|
||||
|
||||
contentText: text("content_text"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
})
|
||||
|
||||
export type CreatedLetter = typeof createdletters.$inferSelect
|
||||
export type NewCreatedLetter = typeof createdletters.$inferInsert
|
||||
69
db/schema/customers.ts
Normal file
69
db/schema/customers.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
smallint,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const customers = pgTable(
|
||||
"customers",
|
||||
{
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
customerNumber: text("customerNumber").notNull(),
|
||||
name: text("name").notNull(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).notNull(),
|
||||
|
||||
infoData: jsonb("infoData").default({}),
|
||||
active: boolean("active").notNull().default(true),
|
||||
|
||||
notes: text("notes"),
|
||||
|
||||
type: text("type").default("Privat"),
|
||||
heroId: text("heroId"),
|
||||
|
||||
isCompany: boolean("isCompany").notNull().default(false),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
customPaymentDays: smallint("customPaymentDays"),
|
||||
|
||||
firstname: text("firstname"),
|
||||
lastname: text("lastname"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
customSurchargePercentage: smallint("customSurchargePercentage")
|
||||
.notNull()
|
||||
.default(0),
|
||||
|
||||
salutation: text("salutation"),
|
||||
title: text("title"),
|
||||
nameAddition: text("nameAddition"),
|
||||
|
||||
availableInPortal: boolean("availableInPortal")
|
||||
.notNull()
|
||||
.default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
customPaymentType: text("custom_payment_type"), // ENUM payment_types separat?
|
||||
}
|
||||
)
|
||||
|
||||
export type Customer = typeof customers.$inferSelect
|
||||
export type NewCustomer = typeof customers.$inferInsert
|
||||
29
db/schema/devices.ts
Normal file
29
db/schema/devices.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
|
||||
export const devices = pgTable("devices", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
type: text("type").notNull(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id),
|
||||
|
||||
password: text("password"),
|
||||
|
||||
externalId: text("externalId"),
|
||||
})
|
||||
|
||||
export type Device = typeof devices.$inferSelect
|
||||
export type NewDevice = typeof devices.$inferInsert
|
||||
28
db/schema/documentboxes.ts
Normal file
28
db/schema/documentboxes.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { pgTable, uuid, timestamp, text, boolean, bigint } from "drizzle-orm/pg-core"
|
||||
|
||||
import { spaces } from "./spaces"
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const documentboxes = pgTable("documentboxes", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
|
||||
space: bigint("space", { mode: "number" }).references(() => spaces.id),
|
||||
|
||||
key: text("key").notNull(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type DocumentBox = typeof documentboxes.$inferSelect
|
||||
export type NewDocumentBox = typeof documentboxes.$inferInsert
|
||||
97
db/schema/enums.ts
Normal file
97
db/schema/enums.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { pgEnum } from "drizzle-orm/pg-core"
|
||||
|
||||
// public.textTemplatePositions
|
||||
export const textTemplatePositionsEnum = pgEnum("texttemplatepositions", [
|
||||
"startText",
|
||||
"endText",
|
||||
])
|
||||
|
||||
// public.folderFunctions
|
||||
export const folderFunctionsEnum = pgEnum("folderfunctions", [
|
||||
"none",
|
||||
"yearSubCategory",
|
||||
"incomingInvoices",
|
||||
"invoices",
|
||||
"quotes",
|
||||
"confirmationOrders",
|
||||
"deliveryNotes",
|
||||
"vehicleData",
|
||||
"reminders",
|
||||
"taxData",
|
||||
"deposit",
|
||||
"timeEvaluations",
|
||||
])
|
||||
|
||||
// public.locked_tenant
|
||||
export const lockedTenantEnum = pgEnum("locked_tenant", [
|
||||
"maintenance_tenant",
|
||||
"maintenance",
|
||||
"general",
|
||||
"no_subscription",
|
||||
])
|
||||
|
||||
// public.credential_types
|
||||
export const credentialTypesEnum = pgEnum("credential_types", [
|
||||
"mail",
|
||||
"m365",
|
||||
])
|
||||
|
||||
// public.payment_types
|
||||
export const paymentTypesEnum = pgEnum("payment_types", [
|
||||
"transfer",
|
||||
"direct_debit",
|
||||
])
|
||||
|
||||
// public.notification_status
|
||||
export const notificationStatusEnum = pgEnum("notification_status", [
|
||||
"queued",
|
||||
"sent",
|
||||
"failed",
|
||||
"read",
|
||||
])
|
||||
|
||||
// public.notification_channel
|
||||
export const notificationChannelEnum = pgEnum("notification_channel", [
|
||||
"email",
|
||||
"inapp",
|
||||
"sms",
|
||||
"push",
|
||||
"webhook",
|
||||
])
|
||||
|
||||
// public.notification_severity
|
||||
export const notificationSeverityEnum = pgEnum("notification_severity", [
|
||||
"info",
|
||||
"success",
|
||||
"warning",
|
||||
"error",
|
||||
])
|
||||
|
||||
// public.times_state
|
||||
export const timesStateEnum = pgEnum("times_state", [
|
||||
"submitted",
|
||||
"approved",
|
||||
"draft",
|
||||
])
|
||||
|
||||
export const helpdeskStatusEnum = [
|
||||
"open",
|
||||
"in_progress",
|
||||
"waiting_for_customer",
|
||||
"answered",
|
||||
"closed",
|
||||
] as const
|
||||
|
||||
export const helpdeskPriorityEnum = [
|
||||
"low",
|
||||
"normal",
|
||||
"high",
|
||||
] as const
|
||||
|
||||
export const helpdeskDirectionEnum = [
|
||||
"incoming",
|
||||
"outgoing",
|
||||
"internal",
|
||||
"system",
|
||||
] as const
|
||||
|
||||
60
db/schema/events.ts
Normal file
60
db/schema/events.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const events = pgTable(
|
||||
"events",
|
||||
{
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).notNull(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
startDate: timestamp("startDate", { withTimezone: true }).notNull(),
|
||||
endDate: timestamp("endDate", { withTimezone: true }),
|
||||
|
||||
eventtype: text("eventtype").default("Umsetzung"),
|
||||
|
||||
project: bigint("project", { mode: "number" }), // FK follows when projects.ts exists
|
||||
|
||||
resources: jsonb("resources").default([]),
|
||||
notes: text("notes"),
|
||||
link: text("link"),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
vehicles: jsonb("vehicles").notNull().default([]),
|
||||
inventoryitems: jsonb("inventoryitems").notNull().default([]),
|
||||
inventoryitemgroups: jsonb("inventoryitemgroups").notNull().default([]),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
vendor: bigint("vendor", { mode: "number" }), // will link once vendors.ts is created
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
}
|
||||
)
|
||||
|
||||
export type Event = typeof events.$inferSelect
|
||||
export type NewEvent = typeof events.$inferInsert
|
||||
79
db/schema/files.ts
Normal file
79
db/schema/files.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { projects } from "./projects"
|
||||
import { customers } from "./customers"
|
||||
import { contracts } from "./contracts"
|
||||
import { vendors } from "./vendors"
|
||||
import { incominginvoices } from "./incominginvoices"
|
||||
import { plants } from "./plants"
|
||||
import { createddocuments } from "./createddocuments"
|
||||
import { vehicles } from "./vehicles"
|
||||
import { products } from "./products"
|
||||
import { inventoryItems } from "./inventoryitems"
|
||||
import { folders } from "./folders"
|
||||
import { filetags } from "./filetags"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { authProfiles } from "./auth_profiles"
|
||||
import { spaces } from "./spaces"
|
||||
import { documentboxes } from "./documentboxes"
|
||||
import { checks } from "./checks"
|
||||
|
||||
export const files = pgTable("files", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
path: text("path"),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
project: bigint("project", { mode: "number" }).references(() => projects.id),
|
||||
customer: bigint("customer", { mode: "number" }).references(() => customers.id),
|
||||
contract: bigint("contract", { mode: "number" }).references(() => contracts.id),
|
||||
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
|
||||
incominginvoice: bigint("incominginvoice", { mode: "number" }).references(() => incominginvoices.id),
|
||||
plant: bigint("plant", { mode: "number" }).references(() => plants.id),
|
||||
createddocument: bigint("createddocument", { mode: "number" }).references(() => createddocuments.id),
|
||||
vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id),
|
||||
product: bigint("product", { mode: "number" }).references(() => products.id),
|
||||
|
||||
check: uuid("check").references(() => checks.id),
|
||||
|
||||
inventoryitem: bigint("inventoryitem", { mode: "number" }).references(() => inventoryItems.id),
|
||||
|
||||
folder: uuid("folder").references(() => folders.id),
|
||||
|
||||
mimeType: text("mimeType"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
space: bigint("space", { mode: "number" }).references(() => spaces.id),
|
||||
|
||||
type: uuid("type").references(() => filetags.id),
|
||||
|
||||
documentbox: uuid("documentbox").references(() => documentboxes.id),
|
||||
|
||||
name: text("name"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
createdBy: uuid("created_by").references(() => authUsers.id),
|
||||
|
||||
authProfile: uuid("auth_profile").references(() => authProfiles.id),
|
||||
})
|
||||
|
||||
export type File = typeof files.$inferSelect
|
||||
export type NewFile = typeof files.$inferInsert
|
||||
33
db/schema/filetags.ts
Normal file
33
db/schema/filetags.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
|
||||
export const filetags = pgTable("filetags", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
color: text("color"),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
createdDocumentType: text("createddocumenttype").default(""),
|
||||
incomingDocumentType: text("incomingDocumentType"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
})
|
||||
|
||||
export type FileTag = typeof filetags.$inferSelect
|
||||
export type NewFileTag = typeof filetags.$inferInsert
|
||||
51
db/schema/folders.ts
Normal file
51
db/schema/folders.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
integer,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { filetags } from "./filetags"
|
||||
import { folderFunctionsEnum } from "./enums"
|
||||
|
||||
export const folders = pgTable("folders", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
icon: text("icon"),
|
||||
|
||||
parent: uuid("parent").references(() => folders.id),
|
||||
|
||||
isSystemUsed: boolean("isSystemUsed").notNull().default(false),
|
||||
|
||||
function: folderFunctionsEnum("function"),
|
||||
|
||||
year: integer("year"),
|
||||
|
||||
standardFiletype: uuid("standardFiletype").references(() => filetags.id),
|
||||
|
||||
standardFiletypeIsOptional: boolean("standardFiletypeIsOptional")
|
||||
.notNull()
|
||||
.default(true),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
})
|
||||
|
||||
export type Folder = typeof folders.$inferSelect
|
||||
export type NewFolder = typeof folders.$inferInsert
|
||||
35
db/schema/generatedexports.ts
Normal file
35
db/schema/generatedexports.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
|
||||
export const generatedexports = pgTable("exports", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
startDate: timestamp("start_date", { withTimezone: true }).notNull(),
|
||||
endDate: timestamp("end_date", { withTimezone: true }).notNull(),
|
||||
|
||||
validUntil: timestamp("valid_until", { withTimezone: true }),
|
||||
|
||||
type: text("type").notNull().default("datev"),
|
||||
|
||||
url: text("url").notNull(),
|
||||
filePath: text("file_path"),
|
||||
})
|
||||
|
||||
export type Export = typeof generatedexports.$inferSelect
|
||||
export type NewExport = typeof generatedexports.$inferInsert
|
||||
22
db/schema/globalmessages.ts
Normal file
22
db/schema/globalmessages.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
export const globalmessages = pgTable("globalmessages", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
title: text("title"),
|
||||
description: text("description"),
|
||||
})
|
||||
|
||||
export type GlobalMessage = typeof globalmessages.$inferSelect
|
||||
export type NewGlobalMessage = typeof globalmessages.$inferInsert
|
||||
17
db/schema/globalmessagesseen.ts
Normal file
17
db/schema/globalmessagesseen.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
pgTable,
|
||||
timestamp,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { globalmessages } from "./globalmessages"
|
||||
|
||||
export const globalmessagesseen = pgTable("globalmessagesseen", {
|
||||
message: bigint("message", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => globalmessages.id),
|
||||
|
||||
seenAt: timestamp("seen_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
})
|
||||
44
db/schema/helpdesk_channel_instances.ts
Normal file
44
db/schema/helpdesk_channel_instances.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
jsonb,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { helpdesk_channel_types } from "./helpdesk_channel_types"
|
||||
|
||||
export const helpdesk_channel_instances = pgTable("helpdesk_channel_instances", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: "cascade" }),
|
||||
|
||||
typeId: text("type_id")
|
||||
.notNull()
|
||||
.references(() => helpdesk_channel_types.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
isActive: boolean("is_active").notNull().default(true),
|
||||
|
||||
config: jsonb("config").notNull(),
|
||||
publicConfig: jsonb("public_config").notNull().default({}),
|
||||
|
||||
publicToken: text("public_token").unique(),
|
||||
secretToken: text("secret_token"),
|
||||
|
||||
createdBy: uuid("created_by").references(() => authUsers.id),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
})
|
||||
|
||||
export type HelpdeskChannelInstance =
|
||||
typeof helpdesk_channel_instances.$inferSelect
|
||||
export type NewHelpdeskChannelInstance =
|
||||
typeof helpdesk_channel_instances.$inferInsert
|
||||
9
db/schema/helpdesk_channel_types.ts
Normal file
9
db/schema/helpdesk_channel_types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { pgTable, text } from "drizzle-orm/pg-core"
|
||||
|
||||
export const helpdesk_channel_types = pgTable("helpdesk_channel_types", {
|
||||
id: text("id").primaryKey(),
|
||||
description: text("description").notNull(),
|
||||
})
|
||||
|
||||
export type HelpdeskChannelType = typeof helpdesk_channel_types.$inferSelect
|
||||
export type NewHelpdeskChannelType = typeof helpdesk_channel_types.$inferInsert
|
||||
45
db/schema/helpdesk_contacts.ts
Normal file
45
db/schema/helpdesk_contacts.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { contacts } from "./contacts"
|
||||
import { helpdesk_channel_instances } from "./helpdesk_channel_instances" // placeholder
|
||||
|
||||
export const helpdesk_contacts = pgTable("helpdesk_contacts", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: "cascade" }),
|
||||
|
||||
customerId: bigint("customer_id", { mode: "number" })
|
||||
.references(() => customers.id, { onDelete: "set null" }),
|
||||
|
||||
email: text("email"),
|
||||
phone: text("phone"),
|
||||
|
||||
externalRef: jsonb("external_ref"),
|
||||
displayName: text("display_name"),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
|
||||
sourceChannelId: uuid("source_channel_id").references(
|
||||
() => helpdesk_channel_instances.id,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
|
||||
contactId: bigint("contact_id", { mode: "number" }).references(
|
||||
() => contacts.id,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
})
|
||||
|
||||
export type HelpdeskContact = typeof helpdesk_contacts.$inferSelect
|
||||
export type NewHelpdeskContact = typeof helpdesk_contacts.$inferInsert
|
||||
34
db/schema/helpdesk_conversation_participants.ts
Normal file
34
db/schema/helpdesk_conversation_participants.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { helpdesk_conversations } from "./helpdesk_conversations"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const helpdesk_conversation_participants = pgTable(
|
||||
"helpdesk_conversation_participants",
|
||||
{
|
||||
conversationId: uuid("conversation_id")
|
||||
.notNull()
|
||||
.references(() => helpdesk_conversations.id, { onDelete: "cascade" }),
|
||||
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => authUsers.id, { onDelete: "cascade" }),
|
||||
|
||||
role: text("role"),
|
||||
},
|
||||
(table) => ({
|
||||
pk: {
|
||||
name: "helpdesk_conversation_participants_pkey",
|
||||
columns: [table.conversationId, table.userId],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
export type HelpdeskConversationParticipant =
|
||||
typeof helpdesk_conversation_participants.$inferSelect
|
||||
export type NewHelpdeskConversationParticipant =
|
||||
typeof helpdesk_conversation_participants.$inferInsert
|
||||
59
db/schema/helpdesk_conversations.ts
Normal file
59
db/schema/helpdesk_conversations.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { helpdesk_contacts } from "./helpdesk_contacts"
|
||||
import { contacts } from "./contacts"
|
||||
import { customers } from "./customers"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { helpdesk_channel_instances } from "./helpdesk_channel_instances"
|
||||
|
||||
export const helpdesk_conversations = pgTable("helpdesk_conversations", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: "cascade" }),
|
||||
|
||||
channelInstanceId: uuid("channel_instance_id")
|
||||
.notNull()
|
||||
.references(() => helpdesk_channel_instances.id, { onDelete: "cascade" }),
|
||||
|
||||
contactId: uuid("contact_id").references(() => helpdesk_contacts.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
|
||||
subject: text("subject"),
|
||||
|
||||
status: text("status").notNull().default("open"),
|
||||
|
||||
priority: text("priority").default("normal"),
|
||||
|
||||
assigneeUserId: uuid("assignee_user_id").references(() => authUsers.id),
|
||||
|
||||
lastMessageAt: timestamp("last_message_at", { withTimezone: true }),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
|
||||
customerId: bigint("customer_id", { mode: "number" }).references(
|
||||
() => customers.id,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
|
||||
contactPersonId: bigint("contact_person_id", { mode: "number" }).references(
|
||||
() => contacts.id,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
|
||||
ticketNumber: text("ticket_number"),
|
||||
})
|
||||
|
||||
export type HelpdeskConversation =
|
||||
typeof helpdesk_conversations.$inferSelect
|
||||
export type NewHelpdeskConversation =
|
||||
typeof helpdesk_conversations.$inferInsert
|
||||
46
db/schema/helpdesk_messages.ts
Normal file
46
db/schema/helpdesk_messages.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { helpdesk_contacts } from "./helpdesk_contacts"
|
||||
import { helpdesk_conversations } from "./helpdesk_conversations"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const helpdesk_messages = pgTable("helpdesk_messages", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: "cascade" }),
|
||||
|
||||
conversationId: uuid("conversation_id")
|
||||
.notNull()
|
||||
.references(() => helpdesk_conversations.id, { onDelete: "cascade" }),
|
||||
|
||||
direction: text("direction").notNull(),
|
||||
|
||||
authorUserId: uuid("author_user_id").references(() => authUsers.id),
|
||||
|
||||
payload: jsonb("payload").notNull(),
|
||||
|
||||
rawMeta: jsonb("raw_meta"),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
|
||||
contactId: uuid("contact_id").references(() => helpdesk_contacts.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
|
||||
externalMessageId: text("external_message_id").unique(),
|
||||
|
||||
receivedAt: timestamp("received_at", { withTimezone: true }).defaultNow(),
|
||||
})
|
||||
|
||||
export type HelpdeskMessage = typeof helpdesk_messages.$inferSelect
|
||||
export type NewHelpdeskMessage = typeof helpdesk_messages.$inferInsert
|
||||
33
db/schema/helpdesk_routing_rules.ts
Normal file
33
db/schema/helpdesk_routing_rules.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const helpdesk_routing_rules = pgTable("helpdesk_routing_rules", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: "cascade" }),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
condition: jsonb("condition").notNull(),
|
||||
action: jsonb("action").notNull(),
|
||||
|
||||
createdBy: uuid("created_by").references(() => authUsers.id),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
})
|
||||
|
||||
export type HelpdeskRoutingRule =
|
||||
typeof helpdesk_routing_rules.$inferSelect
|
||||
export type NewHelpdeskRoutingRule =
|
||||
typeof helpdesk_routing_rules.$inferInsert
|
||||
140
db/schema/historyitems.ts
Normal file
140
db/schema/historyitems.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { vendors } from "./vendors"
|
||||
import { projects } from "./projects"
|
||||
import { plants } from "./plants"
|
||||
import { incominginvoices } from "./incominginvoices"
|
||||
import { contacts } from "./contacts"
|
||||
import { inventoryItems } from "./inventoryitems"
|
||||
import { products } from "./products"
|
||||
import { tasks } from "./tasks"
|
||||
import { vehicles } from "./vehicles"
|
||||
import { bankstatements } from "./bankstatements"
|
||||
import { spaces } from "./spaces"
|
||||
import { costcentres } from "./costcentres"
|
||||
import { ownaccounts } from "./ownaccounts"
|
||||
import { createddocuments } from "./createddocuments"
|
||||
import { documentboxes } from "./documentboxes"
|
||||
import { hourrates } from "./hourrates"
|
||||
import { projecttypes } from "./projecttypes"
|
||||
import { checks } from "./checks"
|
||||
import { services } from "./services"
|
||||
import { events } from "./events"
|
||||
import { inventoryitemgroups } from "./inventoryitemgroups"
|
||||
import { authUsers } from "./auth_users"
|
||||
import {files} from "./files";
|
||||
|
||||
export const historyitems = pgTable("historyitems", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
text: text("text").notNull(),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
|
||||
|
||||
project: bigint("project", { mode: "number" }).references(
|
||||
() => projects.id,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
|
||||
plant: bigint("plant", { mode: "number" }).references(
|
||||
() => plants.id,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
|
||||
incomingInvoice: bigint("incomingInvoice", { mode: "number" }).references(
|
||||
() => incominginvoices.id,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
|
||||
contact: bigint("contact", { mode: "number" }).references(() => contacts.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
|
||||
inventoryitem: bigint("inventoryitem", { mode: "number" }).references(
|
||||
() => inventoryItems.id,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
|
||||
product: bigint("product", { mode: "number" }).references(
|
||||
() => products.id,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
|
||||
event: bigint("event", { mode: "number" }).references(() => events.id),
|
||||
|
||||
newVal: text("newVal"),
|
||||
oldVal: text("oldVal"),
|
||||
|
||||
task: bigint("task", { mode: "number" }).references(() => tasks.id),
|
||||
|
||||
vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id),
|
||||
|
||||
bankstatement: bigint("bankstatement", { mode: "number" }).references(
|
||||
() => bankstatements.id
|
||||
),
|
||||
|
||||
space: bigint("space", { mode: "number" }).references(() => spaces.id),
|
||||
|
||||
config: jsonb("config"),
|
||||
|
||||
projecttype: bigint("projecttype", { mode: "number" }).references(
|
||||
() => projecttypes.id
|
||||
),
|
||||
|
||||
check: uuid("check").references(() => checks.id),
|
||||
|
||||
service: bigint("service", { mode: "number" }).references(
|
||||
() => services.id
|
||||
),
|
||||
|
||||
createddocument: bigint("createddocument", { mode: "number" }).references(
|
||||
() => createddocuments.id
|
||||
),
|
||||
|
||||
file: uuid("file").references(() => files.id),
|
||||
|
||||
inventoryitemgroup: uuid("inventoryitemgroup").references(
|
||||
() => inventoryitemgroups.id
|
||||
),
|
||||
|
||||
source: text("source").default("Software"),
|
||||
|
||||
costcentre: uuid("costcentre").references(() => costcentres.id),
|
||||
|
||||
ownaccount: uuid("ownaccount").references(() => ownaccounts.id),
|
||||
|
||||
documentbox: uuid("documentbox").references(() => documentboxes.id),
|
||||
|
||||
hourrate: uuid("hourrate").references(() => hourrates.id),
|
||||
|
||||
createdBy: uuid("created_by").references(() => authUsers.id),
|
||||
|
||||
action: text("action"),
|
||||
})
|
||||
|
||||
export type HistoryItem = typeof historyitems.$inferSelect
|
||||
export type NewHistoryItem = typeof historyitems.$inferInsert
|
||||
18
db/schema/holidays.ts
Normal file
18
db/schema/holidays.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { pgTable, bigint, date, text, timestamp } from "drizzle-orm/pg-core"
|
||||
|
||||
export const holidays = pgTable("holidays", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedAlwaysAsIdentity(),
|
||||
|
||||
date: date("date").notNull(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
stateCode: text("state_code").notNull(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
})
|
||||
|
||||
export type Holiday = typeof holidays.$inferSelect
|
||||
export type NewHoliday = typeof holidays.$inferInsert
|
||||
27
db/schema/hourrates.ts
Normal file
27
db/schema/hourrates.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { pgTable, uuid, timestamp, text, boolean, bigint, doublePrecision } from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const hourrates = pgTable("hourrates", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
purchasePrice: doublePrecision("purchasePrice").notNull(),
|
||||
sellingPrice: doublePrecision("sellingPrice").notNull(),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type HourRate = typeof hourrates.$inferSelect
|
||||
export type NewHourRate = typeof hourrates.$inferInsert
|
||||
63
db/schema/incominginvoices.ts
Normal file
63
db/schema/incominginvoices.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { vendors } from "./vendors"
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const incominginvoices = pgTable("incominginvoices", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
state: text("state").notNull().default("Entwurf"),
|
||||
|
||||
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
|
||||
|
||||
reference: text("reference"),
|
||||
date: text("date"),
|
||||
|
||||
document: bigint("document", { mode: "number" }),
|
||||
|
||||
dueDate: text("dueDate"),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
paymentType: text("paymentType"),
|
||||
|
||||
accounts: jsonb("accounts").notNull().default([
|
||||
{
|
||||
account: null,
|
||||
taxType: null,
|
||||
amountNet: null,
|
||||
amountTax: 19,
|
||||
costCentre: null,
|
||||
},
|
||||
]),
|
||||
|
||||
paid: boolean("paid").notNull().default(false),
|
||||
expense: boolean("expense").notNull().default(true),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
})
|
||||
|
||||
export type IncomingInvoice = typeof incominginvoices.$inferSelect
|
||||
export type NewIncomingInvoice = typeof incominginvoices.$inferInsert
|
||||
70
db/schema/index.ts
Normal file
70
db/schema/index.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
export * from "./accounts"
|
||||
export * from "./auth_profiles"
|
||||
export * from "./auth_role_permisssions"
|
||||
export * from "./auth_roles"
|
||||
export * from "./auth_tenant_users"
|
||||
export * from "./auth_user_roles"
|
||||
export * from "./auth_users"
|
||||
export * from "./bankaccounts"
|
||||
export * from "./bankrequisitions"
|
||||
export * from "./bankstatements"
|
||||
export * from "./checkexecutions"
|
||||
export * from "./checks"
|
||||
export * from "./citys"
|
||||
export * from "./contacts"
|
||||
export * from "./contracts"
|
||||
export * from "./costcentres"
|
||||
export * from "./countrys"
|
||||
export * from "./createddocuments"
|
||||
export * from "./createdletters"
|
||||
export * from "./customers"
|
||||
export * from "./devices"
|
||||
export * from "./documentboxes"
|
||||
export * from "./enums"
|
||||
export * from "./events"
|
||||
export * from "./files"
|
||||
export * from "./filetags"
|
||||
export * from "./folders"
|
||||
export * from "./generatedexports"
|
||||
export * from "./globalmessages"
|
||||
export * from "./globalmessagesseen"
|
||||
export * from "./helpdesk_channel_instances"
|
||||
export * from "./helpdesk_channel_types"
|
||||
export * from "./helpdesk_contacts"
|
||||
export * from "./helpdesk_conversation_participants"
|
||||
export * from "./helpdesk_conversations"
|
||||
export * from "./helpdesk_messages"
|
||||
export * from "./helpdesk_routing_rules"
|
||||
export * from "./historyitems"
|
||||
export * from "./holidays"
|
||||
export * from "./hourrates"
|
||||
export * from "./incominginvoices"
|
||||
export * from "./inventoryitemgroups"
|
||||
export * from "./inventoryitems"
|
||||
export * from "./letterheads"
|
||||
export * from "./movements"
|
||||
export * from "./notifications_event_types"
|
||||
export * from "./notifications_items"
|
||||
export * from "./notifications_preferences"
|
||||
export * from "./notifications_preferences_defaults"
|
||||
export * from "./ownaccounts"
|
||||
export * from "./plants"
|
||||
export * from "./productcategories"
|
||||
export * from "./products"
|
||||
export * from "./projects"
|
||||
export * from "./projecttypes"
|
||||
export * from "./servicecategories"
|
||||
export * from "./services"
|
||||
export * from "./spaces"
|
||||
export * from "./staff_time_entries"
|
||||
export * from "./staff_time_entry_connects"
|
||||
export * from "./staff_zeitstromtimestamps"
|
||||
export * from "./statementallocations"
|
||||
export * from "./tasks"
|
||||
export * from "./taxtypes"
|
||||
export * from "./tenants"
|
||||
export * from "./texttemplates"
|
||||
export * from "./units"
|
||||
export * from "./user_credentials"
|
||||
export * from "./vehicles"
|
||||
export * from "./vendors"
|
||||
39
db/schema/inventoryitemgroups.ts
Normal file
39
db/schema/inventoryitemgroups.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
jsonb, bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const inventoryitemgroups = pgTable("inventoryitemgroups", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" }).notNull().references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
inventoryitems: jsonb("inventoryitems").notNull().default([]),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
usePlanning: boolean("usePlanning").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type InventoryItemGroup = typeof inventoryitemgroups.$inferSelect
|
||||
export type NewInventoryItemGroup = typeof inventoryitemgroups.$inferInsert
|
||||
68
db/schema/inventoryitems.ts
Normal file
68
db/schema/inventoryitems.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
doublePrecision,
|
||||
uuid,
|
||||
jsonb,
|
||||
date,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { vendors } from "./vendors"
|
||||
import { spaces } from "./spaces"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const inventoryItems = pgTable("inventoryitems", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
usePlanning: boolean("usePlanning").notNull().default(false),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
currentSpace: bigint("currentSpace", { mode: "number" }).references(
|
||||
() => spaces.id
|
||||
),
|
||||
|
||||
articleNumber: text("articleNumber"),
|
||||
serialNumber: text("serialNumber"),
|
||||
|
||||
purchaseDate: date("purchaseDate"),
|
||||
|
||||
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
|
||||
|
||||
quantity: bigint("quantity", { mode: "number" }).notNull().default(0),
|
||||
|
||||
purchasePrice: doublePrecision("purchasePrice").default(0),
|
||||
|
||||
manufacturer: text("manufacturer"),
|
||||
manufacturerNumber: text("manufacturerNumber"),
|
||||
|
||||
currentValue: doublePrecision("currentValue"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() =>
|
||||
authUsers.id
|
||||
),
|
||||
})
|
||||
|
||||
export type InventoryItem = typeof inventoryItems.$inferSelect
|
||||
export type NewInventoryItem = typeof inventoryItems.$inferInsert
|
||||
39
db/schema/letterheads.ts
Normal file
39
db/schema/letterheads.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const letterheads = pgTable("letterheads", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").default("Standard"),
|
||||
|
||||
path: text("path").notNull(),
|
||||
|
||||
documentTypes: text("documentTypes").array().notNull().default([]),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
})
|
||||
|
||||
export type Letterhead = typeof letterheads.$inferSelect
|
||||
export type NewLetterhead = typeof letterheads.$inferInsert
|
||||
49
db/schema/movements.ts
Normal file
49
db/schema/movements.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { products } from "./products"
|
||||
import { spaces } from "./spaces"
|
||||
import { tenants } from "./tenants"
|
||||
import { projects } from "./projects"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const movements = pgTable("movements", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
quantity: bigint("quantity", { mode: "number" }).notNull(),
|
||||
|
||||
productId: bigint("productId", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => products.id),
|
||||
|
||||
spaceId: bigint("spaceId", { mode: "number" }).references(() => spaces.id),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
projectId: bigint("projectId", { mode: "number" }).references(
|
||||
() => projects.id
|
||||
),
|
||||
|
||||
notes: text("notes"),
|
||||
|
||||
serials: text("serials").array(),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Movement = typeof movements.$inferSelect
|
||||
export type NewMovement = typeof movements.$inferInsert
|
||||
34
db/schema/notifications_event_types.ts
Normal file
34
db/schema/notifications_event_types.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
pgTable,
|
||||
text,
|
||||
jsonb,
|
||||
boolean,
|
||||
timestamp,
|
||||
} from "drizzle-orm/pg-core"
|
||||
import {notificationSeverityEnum} from "./enums";
|
||||
|
||||
|
||||
export const notificationsEventTypes = pgTable("notifications_event_types", {
|
||||
eventKey: text("event_key").primaryKey(),
|
||||
|
||||
displayName: text("display_name").notNull(),
|
||||
description: text("description"),
|
||||
category: text("category"),
|
||||
|
||||
severity: notificationSeverityEnum("severity").notNull().default("info"),
|
||||
|
||||
allowedChannels: jsonb("allowed_channels").notNull().default(["inapp", "email"]),
|
||||
|
||||
payloadSchema: jsonb("payload_schema"),
|
||||
|
||||
isActive: boolean("is_active").notNull().default(true),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
})
|
||||
|
||||
export type NotificationsEventType =
|
||||
typeof notificationsEventTypes.$inferSelect
|
||||
export type NewNotificationsEventType =
|
||||
typeof notificationsEventTypes.$inferInsert
|
||||
54
db/schema/notifications_items.ts
Normal file
54
db/schema/notifications_items.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
bigint,
|
||||
text,
|
||||
jsonb,
|
||||
timestamp,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { notificationsEventTypes } from "./notifications_event_types"
|
||||
import {notificationChannelEnum, notificationStatusEnum} from "./enums";
|
||||
|
||||
|
||||
export const notificationsItems = pgTable("notifications_items", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, { onDelete: "cascade", onUpdate: "cascade" }),
|
||||
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => authUsers.id, { onDelete: "cascade", onUpdate: "cascade" }),
|
||||
|
||||
eventType: text("event_type")
|
||||
.notNull()
|
||||
.references(() => notificationsEventTypes.eventKey, {
|
||||
onUpdate: "cascade",
|
||||
onDelete: "restrict",
|
||||
}),
|
||||
|
||||
title: text("title").notNull(),
|
||||
message: text("message").notNull(),
|
||||
|
||||
payload: jsonb("payload"),
|
||||
|
||||
channel: notificationChannelEnum("channel").notNull(),
|
||||
|
||||
status: notificationStatusEnum("status").notNull().default("queued"),
|
||||
|
||||
error: text("error"),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
sentAt: timestamp("sent_at", { withTimezone: true }),
|
||||
readAt: timestamp("read_at", { withTimezone: true }),
|
||||
})
|
||||
|
||||
export type NotificationItem = typeof notificationsItems.$inferSelect
|
||||
export type NewNotificationItem = typeof notificationsItems.$inferInsert
|
||||
60
db/schema/notifications_preferences.ts
Normal file
60
db/schema/notifications_preferences.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
bigint,
|
||||
text,
|
||||
boolean,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { notificationsEventTypes } from "./notifications_event_types"
|
||||
import {notificationChannelEnum} from "./enums";
|
||||
|
||||
export const notificationsPreferences = pgTable(
|
||||
"notifications_preferences",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => authUsers.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
||||
eventType: text("event_type")
|
||||
.notNull()
|
||||
.references(() => notificationsEventTypes.eventKey, {
|
||||
onDelete: "restrict",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
||||
channel: notificationChannelEnum("channel").notNull(),
|
||||
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
uniquePrefs: uniqueIndex(
|
||||
"notifications_preferences_tenant_id_user_id_event_type_chan_key",
|
||||
).on(table.tenantId, table.userId, table.eventType, table.channel),
|
||||
}),
|
||||
)
|
||||
|
||||
export type NotificationPreference =
|
||||
typeof notificationsPreferences.$inferSelect
|
||||
export type NewNotificationPreference =
|
||||
typeof notificationsPreferences.$inferInsert
|
||||
52
db/schema/notifications_preferences_defaults.ts
Normal file
52
db/schema/notifications_preferences_defaults.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
bigint,
|
||||
text,
|
||||
boolean,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { notificationsEventTypes } from "./notifications_event_types"
|
||||
import {notificationChannelEnum} from "./enums";
|
||||
|
||||
export const notificationsPreferencesDefaults = pgTable(
|
||||
"notifications_preferences_defaults",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
||||
eventKey: text("event_key")
|
||||
.notNull()
|
||||
.references(() => notificationsEventTypes.eventKey, {
|
||||
onDelete: "restrict",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
||||
channel: notificationChannelEnum("channel").notNull(),
|
||||
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
uniqueDefaults: uniqueIndex(
|
||||
"notifications_preferences_defau_tenant_id_event_key_channel_key",
|
||||
).on(table.tenantId, table.eventKey, table.channel),
|
||||
}),
|
||||
)
|
||||
|
||||
export type NotificationPreferenceDefault =
|
||||
typeof notificationsPreferencesDefaults.$inferSelect
|
||||
export type NewNotificationPreferenceDefault =
|
||||
typeof notificationsPreferencesDefaults.$inferInsert
|
||||
39
db/schema/ownaccounts.ts
Normal file
39
db/schema/ownaccounts.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
jsonb,
|
||||
bigint,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const ownaccounts = pgTable("ownaccounts", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
number: text("number").notNull(),
|
||||
name: text("name").notNull(),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type OwnAccount = typeof ownaccounts.$inferSelect
|
||||
export type NewOwnAccount = typeof ownaccounts.$inferInsert
|
||||
56
db/schema/plants.ts
Normal file
56
db/schema/plants.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
boolean,
|
||||
uuid,
|
||||
date,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { contracts } from "./contracts"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const plants = pgTable("plants", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
infoData: jsonb("infoData"),
|
||||
contract: bigint("contract", { mode: "number" }).references(
|
||||
() => contracts.id
|
||||
),
|
||||
|
||||
description: jsonb("description").default({
|
||||
html: "",
|
||||
json: [],
|
||||
text: "",
|
||||
}),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Plant = typeof plants.$inferSelect
|
||||
export type NewPlant = typeof plants.$inferInsert
|
||||
37
db/schema/productcategories.ts
Normal file
37
db/schema/productcategories.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const productCategories = pgTable("productcategories", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type ProductCategory = typeof productCategories.$inferSelect
|
||||
export type NewProductCategory = typeof productCategories.$inferInsert
|
||||
69
db/schema/products.ts
Normal file
69
db/schema/products.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
doublePrecision,
|
||||
boolean,
|
||||
smallint,
|
||||
uuid,
|
||||
jsonb,
|
||||
json,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { units } from "./units"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const products = pgTable("products", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
manufacturer: text("manufacturer"),
|
||||
|
||||
unit: bigint("unit", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => units.id),
|
||||
|
||||
tags: json("tags").notNull().default([]),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
ean: text("ean"),
|
||||
barcode: text("barcode"),
|
||||
|
||||
purchasePrice: doublePrecision("purchasePrice"),
|
||||
sellingPrice: doublePrecision("sellingPrice"),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
manufacturerNumber: text("manufacturerNumber"),
|
||||
|
||||
vendorAllocation: jsonb("vendorAllocation").default([]),
|
||||
|
||||
articleNumber: text("articleNumber"),
|
||||
|
||||
barcodes: text("barcodes").array().notNull().default([]),
|
||||
|
||||
productcategories: jsonb("productcategories").default([]),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
taxPercentage: smallint("taxPercentage").notNull().default(19),
|
||||
|
||||
markupPercentage: doublePrecision("markupPercentage"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Product = typeof products.$inferSelect
|
||||
export type NewProduct = typeof products.$inferInsert
|
||||
78
db/schema/projects.ts
Normal file
78
db/schema/projects.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
json,
|
||||
boolean,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { customers } from "./customers"
|
||||
import { contracts } from "./contracts"
|
||||
import { projecttypes } from "./projecttypes"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const projects = pgTable("projects", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
notes: text("notes"),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
phases: jsonb("phases").default([]),
|
||||
|
||||
description: json("description"),
|
||||
|
||||
forms: jsonb("forms").default([]),
|
||||
|
||||
heroId: text("heroId"),
|
||||
|
||||
measure: text("measure"),
|
||||
|
||||
material: jsonb("material"),
|
||||
|
||||
plant: bigint("plant", { mode: "number" }),
|
||||
|
||||
profiles: uuid("profiles").array().notNull().default([]),
|
||||
|
||||
projectNumber: text("projectNumber"),
|
||||
|
||||
contract: bigint("contract", { mode: "number" }).references(
|
||||
() => contracts.id
|
||||
),
|
||||
|
||||
projectType: text("projectType").default("Projekt"),
|
||||
|
||||
projecttype: bigint("projecttype", { mode: "number" }).references(
|
||||
() => projecttypes.id
|
||||
),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
customerRef: text("customerRef"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
activePhase: text("active_phase"),
|
||||
})
|
||||
|
||||
export type Project = typeof projects.$inferSelect
|
||||
export type NewProject = typeof projects.$inferInsert
|
||||
41
db/schema/projecttypes.ts
Normal file
41
db/schema/projecttypes.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
jsonb,
|
||||
boolean,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const projecttypes = pgTable("projecttypes", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
initialPhases: jsonb("initialPhases"),
|
||||
addablePhases: jsonb("addablePhases"),
|
||||
|
||||
icon: text("icon"),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type ProjectType = typeof projecttypes.$inferSelect
|
||||
export type NewProjectType = typeof projecttypes.$inferInsert
|
||||
39
db/schema/servicecategories.ts
Normal file
39
db/schema/servicecategories.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
doublePrecision,
|
||||
boolean,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const serviceCategories = pgTable("servicecategories", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
|
||||
discount: doublePrecision("discount").default(0),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type ServiceCategory = typeof serviceCategories.$inferSelect
|
||||
export type NewServiceCategory = typeof serviceCategories.$inferInsert
|
||||
63
db/schema/services.ts
Normal file
63
db/schema/services.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
doublePrecision,
|
||||
jsonb,
|
||||
boolean,
|
||||
smallint,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { units } from "./units"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const services = pgTable("services", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
|
||||
sellingPrice: doublePrecision("sellingPrice"),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
unit: bigint("unit", { mode: "number" }).references(() => units.id),
|
||||
|
||||
serviceNumber: bigint("serviceNumber", { mode: "number" }),
|
||||
|
||||
tags: jsonb("tags").default([]),
|
||||
servicecategories: jsonb("servicecategories").notNull().default([]),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
purchasePriceComposed: jsonb("purchasePriceComposed")
|
||||
.notNull()
|
||||
.default({ total: 0 }),
|
||||
|
||||
sellingPriceComposed: jsonb("sellingPriceComposed")
|
||||
.notNull()
|
||||
.default({ total: 0 }),
|
||||
|
||||
taxPercentage: smallint("taxPercentage").notNull().default(19),
|
||||
|
||||
materialComposition: jsonb("materialComposition").notNull().default([]),
|
||||
personalComposition: jsonb("personalComposition").notNull().default([]),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Service = typeof services.$inferSelect
|
||||
export type NewService = typeof services.$inferInsert
|
||||
49
db/schema/spaces.ts
Normal file
49
db/schema/spaces.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const spaces = pgTable("spaces", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name"),
|
||||
type: text("type").notNull(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
spaceNumber: text("spaceNumber").notNull(),
|
||||
|
||||
parentSpace: bigint("parentSpace", { mode: "number" }).references(
|
||||
() => spaces.id
|
||||
),
|
||||
|
||||
infoData: jsonb("infoData")
|
||||
.notNull()
|
||||
.default({ zip: "", city: "", streetNumber: "" }),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Space = typeof spaces.$inferSelect
|
||||
export type NewSpace = typeof spaces.$inferInsert
|
||||
68
db/schema/staff_time_entries.ts
Normal file
68
db/schema/staff_time_entries.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
bigint,
|
||||
timestamp,
|
||||
integer,
|
||||
text,
|
||||
boolean,
|
||||
numeric,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { timesStateEnum } from "./enums"
|
||||
import {sql} from "drizzle-orm";
|
||||
|
||||
export const staffTimeEntries = pgTable("staff_time_entries", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => authUsers.id, { onDelete: "cascade" }),
|
||||
|
||||
startedAt: timestamp("started_at", { withTimezone: true }).notNull(),
|
||||
stoppedAt: timestamp("stopped_at", { withTimezone: true }),
|
||||
|
||||
durationMinutes: integer("duration_minutes").generatedAlwaysAs(
|
||||
sql`CASE
|
||||
WHEN stopped_at IS NOT NULL
|
||||
THEN (EXTRACT(epoch FROM (stopped_at - started_at)) / 60)
|
||||
ELSE NULL
|
||||
END`
|
||||
),
|
||||
|
||||
type: text("type").default("work"),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
source: text("source"),
|
||||
|
||||
state: timesStateEnum("state").notNull().default("draft"),
|
||||
|
||||
device: uuid("device"),
|
||||
|
||||
internalNote: text("internal_note"),
|
||||
|
||||
vacationReason: text("vacation_reason"),
|
||||
vacationDays: numeric("vacation_days", { precision: 5, scale: 2 }),
|
||||
|
||||
approvedBy: uuid("approved_by").references(() => authUsers.id),
|
||||
approvedAt: timestamp("approved_at", { withTimezone: true }),
|
||||
|
||||
sickReason: text("sick_reason"),
|
||||
})
|
||||
|
||||
export type StaffTimeEntry = typeof staffTimeEntries.$inferSelect
|
||||
export type NewStaffTimeEntry = typeof staffTimeEntries.$inferInsert
|
||||
38
db/schema/staff_time_entry_connects.ts
Normal file
38
db/schema/staff_time_entry_connects.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
bigint,
|
||||
timestamp,
|
||||
integer,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { staffTimeEntries } from "./staff_time_entries"
|
||||
import {sql} from "drizzle-orm";
|
||||
|
||||
export const staffTimeEntryConnects = pgTable("staff_time_entry_connects", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
timeEntryId: uuid("time_entry_id")
|
||||
.notNull()
|
||||
.references(() => staffTimeEntries.id, { onDelete: "cascade" }),
|
||||
|
||||
projectId: bigint("project_id", { mode: "number" }), // referenziert später projects.id
|
||||
|
||||
startedAt: timestamp("started_at", { withTimezone: true }).notNull(),
|
||||
stoppedAt: timestamp("stopped_at", { withTimezone: true }).notNull(),
|
||||
|
||||
durationMinutes: integer("duration_minutes").generatedAlwaysAs(
|
||||
sql`(EXTRACT(epoch FROM (stopped_at - started_at)) / 60)`
|
||||
),
|
||||
|
||||
notes: text("notes"),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
|
||||
})
|
||||
|
||||
export type StaffTimeEntryConnect =
|
||||
typeof staffTimeEntryConnects.$inferSelect
|
||||
export type NewStaffTimeEntryConnect =
|
||||
typeof staffTimeEntryConnects.$inferInsert
|
||||
44
db/schema/staff_zeitstromtimestamps.ts
Normal file
44
db/schema/staff_zeitstromtimestamps.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
bigint,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authProfiles } from "./auth_profiles"
|
||||
import { staffTimeEntries } from "./staff_time_entries"
|
||||
|
||||
export const staffZeitstromTimestamps = pgTable("staff_zeitstromtimestamps", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
profile: uuid("profile")
|
||||
.notNull()
|
||||
.references(() => authProfiles.id),
|
||||
|
||||
key: text("key").notNull(),
|
||||
|
||||
intent: text("intent").notNull(),
|
||||
|
||||
time: timestamp("time", { withTimezone: true }).notNull(),
|
||||
|
||||
staffTimeEntry: uuid("staff_time_entry").references(
|
||||
() => staffTimeEntries.id
|
||||
),
|
||||
|
||||
internalNote: text("internal_note"),
|
||||
})
|
||||
|
||||
export type StaffZeitstromTimestamp =
|
||||
typeof staffZeitstromTimestamps.$inferSelect
|
||||
export type NewStaffZeitstromTimestamp =
|
||||
typeof staffZeitstromTimestamps.$inferInsert
|
||||
69
db/schema/statementallocations.ts
Normal file
69
db/schema/statementallocations.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
bigint,
|
||||
integer,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
doublePrecision,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { customers } from "./customers"
|
||||
import { vendors } from "./vendors"
|
||||
import { ownaccounts } from "./ownaccounts"
|
||||
import { incominginvoices } from "./incominginvoices"
|
||||
import { createddocuments } from "./createddocuments"
|
||||
import { bankstatements } from "./bankstatements"
|
||||
import { accounts } from "./accounts" // Falls noch nicht erstellt → bitte melden!
|
||||
|
||||
export const statementAllocations = pgTable("statementallocations", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
// foreign keys
|
||||
bsId: integer("bs_id")
|
||||
.notNull()
|
||||
.references(() => bankstatements.id),
|
||||
|
||||
cdId: integer("cd_id").references(() => createddocuments.id),
|
||||
|
||||
amount: doublePrecision("amount").notNull().default(0),
|
||||
|
||||
iiId: bigint("ii_id", { mode: "number" }).references(
|
||||
() => incominginvoices.id
|
||||
),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
account: bigint("account", { mode: "number" }).references(
|
||||
() => accounts.id
|
||||
),
|
||||
|
||||
createdAt: timestamp("created_at", {
|
||||
withTimezone: false,
|
||||
}).defaultNow(),
|
||||
|
||||
ownaccount: uuid("ownaccount").references(() => ownaccounts.id),
|
||||
|
||||
description: text("description"),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
})
|
||||
|
||||
export type StatementAllocation = typeof statementAllocations.$inferSelect
|
||||
export type NewStatementAllocation =
|
||||
typeof statementAllocations.$inferInsert
|
||||
51
db/schema/tasks.ts
Normal file
51
db/schema/tasks.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { customers } from "./customers"
|
||||
|
||||
export const tasks = pgTable("tasks", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
categorie: text("categorie"),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
// FIXED: user_id statt profile, verweist auf auth_users.id
|
||||
userId: uuid("user_id").references(() => authUsers.id),
|
||||
|
||||
project: bigint("project", { mode: "number" }),
|
||||
plant: bigint("plant", { mode: "number" }),
|
||||
|
||||
customer: bigint("customer", { mode: "number" }).references(
|
||||
() => customers.id
|
||||
),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Task = typeof tasks.$inferSelect
|
||||
export type NewTask = typeof tasks.$inferInsert
|
||||
28
db/schema/taxtypes.ts
Normal file
28
db/schema/taxtypes.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const taxTypes = pgTable("taxtypes", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
label: text("label").notNull(),
|
||||
percentage: bigint("percentage", { mode: "number" }).notNull(),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type TaxType = typeof taxTypes.$inferSelect
|
||||
export type NewTaxType = typeof taxTypes.$inferInsert
|
||||
140
db/schema/tenants.ts
Normal file
140
db/schema/tenants.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
integer,
|
||||
smallint,
|
||||
date,
|
||||
uuid,
|
||||
pgEnum,
|
||||
} from "drizzle-orm/pg-core"
|
||||
import { authUsers } from "./auth_users"
|
||||
import {lockedTenantEnum} from "./enums";
|
||||
|
||||
export const tenants = pgTable(
|
||||
"tenants",
|
||||
{
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
short: text("short").notNull(),
|
||||
|
||||
calendarConfig: jsonb("calendarConfig").default({
|
||||
eventTypes: [
|
||||
{ color: "blue", label: "Büro" },
|
||||
{ color: "yellow", label: "Besprechung" },
|
||||
{ color: "green", label: "Umsetzung" },
|
||||
{ color: "red", label: "Vor Ort Termin" },
|
||||
],
|
||||
}),
|
||||
|
||||
timeConfig: jsonb("timeConfig").notNull().default({}),
|
||||
|
||||
tags: jsonb("tags").notNull().default({
|
||||
products: [],
|
||||
documents: [],
|
||||
}),
|
||||
|
||||
measures: jsonb("measures")
|
||||
.notNull()
|
||||
.default([
|
||||
{ name: "Netzwerktechnik", short: "NWT" },
|
||||
{ name: "Elektrotechnik", short: "ELT" },
|
||||
{ name: "Photovoltaik", short: "PV" },
|
||||
{ name: "Videüberwachung", short: "VÜA" },
|
||||
{ name: "Projekt", short: "PRJ" },
|
||||
{ name: "Smart Home", short: "SHO" },
|
||||
]),
|
||||
|
||||
businessInfo: jsonb("businessInfo").default({
|
||||
zip: "",
|
||||
city: "",
|
||||
name: "",
|
||||
street: "",
|
||||
}),
|
||||
|
||||
features: jsonb("features").default({
|
||||
objects: true,
|
||||
calendar: true,
|
||||
contacts: true,
|
||||
projects: true,
|
||||
vehicles: true,
|
||||
contracts: true,
|
||||
inventory: true,
|
||||
accounting: true,
|
||||
timeTracking: true,
|
||||
planningBoard: true,
|
||||
workingTimeTracking: true,
|
||||
}),
|
||||
|
||||
ownFields: jsonb("ownFields"),
|
||||
|
||||
numberRanges: jsonb("numberRanges")
|
||||
.notNull()
|
||||
.default({
|
||||
vendors: { prefix: "", suffix: "", nextNumber: 10000 },
|
||||
customers: { prefix: "", suffix: "", nextNumber: 10000 },
|
||||
products: { prefix: "AT-", suffix: "", nextNumber: 1000 },
|
||||
quotes: { prefix: "AN-", suffix: "", nextNumber: 1000 },
|
||||
confirmationOrders: { prefix: "AB-", suffix: "", nextNumber: 1000 },
|
||||
invoices: { prefix: "RE-", suffix: "", nextNumber: 1000 },
|
||||
spaces: { prefix: "LP-", suffix: "", nextNumber: 1000 },
|
||||
inventoryitems: { prefix: "IA-", suffix: "", nextNumber: 1000 },
|
||||
projects: { prefix: "PRJ-", suffix: "", nextNumber: 1000 },
|
||||
costcentres: { prefix: "KST-", suffix: "", nextNumber: 1000 },
|
||||
}),
|
||||
|
||||
standardEmailForInvoices: text("standardEmailForInvoices"),
|
||||
|
||||
extraModules: jsonb("extraModules").notNull().default([]),
|
||||
|
||||
isInTrial: boolean("isInTrial").default(false),
|
||||
trialEndDate: date("trialEndDate"),
|
||||
|
||||
stripeCustomerId: text("stripeCustomerId"),
|
||||
|
||||
hasActiveLicense: boolean("hasActiveLicense").notNull().default(false),
|
||||
|
||||
userLicenseCount: integer("userLicenseCount")
|
||||
.notNull()
|
||||
.default(0),
|
||||
|
||||
workstationLicenseCount: integer("workstationLicenseCount")
|
||||
.notNull()
|
||||
.default(0),
|
||||
|
||||
standardPaymentDays: smallint("standardPaymentDays")
|
||||
.notNull()
|
||||
.default(14),
|
||||
|
||||
dokuboxEmailAddresses: jsonb("dokuboxEmailAddresses").default([]),
|
||||
|
||||
dokuboxkey: uuid("dokuboxkey").notNull().defaultRandom(),
|
||||
|
||||
autoPrepareIncomingInvoices: boolean("autoPrepareIncomingInvoices")
|
||||
.default(true),
|
||||
|
||||
portalDomain: text("portalDomain"),
|
||||
|
||||
portalConfig: jsonb("portalConfig")
|
||||
.notNull()
|
||||
.default({ primayColor: "#69c350" }),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
|
||||
locked: lockedTenantEnum("locked"),
|
||||
}
|
||||
)
|
||||
|
||||
export type Tenant = typeof tenants.$inferSelect
|
||||
export type NewTenant = typeof tenants.$inferInsert
|
||||
44
db/schema/texttemplates.ts
Normal file
44
db/schema/texttemplates.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import { textTemplatePositionsEnum } from "./enums"
|
||||
|
||||
export const textTemplates = pgTable("texttemplates", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
name: text("name").notNull(),
|
||||
text: text("text").notNull(),
|
||||
|
||||
documentType: text("documentType").default(""),
|
||||
|
||||
default: boolean("default").notNull().default(false),
|
||||
|
||||
pos: textTemplatePositionsEnum("pos").notNull(),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type TextTemplate = typeof textTemplates.$inferSelect
|
||||
export type NewTextTemplate = typeof textTemplates.$inferInsert
|
||||
27
db/schema/units.ts
Normal file
27
db/schema/units.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
timestamp,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
export const units = pgTable("units", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
single: text("single").notNull(),
|
||||
|
||||
multiple: text("multiple"),
|
||||
short: text("short"),
|
||||
|
||||
step: text("step").notNull().default("1"),
|
||||
})
|
||||
|
||||
export type Unit = typeof units.$inferSelect
|
||||
export type NewUnit = typeof units.$inferInsert
|
||||
53
db/schema/user_credentials.ts
Normal file
53
db/schema/user_credentials.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
timestamp,
|
||||
bigint,
|
||||
boolean,
|
||||
jsonb,
|
||||
numeric, pgEnum,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
import {credentialTypesEnum} from "./enums";
|
||||
|
||||
|
||||
|
||||
export const userCredentials = pgTable("user_credentials", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => authUsers.id),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
|
||||
tenantId: bigint("tenant_id", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
smtpPort: numeric("smtp_port"),
|
||||
smtpSsl: boolean("smtp_ssl"),
|
||||
|
||||
type: credentialTypesEnum("type").notNull(),
|
||||
|
||||
imapPort: numeric("imap_port"),
|
||||
imapSsl: boolean("imap_ssl"),
|
||||
|
||||
emailEncrypted: jsonb("email_encrypted"),
|
||||
passwordEncrypted: jsonb("password_encrypted"),
|
||||
|
||||
smtpHostEncrypted: jsonb("smtp_host_encrypted"),
|
||||
imapHostEncrypted: jsonb("imap_host_encrypted"),
|
||||
|
||||
accessTokenEncrypted: jsonb("access_token_encrypted"),
|
||||
refreshTokenEncrypted: jsonb("refresh_token_encrypted"),
|
||||
})
|
||||
|
||||
export type UserCredential = typeof userCredentials.$inferSelect
|
||||
export type NewUserCredential = typeof userCredentials.$inferInsert
|
||||
57
db/schema/vehicles.ts
Normal file
57
db/schema/vehicles.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
doublePrecision,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const vehicles = pgTable("vehicles", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
licensePlate: text("licensePlate"),
|
||||
name: text("name"),
|
||||
type: text("type"),
|
||||
|
||||
active: boolean("active").default(true),
|
||||
|
||||
// FIXED: driver references auth_users.id
|
||||
driver: uuid("driver").references(() => authUsers.id),
|
||||
|
||||
vin: text("vin"),
|
||||
|
||||
tankSize: doublePrecision("tankSize").notNull().default(0),
|
||||
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
buildYear: text("buildYear"),
|
||||
|
||||
towingCapacity: bigint("towingCapacity", { mode: "number" }),
|
||||
powerInKW: bigint("powerInKW", { mode: "number" }),
|
||||
|
||||
color: text("color"),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Vehicle = typeof vehicles.$inferSelect
|
||||
export type NewVehicle = typeof vehicles.$inferInsert
|
||||
45
db/schema/vendors.ts
Normal file
45
db/schema/vendors.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
pgTable,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
jsonb,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import { tenants } from "./tenants"
|
||||
import { authUsers } from "./auth_users"
|
||||
|
||||
export const vendors = pgTable("vendors", {
|
||||
id: bigint("id", { mode: "number" })
|
||||
.primaryKey()
|
||||
.generatedByDefaultAsIdentity(),
|
||||
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
||||
name: text("name").notNull(),
|
||||
vendorNumber: text("vendorNumber").notNull(),
|
||||
|
||||
tenant: bigint("tenant", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => tenants.id),
|
||||
|
||||
infoData: jsonb("infoData").notNull().default({}),
|
||||
notes: text("notes"),
|
||||
|
||||
hasSEPA: boolean("hasSEPA").notNull().default(false),
|
||||
|
||||
profiles: jsonb("profiles").notNull().default([]),
|
||||
archived: boolean("archived").notNull().default(false),
|
||||
|
||||
defaultPaymentMethod: text("defaultPaymentMethod"),
|
||||
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }),
|
||||
updatedBy: uuid("updated_by").references(() => authUsers.id),
|
||||
})
|
||||
|
||||
export type Vendor = typeof vendors.$inferSelect
|
||||
export type NewVendor = typeof vendors.$inferInsert
|
||||
11
drizzle.config.ts
Normal file
11
drizzle.config.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from "drizzle-kit"
|
||||
import {secrets} from "./src/utils/secrets";
|
||||
|
||||
export default defineConfig({
|
||||
dialect: "postgresql",
|
||||
schema: "./db/schema",
|
||||
out: "./db/migrations",
|
||||
dbCredentials: {
|
||||
url: secrets.DATABASE_URL || process.env.DATABASE_URL,
|
||||
},
|
||||
})
|
||||
797
package-lock.json
generated
797
package-lock.json
generated
@@ -28,12 +28,14 @@
|
||||
"canvas": "^3.2.0",
|
||||
"crypto": "^1.0.1",
|
||||
"dayjs": "^1.11.18",
|
||||
"drizzle-orm": "^0.45.0",
|
||||
"fastify": "^5.5.0",
|
||||
"fastify-plugin": "^5.0.1",
|
||||
"imapflow": "^1.1.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"nodemailer": "^7.0.6",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.16.3",
|
||||
"pngjs": "^7.0.0",
|
||||
"sharp": "^0.34.5",
|
||||
"xmlbuilder": "^15.1.1",
|
||||
@@ -44,6 +46,7 @@
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^24.3.0",
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"prisma": "^6.15.0",
|
||||
"tsx": "^4.20.5",
|
||||
"typescript": "^5.9.2"
|
||||
@@ -5273,6 +5276,13 @@
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@drizzle-team/brocli": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz",
|
||||
"integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz",
|
||||
@@ -5283,6 +5293,442 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz",
|
||||
"integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==",
|
||||
"deprecated": "Merged into tsx: https://tsx.is",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.18.20",
|
||||
"source-map-support": "^0.5.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
|
||||
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
|
||||
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
|
||||
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
|
||||
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
|
||||
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
|
||||
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
|
||||
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
|
||||
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
|
||||
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
|
||||
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
|
||||
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
|
||||
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
|
||||
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
|
||||
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
|
||||
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/core-utils/node_modules/esbuild": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
|
||||
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/android-arm": "0.18.20",
|
||||
"@esbuild/android-arm64": "0.18.20",
|
||||
"@esbuild/android-x64": "0.18.20",
|
||||
"@esbuild/darwin-arm64": "0.18.20",
|
||||
"@esbuild/darwin-x64": "0.18.20",
|
||||
"@esbuild/freebsd-arm64": "0.18.20",
|
||||
"@esbuild/freebsd-x64": "0.18.20",
|
||||
"@esbuild/linux-arm": "0.18.20",
|
||||
"@esbuild/linux-arm64": "0.18.20",
|
||||
"@esbuild/linux-ia32": "0.18.20",
|
||||
"@esbuild/linux-loong64": "0.18.20",
|
||||
"@esbuild/linux-mips64el": "0.18.20",
|
||||
"@esbuild/linux-ppc64": "0.18.20",
|
||||
"@esbuild/linux-riscv64": "0.18.20",
|
||||
"@esbuild/linux-s390x": "0.18.20",
|
||||
"@esbuild/linux-x64": "0.18.20",
|
||||
"@esbuild/netbsd-x64": "0.18.20",
|
||||
"@esbuild/openbsd-x64": "0.18.20",
|
||||
"@esbuild/sunos-x64": "0.18.20",
|
||||
"@esbuild/win32-arm64": "0.18.20",
|
||||
"@esbuild/win32-ia32": "0.18.20",
|
||||
"@esbuild/win32-x64": "0.18.20"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild-kit/esm-loader": {
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz",
|
||||
"integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==",
|
||||
"deprecated": "Merged into tsx: https://tsx.is",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@esbuild-kit/core-utils": "^3.3.2",
|
||||
"get-tsconfig": "^4.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
|
||||
@@ -7686,9 +8132,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/archiver-utils/node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
@@ -7964,6 +8410,13 @@
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bwip-js": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/bwip-js/-/bwip-js-4.8.0.tgz",
|
||||
@@ -8323,6 +8776,147 @@
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/drizzle-kit": {
|
||||
"version": "0.31.8",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.8.tgz",
|
||||
"integrity": "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@drizzle-team/brocli": "^0.10.2",
|
||||
"@esbuild-kit/esm-loader": "^2.5.5",
|
||||
"esbuild": "^0.25.4",
|
||||
"esbuild-register": "^3.5.0"
|
||||
},
|
||||
"bin": {
|
||||
"drizzle-kit": "bin.cjs"
|
||||
}
|
||||
},
|
||||
"node_modules/drizzle-orm": {
|
||||
"version": "0.45.0",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.0.tgz",
|
||||
"integrity": "sha512-lyd9VRk3SXKRjV/gQckQzmJgkoYMvVG3A2JAV0vh3L+Lwk+v9+rK5Gj0H22y+ZBmxsrRBgJ5/RbQCN7DWd1dtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-rds-data": ">=3",
|
||||
"@cloudflare/workers-types": ">=4",
|
||||
"@electric-sql/pglite": ">=0.2.0",
|
||||
"@libsql/client": ">=0.10.0",
|
||||
"@libsql/client-wasm": ">=0.10.0",
|
||||
"@neondatabase/serverless": ">=0.10.0",
|
||||
"@op-engineering/op-sqlite": ">=2",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"@planetscale/database": ">=1.13",
|
||||
"@prisma/client": "*",
|
||||
"@tidbcloud/serverless": "*",
|
||||
"@types/better-sqlite3": "*",
|
||||
"@types/pg": "*",
|
||||
"@types/sql.js": "*",
|
||||
"@upstash/redis": ">=1.34.7",
|
||||
"@vercel/postgres": ">=0.8.0",
|
||||
"@xata.io/client": "*",
|
||||
"better-sqlite3": ">=7",
|
||||
"bun-types": "*",
|
||||
"expo-sqlite": ">=14.0.0",
|
||||
"gel": ">=2",
|
||||
"knex": "*",
|
||||
"kysely": "*",
|
||||
"mysql2": ">=2",
|
||||
"pg": ">=8",
|
||||
"postgres": ">=3",
|
||||
"sql.js": ">=1",
|
||||
"sqlite3": ">=5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@aws-sdk/client-rds-data": {
|
||||
"optional": true
|
||||
},
|
||||
"@cloudflare/workers-types": {
|
||||
"optional": true
|
||||
},
|
||||
"@electric-sql/pglite": {
|
||||
"optional": true
|
||||
},
|
||||
"@libsql/client": {
|
||||
"optional": true
|
||||
},
|
||||
"@libsql/client-wasm": {
|
||||
"optional": true
|
||||
},
|
||||
"@neondatabase/serverless": {
|
||||
"optional": true
|
||||
},
|
||||
"@op-engineering/op-sqlite": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@planetscale/database": {
|
||||
"optional": true
|
||||
},
|
||||
"@prisma/client": {
|
||||
"optional": true
|
||||
},
|
||||
"@tidbcloud/serverless": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/better-sqlite3": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/pg": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/sql.js": {
|
||||
"optional": true
|
||||
},
|
||||
"@upstash/redis": {
|
||||
"optional": true
|
||||
},
|
||||
"@vercel/postgres": {
|
||||
"optional": true
|
||||
},
|
||||
"@xata.io/client": {
|
||||
"optional": true
|
||||
},
|
||||
"better-sqlite3": {
|
||||
"optional": true
|
||||
},
|
||||
"bun-types": {
|
||||
"optional": true
|
||||
},
|
||||
"expo-sqlite": {
|
||||
"optional": true
|
||||
},
|
||||
"gel": {
|
||||
"optional": true
|
||||
},
|
||||
"knex": {
|
||||
"optional": true
|
||||
},
|
||||
"kysely": {
|
||||
"optional": true
|
||||
},
|
||||
"mysql2": {
|
||||
"optional": true
|
||||
},
|
||||
"pg": {
|
||||
"optional": true
|
||||
},
|
||||
"postgres": {
|
||||
"optional": true
|
||||
},
|
||||
"prisma": {
|
||||
"optional": true
|
||||
},
|
||||
"sql.js": {
|
||||
"optional": true
|
||||
},
|
||||
"sqlite3": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@@ -8484,6 +9078,19 @@
|
||||
"@esbuild/win32-x64": "0.25.9"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-register": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz",
|
||||
"integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"esbuild": ">=0.12 <1"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@@ -8866,14 +9473,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
|
||||
"integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
|
||||
"license": "ISC",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
|
||||
"integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.3.1",
|
||||
"jackspeak": "^4.1.1",
|
||||
"minimatch": "^10.0.3",
|
||||
"minimatch": "^10.1.1",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
@@ -9200,12 +9807,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz",
|
||||
"integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"jwa": "^1.4.2",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
@@ -9436,10 +10043,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
|
||||
"integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
|
||||
"license": "ISC",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
@@ -9663,6 +10270,95 @@
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.16.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.9.1",
|
||||
"pg-pool": "^3.10.1",
|
||||
"pg-protocol": "^1.10.3",
|
||||
"pg-types": "2.2.0",
|
||||
"pgpass": "1.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.2.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
|
||||
"integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
||||
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
|
||||
"integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.10.3",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
|
||||
"integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "9.9.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-9.9.0.tgz",
|
||||
@@ -9721,6 +10417,45 @@
|
||||
"node": ">=14.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
||||
@@ -10222,6 +10957,27 @@
|
||||
"atomic-sleep": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
@@ -10715,6 +11471,15 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js"
|
||||
"start": "node dist/index.js",
|
||||
"schema:index": "ts-node scripts/generate-schema-index.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -35,12 +36,14 @@
|
||||
"canvas": "^3.2.0",
|
||||
"crypto": "^1.0.1",
|
||||
"dayjs": "^1.11.18",
|
||||
"drizzle-orm": "^0.45.0",
|
||||
"fastify": "^5.5.0",
|
||||
"fastify-plugin": "^5.0.1",
|
||||
"imapflow": "^1.1.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"nodemailer": "^7.0.6",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.16.3",
|
||||
"pngjs": "^7.0.0",
|
||||
"sharp": "^0.34.5",
|
||||
"xmlbuilder": "^15.1.1",
|
||||
@@ -51,6 +54,7 @@
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^24.3.0",
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"prisma": "^6.15.0",
|
||||
"tsx": "^4.20.5",
|
||||
"typescript": "^5.9.2"
|
||||
|
||||
16
scripts/generate-schema-index.ts
Normal file
16
scripts/generate-schema-index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
const schemaDir = path.resolve("db/schema")
|
||||
const indexFile = path.join(schemaDir, "index.ts")
|
||||
|
||||
const files = fs
|
||||
.readdirSync(schemaDir)
|
||||
.filter((f) => f.endsWith(".ts") && f !== "index.ts")
|
||||
|
||||
const exportsToWrite = files
|
||||
.map((f) => `export * from "./${f.replace(".ts", "")}"`)
|
||||
.join("\n")
|
||||
|
||||
fs.writeFileSync(indexFile, exportsToWrite)
|
||||
console.log("✓ schema/index.ts generated")
|
||||
10
src/index.ts
10
src/index.ts
@@ -12,6 +12,7 @@ import authPlugin from "./plugins/auth";
|
||||
import adminRoutes from "./routes/admin";
|
||||
import corsPlugin from "./plugins/cors";
|
||||
import queryConfigPlugin from "./plugins/queryconfig";
|
||||
import dbPlugin from "./plugins/db";
|
||||
import resourceRoutes from "./routes/resources";
|
||||
import resourceRoutesSpecial from "./routes/resourcesSpecial";
|
||||
import fastifyCookie from "@fastify/cookie";
|
||||
@@ -57,6 +58,7 @@ async function main() {
|
||||
await app.register(supabasePlugin);
|
||||
await app.register(tenantPlugin);
|
||||
await app.register(dayjsPlugin);
|
||||
await app.register(dbPlugin);
|
||||
|
||||
app.addHook('preHandler', (req, reply, done) => {
|
||||
console.log(req.method)
|
||||
@@ -114,6 +116,14 @@ async function main() {
|
||||
|
||||
},{prefix: "/api"})
|
||||
|
||||
app.ready(async () => {
|
||||
try {
|
||||
const result = await app.db.execute("SELECT NOW()");
|
||||
console.log("✓ DB connection OK: " + JSON.stringify(result.rows[0]));
|
||||
} catch (err) {
|
||||
console.log("❌ DB connection failed:", err);
|
||||
}
|
||||
});
|
||||
|
||||
// Start
|
||||
try {
|
||||
|
||||
34
src/plugins/db.ts
Normal file
34
src/plugins/db.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import fp from "fastify-plugin"
|
||||
import {drizzle, NodePgDatabase} from "drizzle-orm/node-postgres"
|
||||
import { Pool } from "pg"
|
||||
import * as schema from "../../db/schema"
|
||||
|
||||
export default fp(async (server, opts) => {
|
||||
const pool = new Pool({
|
||||
host: "db-001.netbird.cloud",
|
||||
port: Number(process.env.DB_PORT || 5432),
|
||||
user: "postgres",
|
||||
password: "wJw7aNpEBJdcxgoct6GXNpvY4Cn6ECqu",
|
||||
database: "fedeo",
|
||||
ssl: process.env.DB_DISABLE_SSL === "true" ? false : undefined,
|
||||
})
|
||||
|
||||
// Drizzle instance
|
||||
const db = drizzle(pool, { schema })
|
||||
|
||||
// Dekorieren -> überall server.db
|
||||
server.decorate("db", db)
|
||||
|
||||
// Graceful Shutdown
|
||||
server.addHook("onClose", async () => {
|
||||
await pool.end()
|
||||
})
|
||||
|
||||
server.log.info("Drizzle database connected")
|
||||
})
|
||||
|
||||
declare module "fastify" {
|
||||
interface FastifyInstance {
|
||||
db:NodePgDatabase<typeof schema>
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,21 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { generateRandomPassword, hashPassword } from "../../utils/password"
|
||||
import { sendMail } from "../../utils/mailer"
|
||||
import {secrets} from "../../utils/secrets";
|
||||
import { generateRandomPassword, hashPassword } from "../../utils/password";
|
||||
import { sendMail } from "../../utils/mailer";
|
||||
import { secrets } from "../../utils/secrets";
|
||||
|
||||
import { authUsers } from "../../../db/schema";
|
||||
import { authTenantUsers } from "../../../db/schema";
|
||||
import { tenants } from "../../../db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
export default async function authRoutes(server: FastifyInstance) {
|
||||
// Registrierung
|
||||
server.post("/auth/register",{
|
||||
|
||||
// -----------------------------------------------------
|
||||
// REGISTER
|
||||
// -----------------------------------------------------
|
||||
server.post("/auth/register", {
|
||||
schema: {
|
||||
tags: ["Auth"],
|
||||
summary: "Register User",
|
||||
@@ -19,43 +27,31 @@ export default async function authRoutes(server: FastifyInstance) {
|
||||
password: { type: "string" },
|
||||
},
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: "object",
|
||||
properties: {
|
||||
user: { type: "object" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, async (req, reply) => {
|
||||
const body = req.body as { email: string; password: string };
|
||||
|
||||
if (!body.email || !body.password) {
|
||||
// @ts-ignore
|
||||
return reply.code(400).send({ error: "Email and password required" });
|
||||
}
|
||||
|
||||
// Passwort hashen
|
||||
const passwordHash = await bcrypt.hash(body.password, 10);
|
||||
|
||||
// User speichern
|
||||
const { data, error } = await server.supabase
|
||||
.from("auth_users")
|
||||
.insert({ email: body.email, password_hash: passwordHash })
|
||||
.select("id, email")
|
||||
.single();
|
||||
const [user] = await server.db
|
||||
.insert(authUsers)
|
||||
.values({
|
||||
email: body.email.toLowerCase(),
|
||||
passwordHash,
|
||||
})
|
||||
.returning({
|
||||
id: authUsers.id,
|
||||
email: authUsers.email,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
// @ts-ignore
|
||||
return reply.code(400).send({ error: error.message });
|
||||
}
|
||||
|
||||
return { user: data };
|
||||
return { user };
|
||||
});
|
||||
|
||||
// Login
|
||||
server.post("/auth/login",{
|
||||
|
||||
// -----------------------------------------------------
|
||||
// LOGIN
|
||||
// -----------------------------------------------------
|
||||
server.post("/auth/login", {
|
||||
schema: {
|
||||
tags: ["Auth"],
|
||||
summary: "Login User",
|
||||
@@ -67,103 +63,110 @@ export default async function authRoutes(server: FastifyInstance) {
|
||||
password: { type: "string" },
|
||||
},
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: "object",
|
||||
properties: {
|
||||
token: { type: "string" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, async (req, reply) => {
|
||||
const body = req.body as { email: string; password: string };
|
||||
|
||||
if (!body.email || !body.password) {
|
||||
// @ts-ignore
|
||||
return reply.code(400).send({ error: "Email and password required" });
|
||||
}
|
||||
let user: any = null;
|
||||
|
||||
/**
|
||||
* Wenn das Tenant Objekt verfügbar ist, befindet sich das Backend im Single Tenant Modus.
|
||||
* Es werden nur Benutzer zugelassen, welche auschließlich diesem Tenant angehören.
|
||||
* Das zeigt sich über das im User gesetzte Tenant Feld
|
||||
*
|
||||
* */
|
||||
let user = null
|
||||
let error = null
|
||||
if(req.tenant) {
|
||||
// User finden
|
||||
const { data, error } = await server.supabase
|
||||
.from("auth_users")
|
||||
.select("*, tenants!auth_tenant_users(*)")
|
||||
.eq("email", body.email.toLowerCase())
|
||||
// -------------------------------
|
||||
// SINGLE TENANT MODE
|
||||
// -------------------------------
|
||||
/* if (req.tenant) {
|
||||
const tenantId = req.tenant.id;
|
||||
|
||||
// @ts-ignore
|
||||
user = (data || []).find(i => i.tenants.find(x => x.id === req.tenant.id))
|
||||
if(error) {
|
||||
// @ts-ignore
|
||||
return reply.code(500).send({ error: "Internal Server Error" });
|
||||
}
|
||||
} else {
|
||||
// User finden
|
||||
const { data, error } = await server.supabase
|
||||
.from("auth_users")
|
||||
.select("*")
|
||||
.eq("email", body.email)
|
||||
.single();
|
||||
user = data
|
||||
if(error) {
|
||||
// @ts-ignore
|
||||
return reply.code(500).send({ error: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
|
||||
if(!user) {
|
||||
// @ts-ignore
|
||||
return reply.code(401).send({ error: "Invalid credentials" });
|
||||
} else {
|
||||
|
||||
const valid = await bcrypt.compare(body.password, user.password_hash);
|
||||
if (!valid) {
|
||||
// @ts-ignore
|
||||
return reply.code(401).send({ error: "Invalid credentials" });
|
||||
} else {
|
||||
const token = jwt.sign(
|
||||
{ user_id: user.id, email: user.email, tenant_id: null },
|
||||
secrets.JWT_SECRET!,
|
||||
{ expiresIn: "6h" }
|
||||
);
|
||||
|
||||
reply.setCookie("token", token, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
|
||||
secure: process.env.NODE_ENV === "production", // lokal: false, prod: true
|
||||
maxAge: 60 * 60 * 3, // 3 Stunden
|
||||
const result = await server.db
|
||||
.select({
|
||||
user: authUsers,
|
||||
})
|
||||
.from(authUsers)
|
||||
.innerJoin(
|
||||
authTenantUsers,
|
||||
eq(authTenantUsers.userId, authUsers.id)
|
||||
)
|
||||
.innerJoin(
|
||||
tenants,
|
||||
eq(authTenantUsers.tenantId, tenants.id)
|
||||
)
|
||||
.where(and(
|
||||
eq(authUsers.email, body.email.toLowerCase()),
|
||||
eq(authTenantUsers.tenantId, tenantId)
|
||||
));
|
||||
|
||||
return { token };
|
||||
if (result.length === 0) {
|
||||
return reply.code(401).send({ error: "Invalid credentials" });
|
||||
}
|
||||
|
||||
user = result[0].user;
|
||||
|
||||
// -------------------------------
|
||||
// MULTI TENANT MODE
|
||||
// -------------------------------
|
||||
} else {*/
|
||||
const [found] = await server.db
|
||||
.select()
|
||||
.from(authUsers)
|
||||
.where(eq(authUsers.email, body.email.toLowerCase()))
|
||||
.limit(1);
|
||||
|
||||
if (!found) {
|
||||
return reply.code(401).send({ error: "Invalid credentials" });
|
||||
}
|
||||
|
||||
user = found;
|
||||
/*}*/
|
||||
|
||||
// Passwort prüfen
|
||||
const valid = await bcrypt.compare(body.password, user.passwordHash);
|
||||
if (!valid) {
|
||||
return reply.code(401).send({ error: "Invalid credentials" });
|
||||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{
|
||||
user_id: user.id,
|
||||
email: user.email,
|
||||
tenant_id: req.tenant?.id ?? null,
|
||||
},
|
||||
secrets.JWT_SECRET!,
|
||||
{ expiresIn: "6h" }
|
||||
);
|
||||
|
||||
reply.setCookie("token", token, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
maxAge: 60 * 60 * 3,
|
||||
});
|
||||
|
||||
return { token };
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
// LOGOUT
|
||||
// -----------------------------------------------------
|
||||
server.post("/auth/logout", {
|
||||
schema: {
|
||||
tags: ["Auth"],
|
||||
summary: "Logout User (löscht Cookie)"
|
||||
},
|
||||
summary: "Logout User"
|
||||
}
|
||||
}, async (req, reply) => {
|
||||
reply.clearCookie("token", {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
})
|
||||
});
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
// PASSWORD RESET
|
||||
// -----------------------------------------------------
|
||||
server.post("/auth/password/reset", {
|
||||
schema: {
|
||||
tags: ["Auth"],
|
||||
@@ -177,43 +180,43 @@ export default async function authRoutes(server: FastifyInstance) {
|
||||
}
|
||||
}
|
||||
}, async (req, reply) => {
|
||||
const { email } = req.body as { email: string }
|
||||
const { email } = req.body as { email: string };
|
||||
|
||||
// User finden
|
||||
const { data: user, error } = await server.supabase
|
||||
.from("auth_users")
|
||||
.select("id, email")
|
||||
.eq("email", email)
|
||||
.single()
|
||||
const [user] = await server.db
|
||||
.select({
|
||||
id: authUsers.id,
|
||||
email: authUsers.email,
|
||||
})
|
||||
.from(authUsers)
|
||||
.where(eq(authUsers.email, email.toLowerCase()))
|
||||
.limit(1);
|
||||
|
||||
if (error || !user) {
|
||||
return reply.code(404).send({ error: "User not found" })
|
||||
if (!user) {
|
||||
return reply.code(404).send({ error: "User not found" });
|
||||
}
|
||||
|
||||
// Neues Passwort generieren
|
||||
const plainPassword = generateRandomPassword()
|
||||
const passwordHash = await hashPassword(plainPassword)
|
||||
const plainPassword = generateRandomPassword();
|
||||
const passwordHash = await hashPassword(plainPassword);
|
||||
|
||||
// In DB updaten
|
||||
const { error: updateError } = await server.supabase
|
||||
.from("auth_users")
|
||||
.update({ password_hash: passwordHash, must_change_password: true })
|
||||
.eq("id", user.id)
|
||||
await server.db
|
||||
.update(authUsers)
|
||||
.set({
|
||||
passwordHash,
|
||||
mustChangePassword: true,
|
||||
})
|
||||
.where(eq(authUsers.id, user.id));
|
||||
|
||||
if (updateError) {
|
||||
return reply.code(500).send({ error: "Could not update password" })
|
||||
}
|
||||
|
||||
// Mail verschicken
|
||||
await sendMail(
|
||||
user.email,
|
||||
"FEDEO | Dein neues Passwort",
|
||||
`<p>Hallo,</p>
|
||||
<p>dein Passwort wurde zurückgesetzt.</p>
|
||||
<p><strong>Neues Passwort:</strong> ${plainPassword}</p>
|
||||
<p>Bitte ändere es nach dem Login umgehend.</p>`
|
||||
)
|
||||
`
|
||||
<p>Hallo,</p>
|
||||
<p>Dein Passwort wurde zurückgesetzt.</p>
|
||||
<p><strong>Neues Passwort:</strong> ${plainPassword}</p>
|
||||
<p>Bitte ändere es nach dem Login umgehend.</p>
|
||||
`
|
||||
);
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
}
|
||||
return { success: true };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export let secrets = {
|
||||
JWT_SECRET: string
|
||||
PORT: number
|
||||
HOST: string
|
||||
DATABASE_URL: string
|
||||
SUPABASE_URL: string
|
||||
SUPABASE_SERVICE_ROLE_KEY: string
|
||||
S3_BUCKET: string
|
||||
|
||||
Reference in New Issue
Block a user