mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35: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
@@ -9,7 +9,6 @@ import {
|
||||
createCurrentCredentialRepository,
|
||||
createCurrentDashboardServiceLinkRepository,
|
||||
createCurrentDismissedAlertRepository,
|
||||
createCurrentFileManagerBookmarkRepository,
|
||||
createCurrentHomepageItemRepository,
|
||||
createCurrentHomepageLayoutRepository,
|
||||
createCurrentHostHealthRepository,
|
||||
@@ -35,7 +34,6 @@ import {
|
||||
createCurrentTrustedDeviceRepository,
|
||||
createCurrentUserPreferenceRepository,
|
||||
createCurrentUserRepository,
|
||||
createCurrentTransferRecentRepository,
|
||||
createCurrentVaultProfileRepository,
|
||||
createCurrentSecretSourceRepository,
|
||||
createCurrentSharedCredentialSecretsRepository,
|
||||
@@ -85,9 +83,9 @@ export async function deleteUserAndRelatedData(
|
||||
|
||||
await createCurrentSshCredentialUsageRepository().deleteByUserId(userId);
|
||||
|
||||
await createCurrentFileManagerBookmarkRepository().deleteByUserId(userId);
|
||||
|
||||
await createCurrentTransferRecentRepository().deleteByUserId(userId);
|
||||
// file manager recent/pinned/shortcuts and transfer_recent cascade on the
|
||||
// user's refUser() foreign key, as the file-manager plugin's adopted
|
||||
// tables.
|
||||
|
||||
await createCurrentRecentActivityRepository().deleteByUserId(userId);
|
||||
await createCurrentDismissedAlertRepository().deleteByUserId(userId);
|
||||
|
||||
@@ -1,544 +0,0 @@
|
||||
import type { AuthenticatedRequest } from "../../../types/index.js";
|
||||
import type { Request, RequestHandler, Response, Router } from "express";
|
||||
import { sshLogger } from "../../utils/logger.js";
|
||||
import { createCurrentFileManagerBookmarkRepository } from "../repositories/factory.js";
|
||||
import { isNonEmptyString } from "./host-normalizers.js";
|
||||
|
||||
export function registerHostFileManagerBookmarkRoutes(
|
||||
router: Router,
|
||||
authenticateJWT: RequestHandler,
|
||||
requireViewPermission: RequestHandler,
|
||||
requireDataAccess: RequestHandler,
|
||||
): void {
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/recent:
|
||||
* get:
|
||||
* summary: Get recent files
|
||||
* description: Retrieves a list of recent files for a specific host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: hostId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: A list of recent files.
|
||||
* 400:
|
||||
* description: Invalid userId or hostId.
|
||||
* 500:
|
||||
* description: Failed to fetch recent files.
|
||||
*/
|
||||
router.get(
|
||||
"/file_manager/recent",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const hostIdQuery = Array.isArray(req.query.hostId)
|
||||
? req.query.hostId[0]
|
||||
: req.query.hostId;
|
||||
const hostId = hostIdQuery ? parseInt(hostIdQuery as string) : null;
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
sshLogger.warn("Invalid userId for recent files fetch");
|
||||
return res.status(400).json({ error: "Invalid userId" });
|
||||
}
|
||||
|
||||
if (!hostId) {
|
||||
sshLogger.warn("Host ID is required for recent files fetch");
|
||||
return res.status(400).json({ error: "Host ID is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
const recentFiles =
|
||||
await createCurrentFileManagerBookmarkRepository().listRecentForHost(
|
||||
userId,
|
||||
hostId,
|
||||
20,
|
||||
);
|
||||
|
||||
res.json(recentFiles);
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to fetch recent files", err);
|
||||
res.status(500).json({ error: "Failed to fetch recent files" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/recent:
|
||||
* post:
|
||||
* summary: Add recent file
|
||||
* description: Adds a file to the list of recent files for a host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* hostId:
|
||||
* type: integer
|
||||
* path:
|
||||
* type: string
|
||||
* name:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Recent file added.
|
||||
* 400:
|
||||
* description: Invalid data.
|
||||
* 500:
|
||||
* description: Failed to add recent file.
|
||||
*/
|
||||
router.post(
|
||||
"/file_manager/recent",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path, name } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
sshLogger.warn("Invalid data for recent file addition");
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
try {
|
||||
await createCurrentFileManagerBookmarkRepository().upsertRecent(
|
||||
userId,
|
||||
{
|
||||
hostId,
|
||||
path,
|
||||
name,
|
||||
},
|
||||
);
|
||||
|
||||
res.json({ message: "Recent file added" });
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to add recent file", err);
|
||||
res.status(500).json({ error: "Failed to add recent file" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/recent:
|
||||
* delete:
|
||||
* summary: Remove recent file
|
||||
* description: Removes a file from the list of recent files for a host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* hostId:
|
||||
* type: integer
|
||||
* path:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Recent file removed.
|
||||
* 400:
|
||||
* description: Invalid data.
|
||||
* 500:
|
||||
* description: Failed to remove recent file.
|
||||
*/
|
||||
router.delete(
|
||||
"/file_manager/recent",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
sshLogger.warn("Invalid data for recent file deletion");
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
try {
|
||||
await createCurrentFileManagerBookmarkRepository().deleteRecentForHostPath(
|
||||
userId,
|
||||
{ hostId, path },
|
||||
);
|
||||
|
||||
res.json({ message: "Recent file removed" });
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to remove recent file", err);
|
||||
res.status(500).json({ error: "Failed to remove recent file" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/pinned:
|
||||
* get:
|
||||
* summary: Get pinned files
|
||||
* description: Retrieves a list of pinned files for a specific host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: hostId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: A list of pinned files.
|
||||
* 400:
|
||||
* description: Invalid userId or hostId.
|
||||
* 500:
|
||||
* description: Failed to fetch pinned files.
|
||||
*/
|
||||
router.get(
|
||||
"/file_manager/pinned",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const hostIdQuery = Array.isArray(req.query.hostId)
|
||||
? req.query.hostId[0]
|
||||
: req.query.hostId;
|
||||
const hostId = hostIdQuery ? parseInt(hostIdQuery as string) : null;
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
sshLogger.warn("Invalid userId for pinned files fetch");
|
||||
return res.status(400).json({ error: "Invalid userId" });
|
||||
}
|
||||
|
||||
if (!hostId) {
|
||||
sshLogger.warn("Host ID is required for pinned files fetch");
|
||||
return res.status(400).json({ error: "Host ID is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
const pinnedFiles =
|
||||
await createCurrentFileManagerBookmarkRepository().listPinnedForHost(
|
||||
userId,
|
||||
hostId,
|
||||
);
|
||||
|
||||
res.json(pinnedFiles);
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to fetch pinned files", err);
|
||||
res.status(500).json({ error: "Failed to fetch pinned files" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/pinned:
|
||||
* post:
|
||||
* summary: Add pinned file
|
||||
* description: Adds a file to the list of pinned files for a host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* hostId:
|
||||
* type: integer
|
||||
* path:
|
||||
* type: string
|
||||
* name:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: File pinned.
|
||||
* 400:
|
||||
* description: Invalid data.
|
||||
* 409:
|
||||
* description: File already pinned.
|
||||
* 500:
|
||||
* description: Failed to pin file.
|
||||
*/
|
||||
router.post(
|
||||
"/file_manager/pinned",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path, name } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
sshLogger.warn("Invalid data for pinned file addition");
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
try {
|
||||
const created =
|
||||
await createCurrentFileManagerBookmarkRepository().createPinned(
|
||||
userId,
|
||||
{ hostId, path, name },
|
||||
);
|
||||
|
||||
if (!created) {
|
||||
return res.status(409).json({ error: "File already pinned" });
|
||||
}
|
||||
|
||||
res.json({ message: "File pinned" });
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to pin file", err);
|
||||
res.status(500).json({ error: "Failed to pin file" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/pinned:
|
||||
* delete:
|
||||
* summary: Remove pinned file
|
||||
* description: Removes a file from the list of pinned files for a host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* hostId:
|
||||
* type: integer
|
||||
* path:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Pinned file removed.
|
||||
* 400:
|
||||
* description: Invalid data.
|
||||
* 500:
|
||||
* description: Failed to remove pinned file.
|
||||
*/
|
||||
router.delete(
|
||||
"/file_manager/pinned",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
sshLogger.warn("Invalid data for pinned file deletion");
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
try {
|
||||
await createCurrentFileManagerBookmarkRepository().deletePinnedForHostPath(
|
||||
userId,
|
||||
{ hostId, path },
|
||||
);
|
||||
|
||||
res.json({ message: "Pinned file removed" });
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to remove pinned file", err);
|
||||
res.status(500).json({ error: "Failed to remove pinned file" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/shortcuts:
|
||||
* get:
|
||||
* summary: Get shortcuts
|
||||
* description: Retrieves a list of shortcuts for a specific host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: hostId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: A list of shortcuts.
|
||||
* 400:
|
||||
* description: Invalid userId or hostId.
|
||||
* 500:
|
||||
* description: Failed to fetch shortcuts.
|
||||
*/
|
||||
router.get(
|
||||
"/file_manager/shortcuts",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const hostIdQuery = Array.isArray(req.query.hostId)
|
||||
? req.query.hostId[0]
|
||||
: req.query.hostId;
|
||||
const hostId = hostIdQuery ? parseInt(hostIdQuery as string) : null;
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
sshLogger.warn("Invalid userId for shortcuts fetch");
|
||||
return res.status(400).json({ error: "Invalid userId" });
|
||||
}
|
||||
|
||||
if (!hostId) {
|
||||
sshLogger.warn("Host ID is required for shortcuts fetch");
|
||||
return res.status(400).json({ error: "Host ID is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
const shortcuts =
|
||||
await createCurrentFileManagerBookmarkRepository().listShortcutsForHost(
|
||||
userId,
|
||||
hostId,
|
||||
);
|
||||
|
||||
res.json(shortcuts);
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to fetch shortcuts", err);
|
||||
res.status(500).json({ error: "Failed to fetch shortcuts" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/shortcuts:
|
||||
* post:
|
||||
* summary: Add shortcut
|
||||
* description: Adds a shortcut for a specific host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* hostId:
|
||||
* type: integer
|
||||
* path:
|
||||
* type: string
|
||||
* name:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Shortcut added.
|
||||
* 400:
|
||||
* description: Invalid data.
|
||||
* 409:
|
||||
* description: Shortcut already exists.
|
||||
* 500:
|
||||
* description: Failed to add shortcut.
|
||||
*/
|
||||
router.post(
|
||||
"/file_manager/shortcuts",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path, name } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
sshLogger.warn("Invalid data for shortcut addition");
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
try {
|
||||
const created =
|
||||
await createCurrentFileManagerBookmarkRepository().createShortcut(
|
||||
userId,
|
||||
{ hostId, path, name },
|
||||
);
|
||||
|
||||
if (!created) {
|
||||
return res.status(409).json({ error: "Shortcut already exists" });
|
||||
}
|
||||
|
||||
res.json({ message: "Shortcut added" });
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to add shortcut", err);
|
||||
res.status(500).json({ error: "Failed to add shortcut" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /host/file_manager/shortcuts:
|
||||
* delete:
|
||||
* summary: Remove shortcut
|
||||
* description: Removes a shortcut for a specific host.
|
||||
* tags:
|
||||
* - SSH
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* hostId:
|
||||
* type: integer
|
||||
* path:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Shortcut removed.
|
||||
* 400:
|
||||
* description: Invalid data.
|
||||
* 500:
|
||||
* description: Failed to remove shortcut.
|
||||
*/
|
||||
router.delete(
|
||||
"/file_manager/shortcuts",
|
||||
authenticateJWT,
|
||||
requireViewPermission,
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { hostId, path } = req.body;
|
||||
|
||||
if (!isNonEmptyString(userId) || !hostId || !path) {
|
||||
sshLogger.warn("Invalid data for shortcut deletion");
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
try {
|
||||
await createCurrentFileManagerBookmarkRepository().deleteShortcutForHostPath(
|
||||
userId,
|
||||
{ hostId, path },
|
||||
);
|
||||
|
||||
res.json({ message: "Shortcut removed" });
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to remove shortcut", err);
|
||||
res.status(500).json({ error: "Failed to remove shortcut" });
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -5,14 +5,12 @@ import { pluginEvents, TOPICS } from "../../plugins/events.js";
|
||||
import {
|
||||
createCurrentCommandHistoryRepository,
|
||||
createCurrentCredentialRepository,
|
||||
createCurrentFileManagerBookmarkRepository,
|
||||
createCurrentHostFolderRepository,
|
||||
createCurrentFolderAccessRepository,
|
||||
createCurrentRecentActivityRepository,
|
||||
createCurrentRbacAccessRepository,
|
||||
createCurrentSshCredentialUsageRepository,
|
||||
createCurrentSessionRecordingRepository,
|
||||
createCurrentTransferRecentRepository,
|
||||
createCurrentSyncTombstoneRepository,
|
||||
} from "../repositories/factory.js";
|
||||
import { isNonEmptyString } from "./host-normalizers.js";
|
||||
@@ -400,13 +398,9 @@ export function registerHostFolderRoutes(
|
||||
const hostIds = hostsToDelete.map((host) => host.id);
|
||||
|
||||
if (hostIds.length > 0) {
|
||||
await createCurrentFileManagerBookmarkRepository().deleteByHostIds(
|
||||
hostIds,
|
||||
);
|
||||
|
||||
await createCurrentTransferRecentRepository().deleteByHostIds(
|
||||
hostIds,
|
||||
);
|
||||
// file manager recent/pinned/shortcuts and transfer_recent cascade
|
||||
// on the host's refHost() foreign key, as the file-manager
|
||||
// plugin's adopted tables.
|
||||
|
||||
await createCurrentCommandHistoryRepository().deleteByHostIds(
|
||||
hostIds,
|
||||
|
||||
@@ -18,12 +18,10 @@ import { notifyAutomationInternalEvent } from "../../hosts/automation-events.js"
|
||||
import {
|
||||
createCurrentCommandHistoryRepository,
|
||||
createCurrentCredentialRepository,
|
||||
createCurrentFileManagerBookmarkRepository,
|
||||
createCurrentOpksshTokenRepository,
|
||||
createCurrentRecentActivityRepository,
|
||||
createCurrentSshCredentialUsageRepository,
|
||||
createCurrentSessionRecordingRepository,
|
||||
createCurrentTransferRecentRepository,
|
||||
createCurrentRbacAccessRepository,
|
||||
createCurrentRoleRepository,
|
||||
createCurrentHostResolutionRepository,
|
||||
@@ -55,7 +53,6 @@ import { validateParentHostId } from "./host-parent-validation.js";
|
||||
import { registerHostOpksshRoutes } from "./host-opkssh-routes.js";
|
||||
import { registerHostStepCaRoutes } from "./host-step-ca-routes.js";
|
||||
import { registerHostFolderRoutes } from "./host-folder-routes.js";
|
||||
import { registerHostFileManagerBookmarkRoutes } from "./host-file-manager-bookmark-routes.js";
|
||||
import { registerHostCommandHistoryRoutes } from "./host-command-history-routes.js";
|
||||
import { registerHostAutostartRoutes } from "./host-autostart-routes.js";
|
||||
import { registerHostInternalRoutes } from "./host-internal-routes.js";
|
||||
@@ -2550,13 +2547,9 @@ router.delete(
|
||||
|
||||
const numericHostId = Number(hostId);
|
||||
|
||||
await createCurrentFileManagerBookmarkRepository().deleteByHostId(
|
||||
numericHostId,
|
||||
);
|
||||
|
||||
await createCurrentTransferRecentRepository().deleteByHostId(
|
||||
numericHostId,
|
||||
);
|
||||
// file manager recent/pinned/shortcuts and transfer_recent cascade on
|
||||
// the host's refHost() foreign key, as the file-manager plugin's
|
||||
// adopted tables.
|
||||
|
||||
await createCurrentCommandHistoryRepository().deleteByHostId(
|
||||
numericHostId,
|
||||
@@ -2635,87 +2628,8 @@ router.delete(
|
||||
},
|
||||
);
|
||||
|
||||
registerHostFileManagerBookmarkRoutes(
|
||||
router,
|
||||
authenticateJWT,
|
||||
permissionManager.requirePermission("hosts.view"),
|
||||
requireDataAccess,
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/transfer/recent",
|
||||
authenticateJWT,
|
||||
permissionManager.requirePermission("hosts.view"),
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const sourceHostIdQuery = Array.isArray(req.query.sourceHostId)
|
||||
? req.query.sourceHostId[0]
|
||||
: req.query.sourceHostId;
|
||||
const sourceHostId = sourceHostIdQuery
|
||||
? parseInt(sourceHostIdQuery as string)
|
||||
: null;
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
return res.status(400).json({ error: "Invalid userId" });
|
||||
}
|
||||
|
||||
if (!sourceHostId) {
|
||||
return res.status(400).json({ error: "Source host ID is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
const recent =
|
||||
await createCurrentTransferRecentRepository().listBySourceHost(
|
||||
userId,
|
||||
sourceHostId,
|
||||
10,
|
||||
);
|
||||
|
||||
res.json(recent);
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to fetch transfer recent destinations", err);
|
||||
res.status(500).json({ error: "Failed to fetch recent destinations" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/transfer/recent",
|
||||
authenticateJWT,
|
||||
permissionManager.requirePermission("hosts.view"),
|
||||
requireDataAccess,
|
||||
async (req: Request, res: Response) => {
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
const { sourceHostId, destHostId, destPath, destPathLabel } = req.body;
|
||||
|
||||
if (
|
||||
!isNonEmptyString(userId) ||
|
||||
!sourceHostId ||
|
||||
!destHostId ||
|
||||
!destPath
|
||||
) {
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
try {
|
||||
const transferRecentRepository = createCurrentTransferRecentRepository();
|
||||
await transferRecentRepository.upsertForDestination(userId, {
|
||||
sourceHostId,
|
||||
destHostId,
|
||||
destPath,
|
||||
destPathLabel,
|
||||
});
|
||||
|
||||
await transferRecentRepository.pruneSourceHost(userId, sourceHostId, 10);
|
||||
|
||||
res.json({ message: "Recent destination saved" });
|
||||
} catch (err) {
|
||||
sshLogger.error("Failed to save transfer recent destination", err);
|
||||
res.status(500).json({ error: "Failed to save recent destination" });
|
||||
}
|
||||
},
|
||||
);
|
||||
// File manager recent/pinned/shortcuts and transfer/recent routes moved to
|
||||
// the file-manager plugin, under /plugin-api/file-manager/.
|
||||
registerHostCommandHistoryRoutes(
|
||||
router,
|
||||
authenticateJWT,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { loginRateLimiter } from "../../utils/login-rate-limiter.js";
|
||||
import {
|
||||
createCurrentCredentialRepository,
|
||||
createCurrentDismissedAlertRepository,
|
||||
createCurrentFileManagerBookmarkRepository,
|
||||
createCurrentHostRepository,
|
||||
createCurrentRecentActivityRepository,
|
||||
createCurrentSettingsRepository,
|
||||
@@ -70,7 +69,6 @@ export async function resetUserPassword(
|
||||
await userRepository.update(userId, { passwordHash });
|
||||
|
||||
await createCurrentSshCredentialUsageRepository().deleteByUserId(userId);
|
||||
await createCurrentFileManagerBookmarkRepository().deleteByUserId(userId);
|
||||
await createCurrentRecentActivityRepository().deleteByUserId(userId);
|
||||
await createCurrentDismissedAlertRepository().deleteByUserId(userId);
|
||||
await createCurrentHostRepository().deleteByUserId(userId);
|
||||
|
||||
Reference in new issue
Block a user