mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 22:55:24 +00:00
feat: convert file manager into a plugin
This commit is contained in:
1 parent
b76da9ab52
commit
c85c1fdb86
259 files changed
+54155
-16791
No files matched your search
@@ -14,7 +14,6 @@ import { CommandHistoryRepository } from "./command-history-repository.js";
|
||||
import { CredentialRepository } from "./credential-repository.js";
|
||||
import { DashboardServiceLinkRepository } from "./dashboard-service-link-repository.js";
|
||||
import { DismissedAlertRepository } from "./dismissed-alert-repository.js";
|
||||
import { FileManagerBookmarkRepository } from "./file-manager-bookmark-repository.js";
|
||||
import { HomepageItemRepository } from "./homepage-item-repository.js";
|
||||
import { HomepageLayoutRepository } from "./homepage-layout-repository.js";
|
||||
import { HostFolderRepository } from "./host-folder-repository.js";
|
||||
@@ -57,7 +56,6 @@ import { SsoProviderRepository } from "./sso-provider-repository.js";
|
||||
import { TermixIdentityCaRepository } from "./termix-identity-ca-repository.js";
|
||||
import { TermixIdentityRepository } from "./termix-identity-repository.js";
|
||||
import { TmuxSessionTagRepository } from "./tmux-session-tag-repository.js";
|
||||
import { TransferRecentRepository } from "./transfer-recent-repository.js";
|
||||
import { TrustedDeviceRepository } from "./trusted-device-repository.js";
|
||||
import { UserDataExportRepository } from "./user-data-export-repository.js";
|
||||
import { UserPreferenceRepository } from "./user-preference-repository.js";
|
||||
@@ -229,12 +227,7 @@ export function createCurrentDismissedAlertRepository(): DismissedAlertRepositor
|
||||
);
|
||||
}
|
||||
|
||||
export function createCurrentFileManagerBookmarkRepository(): FileManagerBookmarkRepository {
|
||||
return new FileManagerBookmarkRepository(
|
||||
createCurrentRepositoryContext(),
|
||||
createCurrentRepositoryWriteHook("file_manager_bookmarks_repository_write"),
|
||||
);
|
||||
}
|
||||
// File manager recent/pinned/shortcuts moved to the file-manager plugin.
|
||||
|
||||
export function createCurrentHomepageItemRepository(): HomepageItemRepository {
|
||||
return new HomepageItemRepository(
|
||||
@@ -512,12 +505,7 @@ export function createCurrentTmuxSessionTagRepository(): TmuxSessionTagRepositor
|
||||
);
|
||||
}
|
||||
|
||||
export function createCurrentTransferRecentRepository(): TransferRecentRepository {
|
||||
return new TransferRecentRepository(
|
||||
createCurrentRepositoryContext(),
|
||||
createCurrentRepositoryWriteHook("transfer_recent_repository_write"),
|
||||
);
|
||||
}
|
||||
// transfer_recent moved to the file-manager plugin.
|
||||
|
||||
export function createCurrentTrustedDeviceRepository(): TrustedDeviceRepository {
|
||||
return new TrustedDeviceRepository(
|
||||
|
||||
@@ -1,522 +0,0 @@
|
||||
import { and, desc, eq, inArray } from "drizzle-orm";
|
||||
import {
|
||||
fileManagerPinned,
|
||||
fileManagerRecent,
|
||||
fileManagerShortcuts,
|
||||
} from "../db/schema.js";
|
||||
import type { DatabaseContext } from "./database-context.js";
|
||||
import { rowsAffected } from "./mutation-result.js";
|
||||
|
||||
export type FileManagerRecentRecord = typeof fileManagerRecent.$inferSelect;
|
||||
export type FileManagerPinnedRecord = typeof fileManagerPinned.$inferSelect;
|
||||
export type FileManagerShortcutRecord =
|
||||
typeof fileManagerShortcuts.$inferSelect;
|
||||
|
||||
export interface FileManagerBookmarkInput {
|
||||
hostId: number;
|
||||
path: string;
|
||||
name?: string | null;
|
||||
}
|
||||
|
||||
function resolveBookmarkName(path: string, name?: string | null): string {
|
||||
return name || path.split("/").pop() || "Unknown";
|
||||
}
|
||||
|
||||
export class FileManagerBookmarkRepository {
|
||||
constructor(
|
||||
private readonly context: DatabaseContext,
|
||||
private readonly onWrite?: () => void | Promise<void>,
|
||||
) {}
|
||||
|
||||
async listRecentForHost(
|
||||
userId: string,
|
||||
hostId: number,
|
||||
limit = 20,
|
||||
): Promise<FileManagerRecentRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(fileManagerRecent)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerRecent.userId, userId),
|
||||
eq(fileManagerRecent.hostId, hostId),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(fileManagerRecent.lastOpened))
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
async listRecentByUserId(userId: string): Promise<FileManagerRecentRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(fileManagerRecent)
|
||||
.where(eq(fileManagerRecent.userId, userId));
|
||||
}
|
||||
|
||||
async upsertRecent(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
lastOpened = new Date().toISOString(),
|
||||
): Promise<void> {
|
||||
const [existing] = await this.context.drizzle
|
||||
.select({ id: fileManagerRecent.id })
|
||||
.from(fileManagerRecent)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerRecent.userId, userId),
|
||||
eq(fileManagerRecent.hostId, input.hostId),
|
||||
eq(fileManagerRecent.path, input.path),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existing) {
|
||||
await this.context.drizzle
|
||||
.update(fileManagerRecent)
|
||||
.set({ lastOpened })
|
||||
.where(eq(fileManagerRecent.id, existing.id));
|
||||
} else {
|
||||
await this.context.drizzle.insert(fileManagerRecent).values({
|
||||
userId,
|
||||
hostId: input.hostId,
|
||||
path: input.path,
|
||||
name: resolveBookmarkName(input.path, input.name),
|
||||
lastOpened,
|
||||
});
|
||||
}
|
||||
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
async createRecentForImport(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
lastOpened = new Date().toISOString(),
|
||||
): Promise<boolean> {
|
||||
const exists = await this.existsRecentImportItem(userId, input);
|
||||
if (exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.context.drizzle.insert(fileManagerRecent).values({
|
||||
userId,
|
||||
hostId: input.hostId,
|
||||
path: input.path,
|
||||
name: resolveBookmarkName(input.path, input.name),
|
||||
lastOpened,
|
||||
});
|
||||
await this.afterWrite();
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteRecentForHostPath(
|
||||
userId: string,
|
||||
input: Pick<FileManagerBookmarkInput, "hostId" | "path">,
|
||||
): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerRecent)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerRecent.userId, userId),
|
||||
eq(fileManagerRecent.hostId, input.hostId),
|
||||
eq(fileManagerRecent.path, input.path),
|
||||
),
|
||||
);
|
||||
|
||||
if (rowsAffected(result) > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
async listPinnedForHost(
|
||||
userId: string,
|
||||
hostId: number,
|
||||
): Promise<FileManagerPinnedRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(fileManagerPinned)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerPinned.userId, userId),
|
||||
eq(fileManagerPinned.hostId, hostId),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(fileManagerPinned.pinnedAt));
|
||||
}
|
||||
|
||||
async listPinnedByUserId(userId: string): Promise<FileManagerPinnedRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(fileManagerPinned)
|
||||
.where(eq(fileManagerPinned.userId, userId));
|
||||
}
|
||||
|
||||
async createPinned(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
pinnedAt = new Date().toISOString(),
|
||||
): Promise<boolean> {
|
||||
const exists = await this.existsPinned(userId, input.hostId, input.path);
|
||||
if (exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.context.drizzle.insert(fileManagerPinned).values({
|
||||
userId,
|
||||
hostId: input.hostId,
|
||||
path: input.path,
|
||||
name: resolveBookmarkName(input.path, input.name),
|
||||
pinnedAt,
|
||||
});
|
||||
await this.afterWrite();
|
||||
return true;
|
||||
}
|
||||
|
||||
async createPinnedForImport(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
pinnedAt = new Date().toISOString(),
|
||||
): Promise<boolean> {
|
||||
const exists = await this.existsPinnedImportItem(userId, input);
|
||||
if (exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.context.drizzle.insert(fileManagerPinned).values({
|
||||
userId,
|
||||
hostId: input.hostId,
|
||||
path: input.path,
|
||||
name: resolveBookmarkName(input.path, input.name),
|
||||
pinnedAt,
|
||||
});
|
||||
await this.afterWrite();
|
||||
return true;
|
||||
}
|
||||
|
||||
async deletePinnedForHostPath(
|
||||
userId: string,
|
||||
input: Pick<FileManagerBookmarkInput, "hostId" | "path">,
|
||||
): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerPinned)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerPinned.userId, userId),
|
||||
eq(fileManagerPinned.hostId, input.hostId),
|
||||
eq(fileManagerPinned.path, input.path),
|
||||
),
|
||||
);
|
||||
|
||||
if (rowsAffected(result) > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
async listShortcutsForHost(
|
||||
userId: string,
|
||||
hostId: number,
|
||||
): Promise<FileManagerShortcutRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(fileManagerShortcuts)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerShortcuts.userId, userId),
|
||||
eq(fileManagerShortcuts.hostId, hostId),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(fileManagerShortcuts.createdAt));
|
||||
}
|
||||
|
||||
async listShortcutsByUserId(
|
||||
userId: string,
|
||||
): Promise<FileManagerShortcutRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(fileManagerShortcuts)
|
||||
.where(eq(fileManagerShortcuts.userId, userId));
|
||||
}
|
||||
|
||||
async createShortcut(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
createdAt = new Date().toISOString(),
|
||||
): Promise<boolean> {
|
||||
const exists = await this.existsShortcut(userId, input.hostId, input.path);
|
||||
if (exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.context.drizzle.insert(fileManagerShortcuts).values({
|
||||
userId,
|
||||
hostId: input.hostId,
|
||||
path: input.path,
|
||||
name: resolveBookmarkName(input.path, input.name),
|
||||
createdAt,
|
||||
});
|
||||
await this.afterWrite();
|
||||
return true;
|
||||
}
|
||||
|
||||
async createShortcutForImport(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
createdAt = new Date().toISOString(),
|
||||
): Promise<boolean> {
|
||||
const exists = await this.existsShortcutImportItem(userId, input);
|
||||
if (exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.context.drizzle.insert(fileManagerShortcuts).values({
|
||||
userId,
|
||||
hostId: input.hostId,
|
||||
path: input.path,
|
||||
name: resolveBookmarkName(input.path, input.name),
|
||||
createdAt,
|
||||
});
|
||||
await this.afterWrite();
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteShortcutForHostPath(
|
||||
userId: string,
|
||||
input: Pick<FileManagerBookmarkInput, "hostId" | "path">,
|
||||
): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerShortcuts)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerShortcuts.userId, userId),
|
||||
eq(fileManagerShortcuts.hostId, input.hostId),
|
||||
eq(fileManagerShortcuts.path, input.path),
|
||||
),
|
||||
);
|
||||
|
||||
if (rowsAffected(result) > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
async deleteByUserId(userId: string): Promise<number> {
|
||||
const total =
|
||||
(await this.deleteRecentByUserId(userId)) +
|
||||
(await this.deletePinnedByUserId(userId)) +
|
||||
(await this.deleteShortcutsByUserId(userId));
|
||||
|
||||
if (total > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
async deleteByHostId(hostId: number): Promise<number> {
|
||||
const total =
|
||||
(await this.deleteRecentByHostId(hostId)) +
|
||||
(await this.deletePinnedByHostId(hostId)) +
|
||||
(await this.deleteShortcutsByHostId(hostId));
|
||||
|
||||
if (total > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
async deleteByHostIds(hostIds: number[]): Promise<number> {
|
||||
if (hostIds.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const total =
|
||||
(await this.deleteRecentByHostIds(hostIds)) +
|
||||
(await this.deletePinnedByHostIds(hostIds)) +
|
||||
(await this.deleteShortcutsByHostIds(hostIds));
|
||||
|
||||
if (total > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private async existsPinned(
|
||||
userId: string,
|
||||
hostId: number,
|
||||
path: string,
|
||||
): Promise<boolean> {
|
||||
const rows = await this.context.drizzle
|
||||
.select({ id: fileManagerPinned.id })
|
||||
.from(fileManagerPinned)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerPinned.userId, userId),
|
||||
eq(fileManagerPinned.hostId, hostId),
|
||||
eq(fileManagerPinned.path, path),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return rows.length > 0;
|
||||
}
|
||||
|
||||
private async existsRecentImportItem(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
): Promise<boolean> {
|
||||
const rows = await this.context.drizzle
|
||||
.select({ id: fileManagerRecent.id })
|
||||
.from(fileManagerRecent)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerRecent.userId, userId),
|
||||
eq(fileManagerRecent.path, input.path),
|
||||
eq(
|
||||
fileManagerRecent.name,
|
||||
resolveBookmarkName(input.path, input.name),
|
||||
),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return rows.length > 0;
|
||||
}
|
||||
|
||||
private async existsPinnedImportItem(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
): Promise<boolean> {
|
||||
const rows = await this.context.drizzle
|
||||
.select({ id: fileManagerPinned.id })
|
||||
.from(fileManagerPinned)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerPinned.userId, userId),
|
||||
eq(fileManagerPinned.path, input.path),
|
||||
eq(
|
||||
fileManagerPinned.name,
|
||||
resolveBookmarkName(input.path, input.name),
|
||||
),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return rows.length > 0;
|
||||
}
|
||||
|
||||
private async existsShortcutImportItem(
|
||||
userId: string,
|
||||
input: FileManagerBookmarkInput,
|
||||
): Promise<boolean> {
|
||||
const rows = await this.context.drizzle
|
||||
.select({ id: fileManagerShortcuts.id })
|
||||
.from(fileManagerShortcuts)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerShortcuts.userId, userId),
|
||||
eq(fileManagerShortcuts.path, input.path),
|
||||
eq(
|
||||
fileManagerShortcuts.name,
|
||||
resolveBookmarkName(input.path, input.name),
|
||||
),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return rows.length > 0;
|
||||
}
|
||||
|
||||
private async existsShortcut(
|
||||
userId: string,
|
||||
hostId: number,
|
||||
path: string,
|
||||
): Promise<boolean> {
|
||||
const rows = await this.context.drizzle
|
||||
.select({ id: fileManagerShortcuts.id })
|
||||
.from(fileManagerShortcuts)
|
||||
.where(
|
||||
and(
|
||||
eq(fileManagerShortcuts.userId, userId),
|
||||
eq(fileManagerShortcuts.hostId, hostId),
|
||||
eq(fileManagerShortcuts.path, path),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return rows.length > 0;
|
||||
}
|
||||
|
||||
private async deleteRecentByUserId(userId: string): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerRecent)
|
||||
.where(eq(fileManagerRecent.userId, userId));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deletePinnedByUserId(userId: string): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerPinned)
|
||||
.where(eq(fileManagerPinned.userId, userId));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deleteShortcutsByUserId(userId: string): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerShortcuts)
|
||||
.where(eq(fileManagerShortcuts.userId, userId));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deleteRecentByHostId(hostId: number): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerRecent)
|
||||
.where(eq(fileManagerRecent.hostId, hostId));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deletePinnedByHostId(hostId: number): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerPinned)
|
||||
.where(eq(fileManagerPinned.hostId, hostId));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deleteShortcutsByHostId(hostId: number): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerShortcuts)
|
||||
.where(eq(fileManagerShortcuts.hostId, hostId));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deleteRecentByHostIds(hostIds: number[]): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerRecent)
|
||||
.where(inArray(fileManagerRecent.hostId, hostIds));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deletePinnedByHostIds(hostIds: number[]): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerPinned)
|
||||
.where(inArray(fileManagerPinned.hostId, hostIds));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async deleteShortcutsByHostIds(hostIds: number[]): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(fileManagerShortcuts)
|
||||
.where(inArray(fileManagerShortcuts.hostId, hostIds));
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async afterWrite(): Promise<void> {
|
||||
await this.onWrite?.();
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
import { and, desc, eq, inArray, or } from "drizzle-orm";
|
||||
import { transferRecent } from "../db/schema.js";
|
||||
import type { DatabaseContext } from "./database-context.js";
|
||||
import { rowsAffected } from "./mutation-result.js";
|
||||
|
||||
export type TransferRecentRecord = typeof transferRecent.$inferSelect;
|
||||
|
||||
export interface TransferRecentDestinationInput {
|
||||
sourceHostId: number;
|
||||
destHostId: number;
|
||||
destPath: string;
|
||||
destPathLabel?: string | null;
|
||||
}
|
||||
|
||||
export class TransferRecentRepository {
|
||||
constructor(
|
||||
private readonly context: DatabaseContext,
|
||||
private readonly onWrite?: () => void | Promise<void>,
|
||||
) {}
|
||||
|
||||
async listByUserId(userId: string): Promise<TransferRecentRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(transferRecent)
|
||||
.where(eq(transferRecent.userId, userId));
|
||||
}
|
||||
|
||||
async listBySourceHost(
|
||||
userId: string,
|
||||
sourceHostId: number,
|
||||
limit = 10,
|
||||
): Promise<TransferRecentRecord[]> {
|
||||
return this.context.drizzle
|
||||
.select()
|
||||
.from(transferRecent)
|
||||
.where(
|
||||
and(
|
||||
eq(transferRecent.userId, userId),
|
||||
eq(transferRecent.sourceHostId, sourceHostId),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(transferRecent.lastUsed))
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
async upsertForDestination(
|
||||
userId: string,
|
||||
input: TransferRecentDestinationInput,
|
||||
lastUsed = new Date().toISOString(),
|
||||
): Promise<void> {
|
||||
const [existing] = await this.context.drizzle
|
||||
.select({ id: transferRecent.id })
|
||||
.from(transferRecent)
|
||||
.where(
|
||||
and(
|
||||
eq(transferRecent.userId, userId),
|
||||
eq(transferRecent.sourceHostId, input.sourceHostId),
|
||||
eq(transferRecent.destHostId, input.destHostId),
|
||||
eq(transferRecent.destPath, input.destPath),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existing) {
|
||||
await this.context.drizzle
|
||||
.update(transferRecent)
|
||||
.set({ lastUsed })
|
||||
.where(eq(transferRecent.id, existing.id));
|
||||
} else {
|
||||
await this.context.drizzle.insert(transferRecent).values({
|
||||
userId,
|
||||
sourceHostId: input.sourceHostId,
|
||||
destHostId: input.destHostId,
|
||||
destPath: input.destPath,
|
||||
destPathLabel: input.destPathLabel || input.destPath,
|
||||
lastUsed,
|
||||
});
|
||||
}
|
||||
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
async pruneSourceHost(
|
||||
userId: string,
|
||||
sourceHostId: number,
|
||||
keep = 10,
|
||||
): Promise<number> {
|
||||
const rows = await this.context.drizzle
|
||||
.select({ id: transferRecent.id })
|
||||
.from(transferRecent)
|
||||
.where(
|
||||
and(
|
||||
eq(transferRecent.userId, userId),
|
||||
eq(transferRecent.sourceHostId, sourceHostId),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(transferRecent.lastUsed));
|
||||
|
||||
const idsToDelete = rows.slice(keep).map((row) => row.id);
|
||||
if (idsToDelete.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const result = await this.context.drizzle
|
||||
.delete(transferRecent)
|
||||
.where(inArray(transferRecent.id, idsToDelete));
|
||||
|
||||
if (rowsAffected(result) > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
async deleteByUserId(userId: string): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(transferRecent)
|
||||
.where(eq(transferRecent.userId, userId));
|
||||
|
||||
if (rowsAffected(result) > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
async deleteByHostId(hostId: number): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(transferRecent)
|
||||
.where(
|
||||
or(
|
||||
eq(transferRecent.sourceHostId, hostId),
|
||||
eq(transferRecent.destHostId, hostId),
|
||||
),
|
||||
);
|
||||
|
||||
if (rowsAffected(result) > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
async deleteByHostIds(hostIds: number[]): Promise<number> {
|
||||
if (hostIds.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const result = await this.context.drizzle
|
||||
.delete(transferRecent)
|
||||
.where(
|
||||
or(
|
||||
inArray(transferRecent.sourceHostId, hostIds),
|
||||
inArray(transferRecent.destHostId, hostIds),
|
||||
),
|
||||
);
|
||||
|
||||
if (rowsAffected(result) > 0) {
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async afterWrite(): Promise<void> {
|
||||
await this.onWrite?.();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user