refactor: convert all findMany to new structure

This commit is contained in:
Nicolas Meienberger 2026-02-01 15:30:36 +01:00
parent 74714f8e74
commit 1e04c763f0
27 changed files with 304 additions and 269 deletions

View file

@ -1,5 +1,5 @@
import { hashPassword } from "better-auth/crypto"; import { hashPassword } from "better-auth/crypto";
import { and, eq, ne } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { db } from "~/server/db/db"; import { db } from "~/server/db/db";
import { account, usersTable, member, organization } from "~/server/db/schema"; import { account, usersTable, member, organization } from "~/server/db/schema";
import type { AuthMiddlewareContext } from "../auth"; import type { AuthMiddlewareContext } from "../auth";
@ -14,7 +14,9 @@ export const convertLegacyUserOnFirstLogin = async (ctx: AuthMiddlewareContext)
} }
const legacyUser = await db.query.usersTable.findFirst({ const legacyUser = await db.query.usersTable.findFirst({
where: and(eq(usersTable.username, body.username.trim().toLowerCase()), ne(usersTable.passwordHash, "")), where: {
AND: [{ username: body.username.trim().toLowerCase() }, { passwordHash: { NOT: "" } }],
},
}); });
if (legacyUser) { if (legacyUser) {
@ -26,7 +28,7 @@ export const convertLegacyUserOnFirstLogin = async (ctx: AuthMiddlewareContext)
const accountId = crypto.randomUUID(); const accountId = crypto.randomUUID();
const oldMembership = await tx.query.member.findFirst({ const oldMembership = await tx.query.member.findFirst({
where: eq(member.userId, legacyUser.id), where: { userId: legacyUser.id },
with: { with: {
organization: true, organization: true,
}, },

View file

@ -1,8 +1,6 @@
import { db } from "~/server/db/db"; import { db } from "~/server/db/db";
import type { AuthMiddlewareContext } from "../auth"; import type { AuthMiddlewareContext } from "../auth";
import { logger } from "~/server/utils/logger"; import { logger } from "~/server/utils/logger";
import { eq } from "drizzle-orm";
import { appMetadataTable } from "~/server/db/schema";
import { ForbiddenError } from "http-errors-enhanced"; import { ForbiddenError } from "http-errors-enhanced";
import { REGISTRATION_ENABLED_KEY } from "~/client/lib/constants"; import { REGISTRATION_ENABLED_KEY } from "~/client/lib/constants";
@ -15,7 +13,7 @@ export const ensureOnlyOneUser = async (ctx: AuthMiddlewareContext) => {
} }
const result = await db.query.appMetadataTable.findFirst({ const result = await db.query.appMetadataTable.findFirst({
where: eq(appMetadataTable.key, REGISTRATION_ENABLED_KEY), where: { key: REGISTRATION_ENABLED_KEY },
}); });
if (result?.value !== "true" && existingUser) { if (result?.value !== "true" && existingUser) {

View file

@ -98,7 +98,7 @@ const createBetterAuth = (secret: string) => {
create: { create: {
before: async (session) => { before: async (session) => {
const orgMembership = await db.query.member.findFirst({ const orgMembership = await db.query.member.findFirst({
where: eq(member.userId, session.userId), where: { userId: session.userId },
}); });
if (!orgMembership) { if (!orgMembership) {

View file

@ -15,7 +15,7 @@ const listOrganizations = () => {
const getUserCurrentOrganization = async (userId: string) => { const getUserCurrentOrganization = async (userId: string) => {
const membership = await db.query.member.findFirst({ const membership = await db.query.member.findFirst({
where: eq(member.userId, userId), where: { userId },
with: { with: {
organization: true, organization: true,
}, },
@ -36,9 +36,7 @@ const assignUserToOrganization = async (userId: string, organizationId: string)
throw new Error("Organization not found"); throw new Error("Organization not found");
} }
const existingMembership = await db.query.member.findFirst({ const existingMembership = await db.query.member.findFirst({ where: { userId } });
where: eq(member.userId, userId),
});
await db.transaction(async (tx) => { await db.transaction(async (tx) => {
if (existingMembership) { if (existingMembership) {

View file

@ -1,4 +1,5 @@
import { Database } from "bun:sqlite"; import { Database } from "bun:sqlite";
import { relations } from "./relations";
import path from "node:path"; import path from "node:path";
import { drizzle } from "drizzle-orm/bun-sqlite"; import { drizzle } from "drizzle-orm/bun-sqlite";
import { migrate } from "drizzle-orm/bun-sqlite/migrator"; import { migrate } from "drizzle-orm/bun-sqlite/migrator";
@ -13,7 +14,7 @@ import type * as schemaTypes from "./schema";
* to isolate the db initialization code from the rest of the server code. * to isolate the db initialization code from the rest of the server code.
*/ */
let _sqlite: Database | undefined; let _sqlite: Database | undefined;
let _db: ReturnType<typeof drizzle<typeof schemaTypes>> | undefined; let _db: ReturnType<typeof initDb> | undefined;
let _schema: typeof schemaTypes | undefined; let _schema: typeof schemaTypes | undefined;
/** /**
@ -35,7 +36,7 @@ const initDb = () => {
} }
_sqlite = new Database(DATABASE_URL); _sqlite = new Database(DATABASE_URL);
return drizzle({ client: _sqlite, schema: _schema }); return drizzle({ client: _sqlite, relations, schema: _schema });
}; };
/** /**
@ -51,7 +52,7 @@ export const db = new Proxy(
return Reflect.get(_db, prop, receiver); return Reflect.get(_db, prop, receiver);
}, },
}, },
) as ReturnType<typeof drizzle<typeof schemaTypes>>; ) as ReturnType<typeof initDb>;
export const runDbMigrations = () => { export const runDbMigrations = () => {
let migrationsFolder: string; let migrationsFolder: string;

141
app/server/db/relations.ts Normal file
View file

@ -0,0 +1,141 @@
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
repositoriesTable: {
backupSchedulesTablesViaBackupScheduleMirrorsTable: r.many.backupSchedulesTable({
from: r.repositoriesTable.id.through(r.backupScheduleMirrorsTable.repositoryId),
to: r.backupSchedulesTable.id.through(r.backupScheduleMirrorsTable.scheduleId),
alias: "repositoriesTable_id_backupSchedulesTable_id_via_backupScheduleMirrorsTable",
}),
backupSchedules: r.many.backupSchedulesTable({
alias: "backupSchedulesTable_repositoryId_repositoriesTable_id",
}),
organization: r.one.organization({
from: r.repositoriesTable.organizationId,
to: r.organization.id,
}),
},
backupScheduleMirrorsTable: {
repository: r.one.repositoriesTable({
from: r.backupScheduleMirrorsTable.repositoryId,
to: r.repositoriesTable.id,
optional: false,
}),
backupSchedule: r.one.backupSchedulesTable({
from: r.backupScheduleMirrorsTable.scheduleId,
to: r.backupSchedulesTable.id,
optional: false,
}),
},
backupSchedulesTable: {
mirrors: r.many.repositoriesTable({
alias: "repositoriesTable_id_backupSchedulesTable_id_via_backupScheduleMirrorsTable",
}),
notificationDestinationsTables: r.many.notificationDestinationsTable(),
organization: r.one.organization({
from: r.backupSchedulesTable.organizationId,
to: r.organization.id,
optional: false,
}),
repository: r.one.repositoriesTable({
from: r.backupSchedulesTable.repositoryId,
to: r.repositoriesTable.id,
alias: "backupSchedulesTable_repositoryId_repositoriesTable_id",
optional: false,
}),
volume: r.one.volumesTable({
from: r.backupSchedulesTable.volumeId,
to: r.volumesTable.id,
optional: false,
}),
},
notificationDestinationsTable: {
backupSchedules: r.many.backupSchedulesTable({
from: r.notificationDestinationsTable.id.through(r.backupScheduleNotificationsTable.destinationId),
to: r.backupSchedulesTable.id.through(r.backupScheduleNotificationsTable.scheduleId),
}),
organization: r.one.organization({
from: r.notificationDestinationsTable.organizationId,
to: r.organization.id,
}),
},
account: {
user: r.one.usersTable({
from: r.account.userId,
to: r.usersTable.id,
}),
},
usersTable: {
accounts: r.many.account(),
sessions: r.many.sessionsTable(),
members: r.many.member(),
twoFactors: r.many.twoFactor(),
organizations: r.many.organization({
from: r.usersTable.id.through(r.member.userId),
to: r.organization.id.through(r.member.organizationId),
alias: "usersTable_id_organization_id_via_member",
}),
},
sessionsTable: {
user: r.one.usersTable({
from: r.sessionsTable.userId,
to: r.usersTable.id,
}),
},
twoFactor: {
usersTable: r.one.usersTable({
from: r.twoFactor.userId,
to: r.usersTable.id,
}),
},
organization: {
users: r.many.usersTable({
alias: "usersTable_id_organization_id_via_member",
}),
backupSchedules: r.many.backupSchedulesTable(),
notificationDestinations: r.many.notificationDestinationsTable(),
repositories: r.many.repositoriesTable(),
volumes: r.many.volumesTable(),
members: r.many.member(),
invitations: r.many.invitation(),
},
volumesTable: {
backupSchedules: r.many.backupSchedulesTable(),
organization: r.one.organization({
from: r.volumesTable.organizationId,
to: r.organization.id,
}),
},
member: {
user: r.one.usersTable({
from: r.member.userId,
to: r.usersTable.id,
optional: false,
}),
organization: r.one.organization({
from: r.member.organizationId,
to: r.organization.id,
optional: false,
}),
},
invitation: {
organization: r.one.organization({
from: r.invitation.organizationId,
to: r.organization.id,
optional: false,
}),
},
backupScheduleNotificationsTable: {
destination: r.one.notificationDestinationsTable({
from: r.backupScheduleNotificationsTable.destinationId,
to: r.notificationDestinationsTable.id,
optional: false,
}),
backupSchedule: r.one.backupSchedulesTable({
from: r.backupScheduleNotificationsTable.scheduleId,
to: r.backupSchedulesTable.id,
optional: false,
}),
},
}));

View file

@ -1,4 +1,4 @@
import { relations, sql } from "drizzle-orm"; import { sql } from "drizzle-orm";
import { index, int, integer, sqliteTable, text, real, primaryKey, unique, uniqueIndex } from "drizzle-orm/sqlite-core"; import { index, int, integer, sqliteTable, text, real, primaryKey, unique, uniqueIndex } from "drizzle-orm/sqlite-core";
import type { import type {
CompressionMode, CompressionMode,
@ -175,29 +175,33 @@ export const invitation = sqliteTable(
/** /**
* Volumes Table * Volumes Table
*/ */
export const volumesTable = sqliteTable("volumes_table", { export const volumesTable = sqliteTable(
id: int().primaryKey({ autoIncrement: true }), "volumes_table",
shortId: text("short_id").notNull().unique(), {
name: text().notNull(), id: int().primaryKey({ autoIncrement: true }),
type: text().$type<BackendType>().notNull(), shortId: text("short_id").notNull().unique(),
status: text().$type<BackendStatus>().notNull().default("unmounted"), name: text().notNull(),
lastError: text("last_error"), type: text().$type<BackendType>().notNull(),
lastHealthCheck: integer("last_health_check", { mode: "number" }) status: text().$type<BackendStatus>().notNull().default("unmounted"),
.notNull() lastError: text("last_error"),
.default(sql`(unixepoch() * 1000)`), lastHealthCheck: integer("last_health_check", { mode: "number" })
createdAt: integer("created_at", { mode: "number" }) .notNull()
.notNull() .default(sql`(unixepoch() * 1000)`),
.default(sql`(unixepoch() * 1000)`), createdAt: integer("created_at", { mode: "number" })
updatedAt: integer("updated_at", { mode: "number" }) .notNull()
.notNull() .default(sql`(unixepoch() * 1000)`),
.$onUpdate(() => Date.now()) updatedAt: integer("updated_at", { mode: "number" })
.default(sql`(unixepoch() * 1000)`), .notNull()
config: text("config", { mode: "json" }).$type<typeof volumeConfigSchema.inferOut>().notNull(), .$onUpdate(() => Date.now())
autoRemount: int("auto_remount", { mode: "boolean" }).notNull().default(true), .default(sql`(unixepoch() * 1000)`),
organizationId: text("organization_id") config: text("config", { mode: "json" }).$type<typeof volumeConfigSchema.inferOut>().notNull(),
.notNull() autoRemount: int("auto_remount", { mode: "boolean" }).notNull().default(true),
.references(() => organization.id, { onDelete: "cascade" }), organizationId: text("organization_id")
}, (table) => [unique().on(table.name, table.organizationId)]); .notNull()
.references(() => organization.id, { onDelete: "cascade" }),
},
(table) => [unique().on(table.name, table.organizationId)],
);
export type Volume = typeof volumesTable.$inferSelect; export type Volume = typeof volumesTable.$inferSelect;
export type VolumeInsert = typeof volumesTable.$inferInsert; export type VolumeInsert = typeof volumesTable.$inferInsert;
@ -381,102 +385,3 @@ export const twoFactor = sqliteTable(
}, },
(table) => [index("twoFactor_secret_idx").on(table.secret), index("twoFactor_userId_idx").on(table.userId)], (table) => [index("twoFactor_secret_idx").on(table.secret), index("twoFactor_userId_idx").on(table.userId)],
); );
export const userRelations = relations(usersTable, ({ many }) => ({
sessions: many(sessionsTable),
accounts: many(account),
members: many(member),
invitations: many(invitation),
twoFactors: many(twoFactor),
}));
export const sessionRelations = relations(sessionsTable, ({ one }) => ({
user: one(usersTable, {
fields: [sessionsTable.userId],
references: [usersTable.id],
}),
activeOrganization: one(organization, {
fields: [sessionsTable.activeOrganizationId],
references: [organization.id],
}),
}));
export const accountRelations = relations(account, ({ one }) => ({
user: one(usersTable, {
fields: [account.userId],
references: [usersTable.id],
}),
}));
export const organizationRelations = relations(organization, ({ many }) => ({
members: many(member),
invitations: many(invitation),
}));
export const memberRelations = relations(member, ({ one }) => ({
organization: one(organization, {
fields: [member.organizationId],
references: [organization.id],
}),
usersTable: one(usersTable, {
fields: [member.userId],
references: [usersTable.id],
}),
}));
export const invitationRelations = relations(invitation, ({ one }) => ({
organization: one(organization, {
fields: [invitation.organizationId],
references: [organization.id],
}),
usersTable: one(usersTable, {
fields: [invitation.inviterId],
references: [usersTable.id],
}),
}));
export const twoFactorRelations = relations(twoFactor, ({ one }) => ({
usersTable: one(usersTable, {
fields: [twoFactor.userId],
references: [usersTable.id],
}),
}));
export const backupScheduleMirrorRelations = relations(backupScheduleMirrorsTable, ({ one }) => ({
schedule: one(backupSchedulesTable, {
fields: [backupScheduleMirrorsTable.scheduleId],
references: [backupSchedulesTable.id],
}),
repository: one(repositoriesTable, {
fields: [backupScheduleMirrorsTable.repositoryId],
references: [repositoriesTable.id],
}),
}));
export const backupScheduleRelations = relations(backupSchedulesTable, ({ one, many }) => ({
volume: one(volumesTable, {
fields: [backupSchedulesTable.volumeId],
references: [volumesTable.id],
}),
repository: one(repositoriesTable, {
fields: [backupSchedulesTable.repositoryId],
references: [repositoriesTable.id],
}),
notifications: many(backupScheduleNotificationsTable),
mirrors: many(backupScheduleMirrorsTable),
}));
export const notificationDestinationRelations = relations(notificationDestinationsTable, ({ many }) => ({
schedules: many(backupScheduleNotificationsTable),
}));
export const backupScheduleNotificationRelations = relations(backupScheduleNotificationsTable, ({ one }) => ({
schedule: one(backupSchedulesTable, {
fields: [backupScheduleNotificationsTable.scheduleId],
references: [backupSchedulesTable.id],
}),
destination: one(notificationDestinationsTable, {
fields: [backupScheduleNotificationsTable.destinationId],
references: [notificationDestinationsTable.id],
}),
}));

View file

@ -2,8 +2,6 @@ import { Job } from "../core/scheduler";
import { volumeService } from "../modules/volumes/volume.service"; import { volumeService } from "../modules/volumes/volume.service";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { db } from "../db/db"; import { db } from "../db/db";
import { eq } from "drizzle-orm";
import { volumesTable } from "../db/schema";
import { withContext } from "../core/request-context"; import { withContext } from "../core/request-context";
export class VolumeAutoRemountJob extends Job { export class VolumeAutoRemountJob extends Job {
@ -11,7 +9,7 @@ export class VolumeAutoRemountJob extends Job {
logger.debug("Running auto-remount for all errored volumes..."); logger.debug("Running auto-remount for all errored volumes...");
const volumes = await db.query.volumesTable.findMany({ const volumes = await db.query.volumesTable.findMany({
where: eq(volumesTable.status, "error"), where: { status: "error" },
}); });
for (const volume of volumes) { for (const volume of volumes) {

View file

@ -2,8 +2,6 @@ import { Job } from "../core/scheduler";
import { volumeService } from "../modules/volumes/volume.service"; import { volumeService } from "../modules/volumes/volume.service";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { db } from "../db/db"; import { db } from "../db/db";
import { eq, or } from "drizzle-orm";
import { volumesTable } from "../db/schema";
import { withContext } from "../core/request-context"; import { withContext } from "../core/request-context";
export class VolumeHealthCheckJob extends Job { export class VolumeHealthCheckJob extends Job {
@ -11,7 +9,9 @@ export class VolumeHealthCheckJob extends Job {
logger.debug("Running health check for all volumes..."); logger.debug("Running health check for all volumes...");
const volumes = await db.query.volumesTable.findMany({ const volumes = await db.query.volumesTable.findMany({
where: or(eq(volumesTable.status, "mounted"), eq(volumesTable.status, "error")), where: {
OR: [{ status: "mounted" }, { status: "error" }],
},
}); });
for (const volume of volumes) { for (const volume of volumes) {

View file

@ -2,8 +2,6 @@ import { Job } from "../core/scheduler";
import { repositoriesService } from "../modules/repositories/repositories.service"; import { repositoriesService } from "../modules/repositories/repositories.service";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { db } from "../db/db"; import { db } from "../db/db";
import { eq, or } from "drizzle-orm";
import { repositoriesTable } from "../db/schema";
import { withContext } from "../core/request-context"; import { withContext } from "../core/request-context";
export class RepositoryHealthCheckJob extends Job { export class RepositoryHealthCheckJob extends Job {
@ -11,7 +9,7 @@ export class RepositoryHealthCheckJob extends Job {
logger.debug("Running health check for all repositories..."); logger.debug("Running health check for all repositories...");
const repositories = await db.query.repositoriesTable.findMany({ const repositories = await db.query.repositoriesTable.findMany({
where: or(eq(repositoriesTable.status, "healthy"), eq(repositoriesTable.status, "error")), where: { OR: [{ status: "healthy" }, { status: "error" }] },
}); });
for (const repository of repositories) { for (const repository of repositories) {

View file

@ -1,8 +1,6 @@
import { createMiddleware } from "hono/factory"; import { createMiddleware } from "hono/factory";
import { auth } from "~/lib/auth"; import { auth } from "~/lib/auth";
import { db } from "~/server/db/db"; import { db } from "~/server/db/db";
import { member } from "~/server/db/schema";
import { eq, and } from "drizzle-orm";
import { withContext } from "~/server/core/request-context"; import { withContext } from "~/server/core/request-context";
declare module "hono" { declare module "hono" {
@ -34,7 +32,9 @@ export const requireAuth = createMiddleware(async (c, next) => {
} }
const membership = await db.query.member.findFirst({ const membership = await db.query.member.findFirst({
where: and(eq(member.userId, user.id), eq(member.organizationId, activeOrganizationId)), where: {
AND: [{ userId: user.id }, { organizationId: activeOrganizationId }],
},
}); });
if (!membership) { if (!membership) {
@ -58,7 +58,9 @@ export const requireOrgAdmin = createMiddleware(async (c, next) => {
const organizationId = c.get("organizationId"); const organizationId = c.get("organizationId");
const membership = await db.query.member.findFirst({ const membership = await db.query.member.findFirst({
where: and(eq(member.userId, user.id), eq(member.organizationId, organizationId)), where: {
AND: [{ userId: user.id }, { organizationId: organizationId }],
},
}); });
if (!membership || (membership.role !== "owner" && membership.role !== "admin")) { if (!membership || (membership.role !== "owner" && membership.role !== "admin")) {

View file

@ -24,7 +24,9 @@ export class AuthService {
*/ */
async getUserDeletionImpact(userId: string): Promise<UserDeletionImpactDto> { async getUserDeletionImpact(userId: string): Promise<UserDeletionImpactDto> {
const userMemberships = await db.query.member.findMany({ const userMemberships = await db.query.member.findMany({
where: and(eq(member.userId, userId), eq(member.role, "owner")), where: {
AND: [{ userId: userId }, { role: "owner" }],
},
}); });
const impacts: UserDeletionImpactDto["organizations"] = []; const impacts: UserDeletionImpactDto["organizations"] = [];
@ -43,7 +45,7 @@ export class AuthService {
if (otherOwners[0].count === 0) { if (otherOwners[0].count === 0) {
const org = await db.query.organization.findFirst({ const org = await db.query.organization.findFirst({
where: eq(organization.id, membership.organizationId), where: { id: membership.organizationId },
}); });
if (org) { if (org) {

View file

@ -1,7 +1,5 @@
import { verifyPassword } from "better-auth/crypto"; import { verifyPassword } from "better-auth/crypto";
import { eq } from "drizzle-orm";
import { db } from "~/server/db/db"; import { db } from "~/server/db/db";
import { account } from "~/server/db/schema";
type PasswordVerificationBody = { type PasswordVerificationBody = {
userId: string; userId: string;
@ -10,7 +8,7 @@ type PasswordVerificationBody = {
export const verifyUserPassword = async ({ password, userId }: PasswordVerificationBody) => { export const verifyUserPassword = async ({ password, userId }: PasswordVerificationBody) => {
const userAccount = await db.query.account.findFirst({ const userAccount = await db.query.account.findFirst({
where: eq(account.userId, userId), where: { userId },
}); });
if (!userAccount || !userAccount.password) { if (!userAccount || !userAccount.password) {

View file

@ -1,9 +1,9 @@
import { and, asc, eq, isNull, ne, or } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import cron from "node-cron"; import cron from "node-cron";
import { CronExpressionParser } from "cron-parser"; import { CronExpressionParser } from "cron-parser";
import { NotFoundError, BadRequestError, ConflictError } from "http-errors-enhanced"; import { NotFoundError, BadRequestError, ConflictError } from "http-errors-enhanced";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { backupSchedulesTable, backupScheduleMirrorsTable, repositoriesTable, volumesTable } from "../../db/schema"; import { backupSchedulesTable, backupScheduleMirrorsTable, repositoriesTable } from "../../db/schema";
import { restic } from "../../utils/restic"; import { restic } from "../../utils/restic";
import { logger } from "../../utils/logger"; import { logger } from "../../utils/logger";
import { cache } from "../../utils/cache"; import { cache } from "../../utils/cache";
@ -60,12 +60,9 @@ const processPattern = (pattern: string, volumePath: string) => {
const listSchedules = async () => { const listSchedules = async () => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedules = await db.query.backupSchedulesTable.findMany({ const schedules = await db.query.backupSchedulesTable.findMany({
where: eq(backupSchedulesTable.organizationId, organizationId), where: { organizationId },
with: { with: { volume: true, repository: true },
volume: true, orderBy: { sortOrder: "asc", id: "asc" },
repository: true,
},
orderBy: [asc(backupSchedulesTable.sortOrder), asc(backupSchedulesTable.id)],
}); });
return schedules; return schedules;
}; };
@ -73,11 +70,8 @@ const listSchedules = async () => {
const getSchedule = async (scheduleId: number) => { const getSchedule = async (scheduleId: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
with: { with: { volume: true, repository: true },
volume: true,
repository: true,
},
}); });
if (!schedule) { if (!schedule) {
@ -94,7 +88,9 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
} }
const existingName = await db.query.backupSchedulesTable.findFirst({ const existingName = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.name, data.name), eq(backupSchedulesTable.organizationId, organizationId)), where: {
AND: [{ name: data.name }, { organizationId }],
},
}); });
if (existingName) { if (existingName) {
@ -102,7 +98,9 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
} }
const volume = await db.query.volumesTable.findFirst({ const volume = await db.query.volumesTable.findFirst({
where: and(eq(volumesTable.id, data.volumeId), eq(volumesTable.organizationId, organizationId)), where: {
AND: [{ id: data.volumeId }, { organizationId }],
},
}); });
if (!volume) { if (!volume) {
@ -110,7 +108,9 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
} }
const repository = await db.query.repositoriesTable.findFirst({ const repository = await db.query.repositoriesTable.findFirst({
where: and(eq(repositoriesTable.id, data.repositoryId), eq(repositoriesTable.organizationId, organizationId)), where: {
AND: [{ id: data.repositoryId }, { organizationId }],
},
}); });
if (!repository) { if (!repository) {
@ -148,7 +148,9 @@ const createSchedule = async (data: CreateBackupScheduleBody) => {
const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody) => { const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: {
AND: [{ id: scheduleId }, { organizationId }],
},
}); });
if (!schedule) { if (!schedule) {
@ -161,11 +163,9 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
if (data.name) { if (data.name) {
const existingName = await db.query.backupSchedulesTable.findFirst({ const existingName = await db.query.backupSchedulesTable.findFirst({
where: and( where: {
eq(backupSchedulesTable.name, data.name), AND: [{ name: data.name }, { NOT: { id: scheduleId } }, { organizationId }],
ne(backupSchedulesTable.id, scheduleId), },
eq(backupSchedulesTable.organizationId, organizationId),
),
}); });
if (existingName) { if (existingName) {
@ -174,7 +174,9 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
} }
const repository = await db.query.repositoriesTable.findFirst({ const repository = await db.query.repositoriesTable.findFirst({
where: and(eq(repositoriesTable.id, data.repositoryId), eq(repositoriesTable.organizationId, organizationId)), where: {
AND: [{ id: data.repositoryId }, { organizationId }],
},
}); });
if (!repository) { if (!repository) {
@ -200,7 +202,9 @@ const updateSchedule = async (scheduleId: number, data: UpdateBackupScheduleBody
const deleteSchedule = async (scheduleId: number) => { const deleteSchedule = async (scheduleId: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: {
AND: [{ id: scheduleId }, { organizationId }],
},
}); });
if (!schedule) { if (!schedule) {
@ -215,7 +219,7 @@ const deleteSchedule = async (scheduleId: number) => {
const executeBackup = async (scheduleId: number, manual = false) => { const executeBackup = async (scheduleId: number, manual = false) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
}); });
if (!schedule) { if (!schedule) {
@ -233,7 +237,7 @@ const executeBackup = async (scheduleId: number, manual = false) => {
} }
const volume = await db.query.volumesTable.findFirst({ const volume = await db.query.volumesTable.findFirst({
where: and(eq(volumesTable.id, schedule.volumeId), eq(volumesTable.organizationId, organizationId)), where: { AND: [{ id: schedule.volumeId }, { organizationId }] },
}); });
if (!volume) { if (!volume) {
@ -241,7 +245,7 @@ const executeBackup = async (scheduleId: number, manual = false) => {
} }
const repository = await db.query.repositoriesTable.findFirst({ const repository = await db.query.repositoriesTable.findFirst({
where: and(eq(repositoriesTable.id, schedule.repositoryId), eq(repositoriesTable.organizationId, organizationId)), where: { AND: [{ id: schedule.repositoryId }, { organizationId }] },
}); });
if (!repository) { if (!repository) {
@ -436,11 +440,9 @@ const getSchedulesToExecute = async () => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const now = Date.now(); const now = Date.now();
const schedules = await db.query.backupSchedulesTable.findMany({ const schedules = await db.query.backupSchedulesTable.findMany({
where: and( where: {
eq(backupSchedulesTable.enabled, true), AND: [{ enabled: true }, { lastBackupStatus: { NOT: "in_progress" } }, { organizationId }],
or(ne(backupSchedulesTable.lastBackupStatus, "in_progress"), isNull(backupSchedulesTable.lastBackupStatus)), },
eq(backupSchedulesTable.organizationId, organizationId),
),
}); });
const schedulesToRun: number[] = []; const schedulesToRun: number[] = [];
@ -457,7 +459,7 @@ const getSchedulesToExecute = async () => {
const getScheduleForVolume = async (volumeId: number) => { const getScheduleForVolume = async (volumeId: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.volumeId, volumeId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ volumeId }, { organizationId }] },
with: { volume: true, repository: true }, with: { volume: true, repository: true },
}); });
@ -467,7 +469,7 @@ const getScheduleForVolume = async (volumeId: number) => {
const stopBackup = async (scheduleId: number) => { const stopBackup = async (scheduleId: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
}); });
if (!schedule) { if (!schedule) {
@ -498,7 +500,9 @@ const stopBackup = async (scheduleId: number) => {
const runForget = async (scheduleId: number, repositoryId?: string) => { const runForget = async (scheduleId: number, repositoryId?: string) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: {
AND: [{ id: scheduleId }, { organizationId }],
},
}); });
if (!schedule) { if (!schedule) {
@ -510,10 +514,9 @@ const runForget = async (scheduleId: number, repositoryId?: string) => {
} }
const repository = await db.query.repositoriesTable.findFirst({ const repository = await db.query.repositoriesTable.findFirst({
where: and( where: {
eq(repositoriesTable.id, repositoryId ?? schedule.repositoryId), AND: [{ id: repositoryId ?? schedule.repositoryId }, { organizationId }],
eq(repositoriesTable.organizationId, organizationId), },
),
}); });
if (!repository) { if (!repository) {
@ -535,7 +538,9 @@ const runForget = async (scheduleId: number, repositoryId?: string) => {
const getMirrors = async (scheduleId: number) => { const getMirrors = async (scheduleId: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: {
AND: [{ id: scheduleId }, { organizationId }],
},
}); });
if (!schedule) { if (!schedule) {
@ -543,7 +548,9 @@ const getMirrors = async (scheduleId: number) => {
} }
const mirrors = await db.query.backupScheduleMirrorsTable.findMany({ const mirrors = await db.query.backupScheduleMirrorsTable.findMany({
where: eq(backupScheduleMirrorsTable.scheduleId, scheduleId), where: {
scheduleId,
},
with: { repository: true }, with: { repository: true },
}); });
@ -553,7 +560,7 @@ const getMirrors = async (scheduleId: number) => {
const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody) => { const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
with: { repository: true }, with: { repository: true },
}); });
@ -567,7 +574,7 @@ const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody
} }
const repo = await db.query.repositoriesTable.findFirst({ const repo = await db.query.repositoriesTable.findFirst({
where: and(eq(repositoriesTable.id, mirror.repositoryId), eq(repositoriesTable.organizationId, organizationId)), where: { AND: [{ id: mirror.repositoryId }, { organizationId }] },
}); });
if (!repo) { if (!repo) {
@ -584,7 +591,7 @@ const updateMirrors = async (scheduleId: number, data: UpdateScheduleMirrorsBody
} }
const existingMirrors = await db.query.backupScheduleMirrorsTable.findMany({ const existingMirrors = await db.query.backupScheduleMirrorsTable.findMany({
where: eq(backupScheduleMirrorsTable.scheduleId, scheduleId), where: { scheduleId },
}); });
const existingMirrorsMap = new Map( const existingMirrorsMap = new Map(
@ -622,7 +629,7 @@ const copyToMirrors = async (
) => { ) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
}); });
if (!schedule) { if (!schedule) {
@ -630,7 +637,7 @@ const copyToMirrors = async (
} }
const mirrors = await db.query.backupScheduleMirrorsTable.findMany({ const mirrors = await db.query.backupScheduleMirrorsTable.findMany({
where: eq(backupScheduleMirrorsTable.scheduleId, scheduleId), where: { scheduleId },
with: { repository: true }, with: { repository: true },
}); });
@ -712,7 +719,7 @@ const copyToMirrors = async (
const getMirrorCompatibility = async (scheduleId: number) => { const getMirrorCompatibility = async (scheduleId: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
with: { repository: true }, with: { repository: true },
}); });
@ -720,9 +727,7 @@ const getMirrorCompatibility = async (scheduleId: number) => {
throw new NotFoundError("Backup schedule not found"); throw new NotFoundError("Backup schedule not found");
} }
const allRepositories = await db.query.repositoriesTable.findMany({ const allRepositories = await db.query.repositoriesTable.findMany({ where: { organizationId } });
where: eq(repositoriesTable.organizationId, organizationId),
});
const repos = allRepositories.filter((repo) => repo.id !== schedule.repositoryId); const repos = allRepositories.filter((repo) => repo.id !== schedule.repositoryId);
const compatibility = await Promise.all( const compatibility = await Promise.all(
@ -740,7 +745,7 @@ const reorderSchedules = async (scheduleIds: number[]) => {
} }
const existingSchedules = await db.query.backupSchedulesTable.findMany({ const existingSchedules = await db.query.backupSchedulesTable.findMany({
where: eq(backupSchedulesTable.organizationId, organizationId), where: { organizationId },
columns: { id: true }, columns: { id: true },
}); });
const existingIds = new Set(existingSchedules.map((s) => s.id)); const existingIds = new Set(existingSchedules.map((s) => s.id));

View file

@ -3,7 +3,6 @@ import { v00001 } from "./migrations/00001-retag-snapshots";
import { v00002 } from "./migrations/00002-isolate-restic-passwords"; import { v00002 } from "./migrations/00002-isolate-restic-passwords";
import { v00003 } from "./migrations/00003-assign-organization"; import { v00003 } from "./migrations/00003-assign-organization";
import { sql } from "drizzle-orm"; import { sql } from "drizzle-orm";
import { eq } from "drizzle-orm";
import { appMetadataTable, usersTable } from "../../db/schema"; import { appMetadataTable, usersTable } from "../../db/schema";
import { db } from "../../db/db"; import { db } from "../../db/db";
@ -26,9 +25,7 @@ const recordMigrationCheckpoint = async (version: string): Promise<void> => {
const hasMigrationCheckpoint = async (id: string): Promise<boolean> => { const hasMigrationCheckpoint = async (id: string): Promise<boolean> => {
const key = `${MIGRATION_KEY_PREFIX}${id}`; const key = `${MIGRATION_KEY_PREFIX}${id}`;
const result = await db.query.appMetadataTable.findFirst({ const result = await db.query.appMetadataTable.findFirst({ where: { key } });
where: eq(appMetadataTable.key, key),
});
return result !== undefined; return result !== undefined;
}; };

View file

@ -1,6 +1,6 @@
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { db } from "../../../db/db"; import { db } from "../../../db/db";
import { backupScheduleMirrorsTable, repositoriesTable, type Repository } from "../../../db/schema"; import { backupScheduleMirrorsTable, type Repository } from "../../../db/schema";
import { logger } from "../../../utils/logger"; import { logger } from "../../../utils/logger";
import { toMessage } from "~/server/utils/errors"; import { toMessage } from "~/server/utils/errors";
import { exec } from "~/server/utils/spawn"; import { exec } from "~/server/utils/spawn";
@ -42,7 +42,7 @@ const execute = async () => {
const newTag = schedule.shortId; const newTag = schedule.shortId;
const repository = await db.query.repositoriesTable.findFirst({ const repository = await db.query.repositoriesTable.findFirst({
where: eq(repositoriesTable.id, schedule.repositoryId), where: { id: schedule.repositoryId },
}); });
if (!repository) { if (!repository) {
@ -63,7 +63,7 @@ const execute = async () => {
for (const mirror of mirrors) { for (const mirror of mirrors) {
const mirrorRepo = await db.query.repositoriesTable.findFirst({ const mirrorRepo = await db.query.repositoriesTable.findFirst({
where: eq(repositoriesTable.id, mirror.repositoryId), where: { id: mirror.repositoryId },
}); });
if (!mirrorRepo) { if (!mirrorRepo) {

View file

@ -1,6 +1,5 @@
import { eq } from "drizzle-orm";
import { db } from "../../../db/db"; import { db } from "../../../db/db";
import { member, usersTable } from "../../../db/schema"; import { member } from "../../../db/schema";
import { logger } from "../../../utils/logger"; import { logger } from "../../../utils/logger";
import { toMessage } from "~/server/utils/errors"; import { toMessage } from "~/server/utils/errors";
@ -8,7 +7,9 @@ const execute = async () => {
const errors: Array<{ name: string; error: string }> = []; const errors: Array<{ name: string; error: string }> = [];
try { try {
const allUsers = await db.query.usersTable.findMany({ where: eq(usersTable.role, "admin") }); const allUsers = await db.query.usersTable.findMany({
where: { role: "admin" },
});
const allOrganizations = await db.query.organization.findMany({}); const allOrganizations = await db.query.organization.findMany({});
if (allUsers.length === 0) { if (allUsers.length === 0) {
@ -35,7 +36,7 @@ const execute = async () => {
const org = allOrganizations[0]; const org = allOrganizations[0];
const existingMembers = await db.query.member.findMany({ const existingMembers = await db.query.member.findMany({
where: (memberTable, { eq }) => eq(memberTable.userId, user.id), where: { userId: user.id },
}); });
if (existingMembers.length > 0) { if (existingMembers.length > 0) {

View file

@ -1,7 +1,5 @@
import { Scheduler } from "../../core/scheduler"; import { Scheduler } from "../../core/scheduler";
import { eq, or } from "drizzle-orm";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { volumesTable } from "../../db/schema";
import { logger } from "../../utils/logger"; import { logger } from "../../utils/logger";
import { createVolumeBackend } from "../backends/backend"; import { createVolumeBackend } from "../backends/backend";
@ -9,7 +7,7 @@ export const shutdown = async () => {
await Scheduler.stop(); await Scheduler.stop();
const volumes = await db.query.volumesTable.findMany({ const volumes = await db.query.volumesTable.findMany({
where: or(eq(volumesTable.status, "mounted")), where: { status: "mounted" },
}); });
for (const volume of volumes) { for (const volume of volumes) {

View file

@ -1,7 +1,7 @@
import { Scheduler } from "../../core/scheduler"; import { Scheduler } from "../../core/scheduler";
import { and, eq, or } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { backupSchedulesTable, volumesTable } from "../../db/schema"; import { backupSchedulesTable } from "../../db/schema";
import { logger } from "../../utils/logger"; import { logger } from "../../utils/logger";
import { volumeService } from "../volumes/volume.service"; import { volumeService } from "../volumes/volume.service";
import { CleanupDanglingMountsJob } from "../../jobs/cleanup-dangling"; import { CleanupDanglingMountsJob } from "../../jobs/cleanup-dangling";
@ -62,10 +62,14 @@ export const startup = async () => {
await ensureLatestConfigurationSchema(); await ensureLatestConfigurationSchema();
const volumes = await db.query.volumesTable.findMany({ const volumes = await db.query.volumesTable.findMany({
where: or( where: {
eq(volumesTable.status, "mounted"), OR: [
and(eq(volumesTable.autoRemount, true), eq(volumesTable.status, "error")), { status: "mounted" },
), {
AND: [{ autoRemount: true }, { status: "error" }],
},
],
},
}); });
for (const volume of volumes) { for (const volume of volumes) {

View file

@ -1,10 +1,9 @@
import { eq, and, inArray } from "drizzle-orm"; import { eq, and } from "drizzle-orm";
import { BadRequestError, ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced"; import { BadRequestError, ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { import {
notificationDestinationsTable, notificationDestinationsTable,
backupScheduleNotificationsTable, backupScheduleNotificationsTable,
backupSchedulesTable,
type NotificationDestination, type NotificationDestination,
} from "../../db/schema"; } from "../../db/schema";
import { cryptoUtils } from "../../utils/crypto"; import { cryptoUtils } from "../../utils/crypto";
@ -19,8 +18,8 @@ import { getOrganizationId } from "~/server/core/request-context";
const listDestinations = async () => { const listDestinations = async () => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const destinations = await db.query.notificationDestinationsTable.findMany({ const destinations = await db.query.notificationDestinationsTable.findMany({
where: eq(notificationDestinationsTable.organizationId, organizationId), where: { organizationId },
orderBy: (destinations, { asc }) => [asc(destinations.name)], orderBy: { name: "asc" },
}); });
return destinations; return destinations;
}; };
@ -28,10 +27,7 @@ const listDestinations = async () => {
const getDestination = async (id: number) => { const getDestination = async (id: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const destination = await db.query.notificationDestinationsTable.findFirst({ const destination = await db.query.notificationDestinationsTable.findFirst({
where: and( where: { AND: [{ id }, { organizationId }] },
eq(notificationDestinationsTable.id, id),
eq(notificationDestinationsTable.organizationId, organizationId),
),
}); });
if (!destination) { if (!destination) {
@ -256,7 +252,7 @@ const testDestination = async (id: number) => {
const getScheduleNotifications = async (scheduleId: number) => { const getScheduleNotifications = async (scheduleId: number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
}); });
if (!schedule) { if (!schedule) {
@ -264,7 +260,7 @@ const getScheduleNotifications = async (scheduleId: number) => {
} }
const assignments = await db.query.backupScheduleNotificationsTable.findMany({ const assignments = await db.query.backupScheduleNotificationsTable.findMany({
where: eq(backupScheduleNotificationsTable.scheduleId, scheduleId), where: { scheduleId },
with: { with: {
destination: true, destination: true,
}, },
@ -285,7 +281,7 @@ const updateScheduleNotifications = async (
) => { ) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const schedule = await db.query.backupSchedulesTable.findFirst({ const schedule = await db.query.backupSchedulesTable.findFirst({
where: and(eq(backupSchedulesTable.id, scheduleId), eq(backupSchedulesTable.organizationId, organizationId)), where: { AND: [{ id: scheduleId }, { organizationId }] },
}); });
if (!schedule) { if (!schedule) {
@ -295,10 +291,9 @@ const updateScheduleNotifications = async (
const destinationIds = [...new Set(assignments.map((a) => a.destinationId))]; const destinationIds = [...new Set(assignments.map((a) => a.destinationId))];
if (destinationIds.length > 0) { if (destinationIds.length > 0) {
const destinations = await db.query.notificationDestinationsTable.findMany({ const destinations = await db.query.notificationDestinationsTable.findMany({
where: and( where: {
inArray(notificationDestinationsTable.id, destinationIds), AND: [{ id: { in: destinationIds } }, { organizationId }],
eq(notificationDestinationsTable.organizationId, organizationId), },
),
}); });
if (destinations.length !== destinationIds.length) { if (destinations.length !== destinationIds.length) {
@ -338,7 +333,7 @@ const sendBackupNotification = async (
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const assignments = await db.query.backupScheduleNotificationsTable.findMany({ const assignments = await db.query.backupScheduleNotificationsTable.findMany({
where: eq(backupScheduleNotificationsTable.scheduleId, scheduleId), where: { scheduleId },
with: { with: {
destination: true, destination: true,
}, },

View file

@ -1,5 +1,5 @@
import crypto from "node:crypto"; import crypto from "node:crypto";
import { and, eq, or } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import { ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced"; import { ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { repositoriesTable } from "../../db/schema"; import { repositoriesTable } from "../../db/schema";
@ -26,18 +26,15 @@ const runningDoctors = new Map<string, AbortController>();
const findRepository = async (idOrShortId: string) => { const findRepository = async (idOrShortId: string) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
return await db.query.repositoriesTable.findFirst({ return await db.query.repositoriesTable.findFirst({
where: and( where: {
or(eq(repositoriesTable.id, idOrShortId), eq(repositoriesTable.shortId, idOrShortId)), AND: [{ OR: [{ id: idOrShortId }, { shortId: idOrShortId }] }, { organizationId }],
eq(repositoriesTable.organizationId, organizationId), },
),
}); });
}; };
const listRepositories = async () => { const listRepositories = async () => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const repositories = await db.query.repositoriesTable.findMany({ const repositories = await db.query.repositoriesTable.findMany({ where: { organizationId } });
where: eq(repositoriesTable.organizationId, organizationId),
});
return repositories; return repositories;
}; };

View file

@ -15,7 +15,7 @@ import {
import { systemService } from "./system.service"; import { systemService } from "./system.service";
import { requireAuth, requireOrgAdmin } from "../auth/auth.middleware"; import { requireAuth, requireOrgAdmin } from "../auth/auth.middleware";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { organization, usersTable } from "../../db/schema"; import { usersTable } from "../../db/schema";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { verifyUserPassword } from "../auth/helpers"; import { verifyUserPassword } from "../auth/helpers";
import { cryptoUtils } from "../../utils/crypto"; import { cryptoUtils } from "../../utils/crypto";
@ -78,9 +78,7 @@ export const systemController = new Hono()
} }
try { try {
const org = await db.query.organization.findFirst({ const org = await db.query.organization.findFirst({ where: { id: organizationId } });
where: eq(organization.id, organizationId),
});
if (!org?.metadata?.resticPassword) { if (!org?.metadata?.resticPassword) {
return c.json({ message: "Organization Restic password not found" }, 404); return c.json({ message: "Organization Restic password not found" }, 404);

View file

@ -6,7 +6,6 @@ import { cache } from "../../utils/cache";
import { logger } from "~/server/utils/logger"; import { logger } from "~/server/utils/logger";
import { db } from "../../db/db"; import { db } from "../../db/db";
import { appMetadataTable } from "../../db/schema"; import { appMetadataTable } from "../../db/schema";
import { eq } from "drizzle-orm";
import { REGISTRATION_ENABLED_KEY } from "~/client/lib/constants"; import { REGISTRATION_ENABLED_KEY } from "~/client/lib/constants";
const CACHE_TTL = 60 * 60; const CACHE_TTL = 60 * 60;
@ -91,7 +90,7 @@ const getUpdates = async (): Promise<UpdateInfoDto> => {
const isRegistrationEnabled = async () => { const isRegistrationEnabled = async () => {
const result = await db.query.appMetadataTable.findFirst({ const result = await db.query.appMetadataTable.findFirst({
where: eq(appMetadataTable.key, REGISTRATION_ENABLED_KEY), where: { key: REGISTRATION_ENABLED_KEY },
}); });
return result?.value === "true"; return result?.value === "true";

View file

@ -46,7 +46,7 @@ async function encryptSensitiveFields(config: BackendConfig): Promise<BackendCon
const listVolumes = async () => { const listVolumes = async () => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const volumes = await db.query.volumesTable.findMany({ const volumes = await db.query.volumesTable.findMany({
where: eq(volumesTable.organizationId, organizationId), where: { organizationId: organizationId },
}); });
return volumes; return volumes;
@ -56,10 +56,12 @@ const findVolume = async (idOrShortId: string | number) => {
const organizationId = getOrganizationId(); const organizationId = getOrganizationId();
const isNumeric = typeof idOrShortId === "number" || /^\d+$/.test(String(idOrShortId)); const isNumeric = typeof idOrShortId === "number" || /^\d+$/.test(String(idOrShortId));
return await db.query.volumesTable.findFirst({ return await db.query.volumesTable.findFirst({
where: and( where: {
isNumeric ? eq(volumesTable.id, Number(idOrShortId)) : eq(volumesTable.shortId, idOrShortId), AND: [
eq(volumesTable.organizationId, organizationId), isNumeric ? { id: Number(idOrShortId) } : { shortId: String(idOrShortId) },
), { organizationId: organizationId },
],
},
}); });
}; };

View file

@ -4,7 +4,6 @@ import path from "node:path";
import os from "node:os"; import os from "node:os";
import { throttle } from "es-toolkit"; import { throttle } from "es-toolkit";
import { type } from "arktype"; import { type } from "arktype";
import { eq } from "drizzle-orm";
import { REPOSITORY_BASE, RESTIC_PASS_FILE, DEFAULT_EXCLUDES, RESTIC_CACHE_DIR } from "../core/constants"; import { REPOSITORY_BASE, RESTIC_PASS_FILE, DEFAULT_EXCLUDES, RESTIC_CACHE_DIR } from "../core/constants";
import { config as appConfig } from "../core/config"; import { config as appConfig } from "../core/config";
import { logger } from "./logger"; import { logger } from "./logger";
@ -14,7 +13,6 @@ import { safeSpawn, exec } from "./spawn";
import type { CompressionMode, RepositoryConfig, OverwriteMode, BandwidthLimit } from "~/schemas/restic"; import type { CompressionMode, RepositoryConfig, OverwriteMode, BandwidthLimit } from "~/schemas/restic";
import { ResticError } from "./errors"; import { ResticError } from "./errors";
import { db } from "../db/db"; import { db } from "../db/db";
import { organization } from "../db/schema";
const backupOutputSchema = type({ const backupOutputSchema = type({
message_type: "'summary'", message_type: "'summary'",
@ -110,9 +108,7 @@ export const buildEnv = async (config: RepositoryConfig, organizationId: string)
await fs.writeFile(passwordFilePath, decryptedPassword, { mode: 0o600 }); await fs.writeFile(passwordFilePath, decryptedPassword, { mode: 0o600 });
env.RESTIC_PASSWORD_FILE = passwordFilePath; env.RESTIC_PASSWORD_FILE = passwordFilePath;
} else { } else {
const org = await db.query.organization.findFirst({ const org = await db.query.organization.findFirst({ where: { id: organizationId } });
where: eq(organization.id, organizationId),
});
if (!org) { if (!org) {
throw new Error(`Organization ${organizationId} not found`); throw new Error(`Organization ${organizationId} not found`);

View file

@ -19,7 +19,7 @@ export const createTestOrganization = async (overrides: Partial<typeof organizat
}; };
const existing = await db.query.organization.findFirst({ const existing = await db.query.organization.findFirst({
where: (o, { eq }) => eq(o.id, org.id ?? TEST_ORG_ID), where: { id: org.id },
}); });
if (existing) { if (existing) {

View file

@ -2,11 +2,11 @@ import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql"; import { drizzle } from "drizzle-orm/libsql";
import path from "node:path"; import path from "node:path";
import { DATABASE_URL } from "~/server/core/constants"; import { DATABASE_URL } from "~/server/core/constants";
import * as schema from "~/server/db/schema"; import { relations } from "~/server/db/relations";
const sqlite = createClient({ url: `file:${path.join(process.cwd(), "playwright", DATABASE_URL)}` }); const sqlite = createClient({ url: `file:${path.join(process.cwd(), "playwright", DATABASE_URL)}` });
export const db = drizzle({ client: sqlite, schema: schema }); export const db = drizzle({ client: sqlite, relations: relations });
export const resetDatabase = async () => { export const resetDatabase = async () => {
const cursor = await sqlite.execute("SELECT name FROM sqlite_master WHERE type='table'"); const cursor = await sqlite.execute("SELECT name FROM sqlite_master WHERE type='table'");