mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
feat: move snippets backend and tables into a plugin (B2)
This commit is contained in:
1 parent
fdedca39fd
commit
e07221eaf3
51 files changed
+28426
-3553
No files matched your search
@@ -435,17 +435,6 @@ async function initializeCompleteDatabase(): Promise<void> {
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS snippets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
description TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS c2s_tunnel_presets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id TEXT NOT NULL,
|
||||
@@ -1581,65 +1570,6 @@ const migrateSchema = () => {
|
||||
addColumnIfNotExists("file_manager_pinned", "host_id", "INTEGER NOT NULL");
|
||||
addColumnIfNotExists("file_manager_shortcuts", "host_id", "INTEGER NOT NULL");
|
||||
|
||||
addColumnIfNotExists("snippets", "folder", "TEXT");
|
||||
addColumnIfNotExists("snippets", "order", "INTEGER NOT NULL DEFAULT 0");
|
||||
addColumnIfNotExists("snippets", "host_filter", "TEXT");
|
||||
addColumnIfNotExists("snippets", "is_note", "INTEGER NOT NULL DEFAULT 0");
|
||||
|
||||
try {
|
||||
sqlite
|
||||
.prepare("SELECT id FROM snippet_folders LIMIT 1")
|
||||
.get();
|
||||
} catch {
|
||||
try {
|
||||
sqlite.exec(`
|
||||
CREATE TABLE IF NOT EXISTS snippet_folders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
color TEXT,
|
||||
icon TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
||||
);
|
||||
`);
|
||||
} catch (createError) {
|
||||
databaseLogger.warn("Failed to create snippet_folders table", {
|
||||
operation: "schema_migration",
|
||||
error: createError,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
sqlite.prepare("SELECT id FROM snippet_access LIMIT 1").get();
|
||||
} catch {
|
||||
try {
|
||||
sqlite.exec(`
|
||||
CREATE TABLE IF NOT EXISTS snippet_access (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
snippet_id INTEGER NOT NULL,
|
||||
user_id TEXT,
|
||||
role_id INTEGER,
|
||||
granted_by TEXT NOT NULL,
|
||||
permission_level TEXT NOT NULL DEFAULT 'view',
|
||||
expires_at TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (snippet_id) REFERENCES snippets (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (role_id) REFERENCES roles (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (granted_by) REFERENCES users (id) ON DELETE CASCADE
|
||||
);
|
||||
`);
|
||||
} catch (createError) {
|
||||
databaseLogger.warn("Failed to create snippet_access table", {
|
||||
operation: "schema_migration",
|
||||
error: createError,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
sqlite.prepare("SELECT id FROM termix_identities LIMIT 1").get();
|
||||
} catch {
|
||||
|
||||
@@ -98,23 +98,6 @@ export const PERFORMANCE_INDEXES: PerformanceIndex[] = [
|
||||
columns: "user_id",
|
||||
},
|
||||
|
||||
// Snippets.
|
||||
{ name: "idx_snippets_user_id", table: "snippets", columns: "user_id" },
|
||||
{
|
||||
name: "idx_snippet_access_user_id",
|
||||
table: "snippet_access",
|
||||
columns: "user_id",
|
||||
},
|
||||
{
|
||||
name: "idx_snippet_access_snippet_id",
|
||||
table: "snippet_access",
|
||||
columns: "snippet_id",
|
||||
},
|
||||
{
|
||||
name: "idx_snippet_access_role_id",
|
||||
table: "snippet_access",
|
||||
columns: "role_id",
|
||||
},
|
||||
|
||||
// Per-user history and file manager surfaces.
|
||||
{
|
||||
|
||||
@@ -557,48 +557,6 @@ export const sshCredentialUsage = mysqlTable(
|
||||
],
|
||||
);
|
||||
|
||||
export const snippets = mysqlTable(
|
||||
"snippets",
|
||||
{
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
userId: varchar("user_id", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
content: text("content").notNull(),
|
||||
description: text("description"),
|
||||
folder: varchar("folder", { length: 255 }),
|
||||
order: int("order").notNull().default(0),
|
||||
syncId: varchar("sync_id", { length: 255 }).unique(),
|
||||
createdAt: varchar("created_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`(CURRENT_TIMESTAMP)`),
|
||||
updatedAt: varchar("updated_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`(CURRENT_TIMESTAMP)`),
|
||||
hostFilter: text("host_filter"),
|
||||
isNote: boolean("is_note").notNull().default(false),
|
||||
},
|
||||
(table) => [index("idx_snippets_user_id").on(table.userId)],
|
||||
);
|
||||
|
||||
export const snippetFolders = mysqlTable("snippet_folders", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
userId: varchar("user_id", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
color: text("color"),
|
||||
icon: text("icon"),
|
||||
syncId: varchar("sync_id", { length: 255 }).unique(),
|
||||
createdAt: varchar("created_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`(CURRENT_TIMESTAMP)`),
|
||||
updatedAt: varchar("updated_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`(CURRENT_TIMESTAMP)`),
|
||||
});
|
||||
|
||||
export const c2sTunnelPresets = mysqlTable("c2s_tunnel_presets", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
userId: varchar("user_id", { length: 255 })
|
||||
@@ -616,39 +574,6 @@ export const c2sTunnelPresets = mysqlTable("c2s_tunnel_presets", {
|
||||
.default(sql`(CURRENT_TIMESTAMP)`),
|
||||
});
|
||||
|
||||
export const snippetAccess = mysqlTable(
|
||||
"snippet_access",
|
||||
{
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
snippetId: int("snippet_id")
|
||||
.notNull()
|
||||
.references(() => snippets.id, { onDelete: "cascade" }),
|
||||
|
||||
userId: varchar("user_id", { length: 255 }).references(() => users.id, { onDelete: "cascade" }),
|
||||
roleId: int("role_id").references(() => roles.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
|
||||
grantedBy: varchar("granted_by", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
|
||||
permissionLevel: text("permission_level").notNull().default("view"),
|
||||
|
||||
expiresAt: varchar("expires_at", { length: 255 }),
|
||||
|
||||
createdAt: varchar("created_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`(CURRENT_TIMESTAMP)`),
|
||||
},
|
||||
// Same three lookup shapes as host_access: by grantee, by role, by snippet.
|
||||
(table) => [
|
||||
index("idx_snippet_access_user_id").on(table.userId),
|
||||
index("idx_snippet_access_snippet_id").on(table.snippetId),
|
||||
index("idx_snippet_access_role_id").on(table.roleId),
|
||||
],
|
||||
);
|
||||
|
||||
export const sshFolders = mysqlTable(
|
||||
"ssh_folders",
|
||||
{
|
||||
|
||||
@@ -558,48 +558,6 @@ export const sshCredentialUsage = pgTable(
|
||||
],
|
||||
);
|
||||
|
||||
export const snippets = pgTable(
|
||||
"snippets",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
userId: varchar("user_id", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
content: text("content").notNull(),
|
||||
description: text("description"),
|
||||
folder: varchar("folder", { length: 255 }),
|
||||
order: integer("order").notNull().default(0),
|
||||
syncId: varchar("sync_id", { length: 255 }).unique(),
|
||||
createdAt: varchar("created_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
updatedAt: varchar("updated_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
hostFilter: text("host_filter"),
|
||||
isNote: boolean("is_note").notNull().default(false),
|
||||
},
|
||||
(table) => [index("idx_snippets_user_id").on(table.userId)],
|
||||
);
|
||||
|
||||
export const snippetFolders = pgTable("snippet_folders", {
|
||||
id: serial("id").primaryKey(),
|
||||
userId: varchar("user_id", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
color: text("color"),
|
||||
icon: text("icon"),
|
||||
syncId: varchar("sync_id", { length: 255 }).unique(),
|
||||
createdAt: varchar("created_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
updatedAt: varchar("updated_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
});
|
||||
|
||||
export const c2sTunnelPresets = pgTable("c2s_tunnel_presets", {
|
||||
id: serial("id").primaryKey(),
|
||||
userId: varchar("user_id", { length: 255 })
|
||||
@@ -617,39 +575,6 @@ export const c2sTunnelPresets = pgTable("c2s_tunnel_presets", {
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
});
|
||||
|
||||
export const snippetAccess = pgTable(
|
||||
"snippet_access",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
snippetId: integer("snippet_id")
|
||||
.notNull()
|
||||
.references(() => snippets.id, { onDelete: "cascade" }),
|
||||
|
||||
userId: varchar("user_id", { length: 255 }).references(() => users.id, { onDelete: "cascade" }),
|
||||
roleId: integer("role_id").references(() => roles.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
|
||||
grantedBy: varchar("granted_by", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
|
||||
permissionLevel: text("permission_level").notNull().default("view"),
|
||||
|
||||
expiresAt: varchar("expires_at", { length: 255 }),
|
||||
|
||||
createdAt: varchar("created_at", { length: 255 })
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
},
|
||||
// Same three lookup shapes as host_access: by grantee, by role, by snippet.
|
||||
(table) => [
|
||||
index("idx_snippet_access_user_id").on(table.userId),
|
||||
index("idx_snippet_access_snippet_id").on(table.snippetId),
|
||||
index("idx_snippet_access_role_id").on(table.roleId),
|
||||
],
|
||||
);
|
||||
|
||||
export const sshFolders = pgTable(
|
||||
"ssh_folders",
|
||||
{
|
||||
|
||||
@@ -550,48 +550,6 @@ export const sshCredentialUsage = sqliteTable(
|
||||
],
|
||||
);
|
||||
|
||||
export const snippets = sqliteTable(
|
||||
"snippets",
|
||||
{
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
name: text("name").notNull(),
|
||||
content: text("content").notNull(),
|
||||
description: text("description"),
|
||||
folder: text("folder"),
|
||||
order: integer("order").notNull().default(0),
|
||||
syncId: text("sync_id").unique(),
|
||||
createdAt: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
updatedAt: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
hostFilter: text("host_filter"),
|
||||
isNote: integer("is_note", { mode: "boolean" }).notNull().default(false),
|
||||
},
|
||||
(table) => [index("idx_snippets_user_id").on(table.userId)],
|
||||
);
|
||||
|
||||
export const snippetFolders = sqliteTable("snippet_folders", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
name: text("name").notNull(),
|
||||
color: text("color"),
|
||||
icon: text("icon"),
|
||||
syncId: text("sync_id").unique(),
|
||||
createdAt: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
updatedAt: text("updated_at")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
});
|
||||
|
||||
export const c2sTunnelPresets = sqliteTable("c2s_tunnel_presets", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
userId: text("user_id")
|
||||
@@ -609,39 +567,6 @@ export const c2sTunnelPresets = sqliteTable("c2s_tunnel_presets", {
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
});
|
||||
|
||||
export const snippetAccess = sqliteTable(
|
||||
"snippet_access",
|
||||
{
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
snippetId: integer("snippet_id")
|
||||
.notNull()
|
||||
.references(() => snippets.id, { onDelete: "cascade" }),
|
||||
|
||||
userId: text("user_id").references(() => users.id, { onDelete: "cascade" }),
|
||||
roleId: integer("role_id").references(() => roles.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
|
||||
grantedBy: text("granted_by")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
|
||||
permissionLevel: text("permission_level").notNull().default("view"),
|
||||
|
||||
expiresAt: text("expires_at"),
|
||||
|
||||
createdAt: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
},
|
||||
// Same three lookup shapes as host_access: by grantee, by role, by snippet.
|
||||
(table) => [
|
||||
index("idx_snippet_access_user_id").on(table.userId),
|
||||
index("idx_snippet_access_snippet_id").on(table.snippetId),
|
||||
index("idx_snippet_access_role_id").on(table.roleId),
|
||||
],
|
||||
);
|
||||
|
||||
export const sshFolders = sqliteTable(
|
||||
"ssh_folders",
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user