mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
feat: turn session recording into a plugin (B13)
This commit is contained in:
1 parent
a276f6898c
commit
0af2c94983
147 files changed
+25915
-2074
No files matched your search
@@ -479,27 +479,6 @@ async function initializeCompleteDatabase(): Promise<void> {
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session_recordings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
host_id INTEGER NOT NULL,
|
||||
user_id TEXT,
|
||||
username TEXT,
|
||||
access_id INTEGER,
|
||||
started_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ended_at TEXT,
|
||||
duration INTEGER,
|
||||
commands TEXT,
|
||||
dangerous_actions TEXT,
|
||||
recording_path TEXT,
|
||||
protocol TEXT NOT NULL DEFAULT 'ssh',
|
||||
format TEXT NOT NULL DEFAULT 'text',
|
||||
terminated_by_owner INTEGER DEFAULT 0,
|
||||
termination_reason TEXT,
|
||||
FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (access_id) REFERENCES host_access (id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS secret_sources (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
@@ -937,17 +916,6 @@ const relaxPluginGrantGrantedBy = () => {
|
||||
};
|
||||
|
||||
const migrateSchema = () => {
|
||||
addColumnIfNotExists(
|
||||
"session_recordings",
|
||||
"protocol",
|
||||
"TEXT NOT NULL DEFAULT 'ssh'",
|
||||
);
|
||||
addColumnIfNotExists(
|
||||
"session_recordings",
|
||||
"format",
|
||||
"TEXT NOT NULL DEFAULT 'text'",
|
||||
);
|
||||
|
||||
addColumnIfNotExists("user_preferences", "theme", "TEXT");
|
||||
addColumnIfNotExists("user_preferences", "font_size", "TEXT");
|
||||
addColumnIfNotExists("user_preferences", "accent_color", "TEXT");
|
||||
|
||||
@@ -124,17 +124,6 @@ export const PERFORMANCE_INDEXES: PerformanceIndex[] = [
|
||||
columns: "user_id",
|
||||
},
|
||||
|
||||
// Recordings and live session sharing.
|
||||
{
|
||||
name: "idx_session_recordings_user_started",
|
||||
table: "session_recordings",
|
||||
columns: "user_id, started_at",
|
||||
},
|
||||
{
|
||||
name: "idx_session_recordings_host",
|
||||
table: "session_recordings",
|
||||
columns: "host_id",
|
||||
},
|
||||
|
||||
// Automations.
|
||||
{
|
||||
|
||||
@@ -719,48 +719,6 @@ export const auditLogs = mysqlTable(
|
||||
],
|
||||
);
|
||||
|
||||
export const sessionRecordings = mysqlTable(
|
||||
"session_recordings",
|
||||
{
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
|
||||
hostId: int("host_id")
|
||||
.notNull()
|
||||
.references(() => hosts.id, { onDelete: "cascade" }),
|
||||
// Nullable on purpose: a recording is evidence about the host as much as the
|
||||
// person, so it outlives the account. username keeps it attributable.
|
||||
userId: varchar("user_id", { length: 255 }).references(() => users.id, { onDelete: "set null" }),
|
||||
username: text("username"),
|
||||
accessId: int("access_id").references(() => hostAccess.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
|
||||
startedAt: varchar("started_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`(CURRENT_TIMESTAMP)`),
|
||||
endedAt: text("ended_at"),
|
||||
duration: int("duration"),
|
||||
|
||||
commands: text("commands"),
|
||||
dangerousActions: text("dangerous_actions"),
|
||||
|
||||
recordingPath: text("recording_path"),
|
||||
protocol: varchar("protocol", { length: 255 }).notNull().default("ssh"),
|
||||
format: text("format").notNull().default("text"),
|
||||
|
||||
terminatedByOwner: boolean("terminated_by_owner").default(false),
|
||||
terminationReason: text("termination_reason"),
|
||||
},
|
||||
// Listed newest-first per user, and audited per host.
|
||||
(table) => [
|
||||
index("idx_session_recordings_user_started").on(
|
||||
table.userId,
|
||||
table.startedAt,
|
||||
),
|
||||
index("idx_session_recordings_host").on(table.hostId),
|
||||
],
|
||||
);
|
||||
|
||||
export const opksshTokens = mysqlTable(
|
||||
"opkssh_tokens",
|
||||
{
|
||||
|
||||
@@ -720,48 +720,6 @@ export const auditLogs = pgTable(
|
||||
],
|
||||
);
|
||||
|
||||
export const sessionRecordings = pgTable(
|
||||
"session_recordings",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
|
||||
hostId: integer("host_id")
|
||||
.notNull()
|
||||
.references(() => hosts.id, { onDelete: "cascade" }),
|
||||
// Nullable on purpose: a recording is evidence about the host as much as the
|
||||
// person, so it outlives the account. username keeps it attributable.
|
||||
userId: varchar("user_id", { length: 255 }).references(() => users.id, { onDelete: "set null" }),
|
||||
username: text("username"),
|
||||
accessId: integer("access_id").references(() => hostAccess.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
|
||||
startedAt: varchar("started_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
endedAt: text("ended_at"),
|
||||
duration: integer("duration"),
|
||||
|
||||
commands: text("commands"),
|
||||
dangerousActions: text("dangerous_actions"),
|
||||
|
||||
recordingPath: text("recording_path"),
|
||||
protocol: varchar("protocol", { length: 255 }).notNull().default("ssh"),
|
||||
format: text("format").notNull().default("text"),
|
||||
|
||||
terminatedByOwner: boolean("terminated_by_owner").default(false),
|
||||
terminationReason: text("termination_reason"),
|
||||
},
|
||||
// Listed newest-first per user, and audited per host.
|
||||
(table) => [
|
||||
index("idx_session_recordings_user_started").on(
|
||||
table.userId,
|
||||
table.startedAt,
|
||||
),
|
||||
index("idx_session_recordings_host").on(table.hostId),
|
||||
],
|
||||
);
|
||||
|
||||
export const opksshTokens = pgTable(
|
||||
"opkssh_tokens",
|
||||
{
|
||||
|
||||
@@ -712,50 +712,6 @@ export const auditLogs = sqliteTable(
|
||||
],
|
||||
);
|
||||
|
||||
export const sessionRecordings = sqliteTable(
|
||||
"session_recordings",
|
||||
{
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
|
||||
hostId: integer("host_id")
|
||||
.notNull()
|
||||
.references(() => hosts.id, { onDelete: "cascade" }),
|
||||
// Nullable on purpose: a recording is evidence about the host as much as the
|
||||
// person, so it outlives the account. username keeps it attributable.
|
||||
userId: text("user_id").references(() => users.id, { onDelete: "set null" }),
|
||||
username: text("username"),
|
||||
accessId: integer("access_id").references(() => hostAccess.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
|
||||
startedAt: text("started_at")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
endedAt: text("ended_at"),
|
||||
duration: integer("duration"),
|
||||
|
||||
commands: text("commands"),
|
||||
dangerousActions: text("dangerous_actions"),
|
||||
|
||||
recordingPath: text("recording_path"),
|
||||
protocol: text("protocol").notNull().default("ssh"),
|
||||
format: text("format").notNull().default("text"),
|
||||
|
||||
terminatedByOwner: integer("terminated_by_owner", {
|
||||
mode: "boolean",
|
||||
}).default(false),
|
||||
terminationReason: text("termination_reason"),
|
||||
},
|
||||
// Listed newest-first per user, and audited per host.
|
||||
(table) => [
|
||||
index("idx_session_recordings_user_started").on(
|
||||
table.userId,
|
||||
table.startedAt,
|
||||
),
|
||||
index("idx_session_recordings_host").on(table.hostId),
|
||||
],
|
||||
);
|
||||
|
||||
export const opksshTokens = sqliteTable(
|
||||
"opkssh_tokens",
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user