mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 07:35:24 +00:00
* feat: shell controller, TabStage, layout and theme plugins with repair view * feat: move the classic layout into its own bundled plugin * feat: move color themes into a bundled themes plugin * fix: plugin tests and type checks work with a linked sdk * fix: home tab, layout switch and missing layout fallbacks for looks * chore: add classic layout and themes to the readme * chore: drop example plugin links from the plugin guide * feat: plugin filters by type, paged browse, hide plugins needing newer termix * feat: move desktop, status, import, audit and branding features into plugins * feat: host lock api so a plugin can load hosts from a config file (#1581) * chore: add hosts config to the plugin table * feat: login policy hook for plugins and optional https redirect (#1584) * chore: add login security to the plugin table * feat: api key socket tickets so a master can open remote sessions (#1654) * feat: sync moves to a plugin, desktop gets multiple accounts (#1580, #1654) * chore: add sync to the plugin table * feat: move the sync screens and wizard out of core into the plugin (#1580) * fix: add a neutral linked desktop hook for plugins (#1580) * fix: show the linked account's name and admin flag in the app (#1580) * feat: ssh transport providers, core ssh relay and plugin folders api * feat: support FIDO2 security keys through ssh agent * feat: add companion panels beside host tabs * feat: mTLS client certificates for desktop server links * feat: native web view for plugins in the desktop app * fix: detect the desktop link window by Electron's default user agent Fixes #1675 * fix: changelog for desktop link fix * fix: keep text primary keys in the plugin data export * fix: ship sharp binaries for Windows arm64 builds * feat: client cert on the sync link types (sdk 1.0.18) * chore: update translations * feat: themed title bar for the desktop app (#1690) * chore: changelog for the themed title bar * fix: draw the title bar in the sidebar color * feat: per host layout for the terminal panel bar * chore: sdk 1.0.19 for the panel bar layout * chore: bundle all 43 plugins for 26.10.3 * chore: update translations --------- Co-authored-by: SirHephaistos <60616553+SirHephaistos@users.noreply.github.com> Co-authored-by: Giovanny Aranda <garanda21@gmail.com>
136 lines
4.1 KiB
TypeScript
136 lines
4.1 KiB
TypeScript
import type { AuthenticatedRequest } from "../../../types/index.js";
|
|
import type { RequestHandler, Router } from "express";
|
|
import {
|
|
createCurrentAuditLogRepository,
|
|
createCurrentUserRepository,
|
|
} from "../repositories/factory.js";
|
|
import { apiLogger } from "../../utils/logger.js";
|
|
|
|
async function isAdminUser(userId: string | undefined): Promise<boolean> {
|
|
if (!userId) return false;
|
|
const user = await createCurrentUserRepository().findById(userId);
|
|
return !!user?.isAdmin;
|
|
}
|
|
|
|
export function registerAuditLogRoutes(
|
|
router: Router,
|
|
authenticateJWT: RequestHandler,
|
|
): void {
|
|
/**
|
|
* @openapi
|
|
* /audit-logs:
|
|
* get:
|
|
* summary: List audit logs
|
|
* description: Returns paginated, filterable audit log entries. Admin only.
|
|
* tags:
|
|
* - Audit
|
|
* parameters:
|
|
* - in: query
|
|
* name: page
|
|
* schema: { type: integer, default: 1 }
|
|
* - in: query
|
|
* name: limit
|
|
* schema: { type: integer, default: 50, maximum: 200 }
|
|
* - in: query
|
|
* name: userId
|
|
* schema: { type: string }
|
|
* - in: query
|
|
* name: action
|
|
* schema: { type: string }
|
|
* - in: query
|
|
* name: resourceType
|
|
* schema: { type: string }
|
|
* - in: query
|
|
* name: success
|
|
* schema: { type: string, enum: [true, false] }
|
|
* - in: query
|
|
* name: startDate
|
|
* schema: { type: string, format: date-time }
|
|
* - in: query
|
|
* name: endDate
|
|
* schema: { type: string, format: date-time }
|
|
* responses:
|
|
* 200:
|
|
* description: Paginated list of audit logs.
|
|
* 403:
|
|
* description: Not authorized.
|
|
* 500:
|
|
* description: Failed to fetch audit logs.
|
|
*/
|
|
router.get("/audit-logs", authenticateJWT, async (req, res) => {
|
|
try {
|
|
const authReq = req as AuthenticatedRequest;
|
|
if (!(await isAdminUser(authReq.userId))) {
|
|
return res.status(403).json({ error: "Not authorized" });
|
|
}
|
|
|
|
const page = Math.max(1, parseInt(String(req.query.page || "1"), 10));
|
|
const limit = Math.min(
|
|
200,
|
|
Math.max(1, parseInt(String(req.query.limit || "50"), 10)),
|
|
);
|
|
const offset = (page - 1) * limit;
|
|
|
|
const { userId, action, resourceType, success, startDate, endDate } =
|
|
req.query as Record<string, string | undefined>;
|
|
|
|
const { logs, total } = await createCurrentAuditLogRepository().listPage({
|
|
filters: {
|
|
userId,
|
|
action,
|
|
resourceType,
|
|
success:
|
|
success !== undefined && success !== ""
|
|
? success === "true"
|
|
: undefined,
|
|
startDate,
|
|
endDate,
|
|
},
|
|
limit,
|
|
offset,
|
|
});
|
|
const totalPages = Math.ceil(total / limit);
|
|
|
|
return res.json({ logs, total, page, totalPages });
|
|
} catch (err) {
|
|
apiLogger.error("Failed to fetch audit logs", err);
|
|
return res.status(500).json({ error: "Failed to fetch audit logs" });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @openapi
|
|
* /audit-logs/actions:
|
|
* get:
|
|
* summary: List distinct audit log action types
|
|
* description: Returns all distinct action values in the audit log for filter dropdowns. Admin only.
|
|
* tags:
|
|
* - Audit
|
|
* responses:
|
|
* 200:
|
|
* description: List of distinct action strings.
|
|
* 403:
|
|
* description: Not authorized.
|
|
* 500:
|
|
* description: Failed to fetch audit log actions.
|
|
*/
|
|
router.get("/audit-logs/actions", authenticateJWT, async (req, res) => {
|
|
try {
|
|
const authReq = req as AuthenticatedRequest;
|
|
if (!(await isAdminUser(authReq.userId))) {
|
|
return res.status(403).json({ error: "Not authorized" });
|
|
}
|
|
|
|
const actions =
|
|
await createCurrentAuditLogRepository().listDistinctActions();
|
|
|
|
return res.json({ actions });
|
|
} catch (err) {
|
|
apiLogger.error("Failed to fetch audit log actions", err);
|
|
return res
|
|
.status(500)
|
|
.json({ error: "Failed to fetch audit log actions" });
|
|
}
|
|
});
|
|
}
|