mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
fix: update the permission for create and read the api keys
This commit is contained in:
+6
-4
@@ -35,7 +35,8 @@ export {
|
||||
|
||||
// API keys
|
||||
export {
|
||||
getApiKeys, getApiKeyById, createApiKey, updateApiKey, deleteApiKey, validateApiKey,
|
||||
getApiKeys, getApiKeysByOwnerId, getApiKeyById, getApiKeyByIdAndOwnerId,
|
||||
createApiKey, updateApiKey, deleteApiKey, validateApiKey,
|
||||
} from "./repos/apiKeysRepo.js";
|
||||
|
||||
// Combos
|
||||
@@ -84,7 +85,7 @@ export async function exportDb() {
|
||||
providerConnections: db.all(`SELECT * FROM providerConnections`).map((r) => ({ ...parseJson(r.data, {}), id: r.id, provider: r.provider, authType: r.authType, name: r.name, email: r.email, priority: r.priority, isActive: r.isActive === 1, createdAt: r.createdAt, updatedAt: r.updatedAt })),
|
||||
providerNodes: db.all(`SELECT * FROM providerNodes`).map((r) => ({ ...parseJson(r.data, {}), id: r.id, type: r.type, name: r.name, createdAt: r.createdAt, updatedAt: r.updatedAt })),
|
||||
proxyPools: db.all(`SELECT * FROM proxyPools`).map((r) => ({ ...parseJson(r.data, {}), id: r.id, isActive: r.isActive === 1, testStatus: r.testStatus, createdAt: r.createdAt, updatedAt: r.updatedAt })),
|
||||
apiKeys: db.all(`SELECT * FROM apiKeys`).map((r) => ({ id: r.id, key: r.key, name: r.name, machineId: r.machineId, isActive: r.isActive === 1, createdAt: r.createdAt })),
|
||||
apiKeys: db.all(`SELECT * FROM apiKeys`).map((r) => ({ id: r.id, key: r.key, name: r.name, machineId: r.machineId, ownerId: r.ownerId, isActive: r.isActive === 1, createdAt: r.createdAt })),
|
||||
combos: db.all(`SELECT * FROM combos`).map((r) => ({ id: r.id, name: r.name, kind: r.kind, models: parseJson(r.models, []), createdAt: r.createdAt, updatedAt: r.updatedAt })),
|
||||
modelAliases: {},
|
||||
customModels: [],
|
||||
@@ -164,10 +165,11 @@ export async function importDb(payload) {
|
||||
[id, isActive === false ? 0 : 1, testStatus || "unknown", stringifyJson(rest), createdAt || new Date().toISOString(), updatedAt || new Date().toISOString()]
|
||||
);
|
||||
}
|
||||
const defaultKeyOwner = db.get(`SELECT id FROM users WHERE role = 'admin' ORDER BY createdAt ASC LIMIT 1`)?.id || null;
|
||||
for (const k of payload.apiKeys || []) {
|
||||
db.run(
|
||||
`INSERT OR REPLACE INTO apiKeys(id, key, name, machineId, isActive, createdAt) VALUES(?, ?, ?, ?, ?, ?)`,
|
||||
[k.id, k.key, k.name || null, k.machineId || null, k.isActive === false ? 0 : 1, k.createdAt || new Date().toISOString()]
|
||||
`INSERT OR REPLACE INTO apiKeys(id, key, name, machineId, ownerId, isActive, createdAt) VALUES(?, ?, ?, ?, ?, ?, ?)`,
|
||||
[k.id, k.key, k.name || null, k.machineId || null, k.ownerId || defaultKeyOwner, k.isActive === false ? 0 : 1, k.createdAt || new Date().toISOString()]
|
||||
);
|
||||
}
|
||||
for (const c of payload.combos || []) {
|
||||
|
||||
@@ -111,6 +111,7 @@ function syncSchemaFromTables(adapter) {
|
||||
// ─── Legacy JSON import (one-time) ───────────────────────────────────────
|
||||
function importLegacyMain(adapter, data) {
|
||||
if (!data || typeof data !== "object") return;
|
||||
const defaultKeyOwner = adapter.get(`SELECT id FROM users WHERE role = 'admin' ORDER BY createdAt ASC LIMIT 1`)?.id || null;
|
||||
|
||||
if (data.settings) {
|
||||
adapter.run(`INSERT INTO settings(id, data) VALUES(1, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data`, [stringifyJson(data.settings)]);
|
||||
@@ -142,8 +143,8 @@ function importLegacyMain(adapter, data) {
|
||||
|
||||
importWithAssertion(adapter, "apiKeys", data.apiKeys || [], (k) => {
|
||||
adapter.run(
|
||||
`INSERT OR REPLACE INTO apiKeys(id, key, name, machineId, isActive, createdAt) VALUES(?, ?, ?, ?, ?, ?)`,
|
||||
[k.id, k.key, k.name || null, k.machineId || null, k.isActive === false ? 0 : 1, k.createdAt || new Date().toISOString()]
|
||||
`INSERT OR REPLACE INTO apiKeys(id, key, name, machineId, ownerId, isActive, createdAt) VALUES(?, ?, ?, ?, ?, ?, ?)`,
|
||||
[k.id, k.key, k.name || null, k.machineId || null, k.ownerId || defaultKeyOwner, k.isActive === false ? 0 : 1, k.createdAt || new Date().toISOString()]
|
||||
);
|
||||
}, (k) => ({ id: k.id ?? null, name: k.name ?? null }));
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// API keys are private to the dashboard account that created them. Keys from
|
||||
// pre-multi-user installations are retained as keys owned by the first admin.
|
||||
export default {
|
||||
version: 3,
|
||||
name: "api-key-owners",
|
||||
up(db) {
|
||||
const columns = db.all(`PRAGMA table_info(apiKeys)`);
|
||||
if (!columns.some((column) => column.name === "ownerId")) {
|
||||
db.exec(`ALTER TABLE apiKeys ADD COLUMN ownerId TEXT`);
|
||||
}
|
||||
|
||||
const admin = db.get(`SELECT id FROM users WHERE role = 'admin' ORDER BY createdAt ASC LIMIT 1`);
|
||||
if (admin) {
|
||||
db.run(`UPDATE apiKeys SET ownerId = ? WHERE ownerId IS NULL OR ownerId = ''`, [admin.id]);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -3,8 +3,9 @@
|
||||
// Versions MUST be unique and monotonically increasing.
|
||||
import m001 from "./001-initial.js";
|
||||
import m002 from "./002-users-table.js";
|
||||
import m003 from "./003-api-key-owners.js";
|
||||
|
||||
export const MIGRATIONS = [m001, m002].sort((a, b) => a.version - b.version);
|
||||
export const MIGRATIONS = [m001, m002, m003].sort((a, b) => a.version - b.version);
|
||||
|
||||
export function latestVersion() {
|
||||
return MIGRATIONS.length ? MIGRATIONS[MIGRATIONS.length - 1].version : 0;
|
||||
|
||||
@@ -8,6 +8,7 @@ function rowToKey(row) {
|
||||
key: row.key,
|
||||
name: row.name,
|
||||
machineId: row.machineId,
|
||||
ownerId: row.ownerId,
|
||||
isActive: row.isActive === 1 || row.isActive === true,
|
||||
createdAt: row.createdAt,
|
||||
};
|
||||
@@ -19,13 +20,25 @@ export async function getApiKeys() {
|
||||
return rows.map(rowToKey);
|
||||
}
|
||||
|
||||
export async function getApiKeysByOwnerId(ownerId) {
|
||||
const db = await getAdapter();
|
||||
const rows = db.all(`SELECT * FROM apiKeys WHERE ownerId = ? ORDER BY createdAt ASC`, [ownerId]);
|
||||
return rows.map(rowToKey);
|
||||
}
|
||||
|
||||
export async function getApiKeyById(id) {
|
||||
const db = await getAdapter();
|
||||
const row = db.get(`SELECT * FROM apiKeys WHERE id = ?`, [id]);
|
||||
return rowToKey(row);
|
||||
}
|
||||
|
||||
export async function createApiKey(name, machineId) {
|
||||
export async function getApiKeyByIdAndOwnerId(id, ownerId) {
|
||||
const db = await getAdapter();
|
||||
const row = db.get(`SELECT * FROM apiKeys WHERE id = ? AND ownerId = ?`, [id, ownerId]);
|
||||
return rowToKey(row);
|
||||
}
|
||||
|
||||
export async function createApiKey(name, machineId, ownerId = null) {
|
||||
if (!machineId) throw new Error("machineId is required");
|
||||
const db = await getAdapter();
|
||||
const { generateApiKeyWithMachine } = await import("@/shared/utils/apiKey");
|
||||
@@ -35,12 +48,13 @@ export async function createApiKey(name, machineId) {
|
||||
name,
|
||||
key: result.key,
|
||||
machineId,
|
||||
ownerId,
|
||||
isActive: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
db.run(
|
||||
`INSERT INTO apiKeys(id, key, name, machineId, isActive, createdAt) VALUES(?, ?, ?, ?, ?, ?)`,
|
||||
[apiKey.id, apiKey.key, apiKey.name, apiKey.machineId, 1, apiKey.createdAt]
|
||||
`INSERT INTO apiKeys(id, key, name, machineId, ownerId, isActive, createdAt) VALUES(?, ?, ?, ?, ?, ?, ?)`,
|
||||
[apiKey.id, apiKey.key, apiKey.name, apiKey.machineId, apiKey.ownerId, 1, apiKey.createdAt]
|
||||
);
|
||||
return apiKey;
|
||||
}
|
||||
@@ -53,8 +67,8 @@ export async function updateApiKey(id, data) {
|
||||
if (!row) return;
|
||||
const merged = { ...rowToKey(row), ...data };
|
||||
db.run(
|
||||
`UPDATE apiKeys SET key = ?, name = ?, machineId = ?, isActive = ? WHERE id = ?`,
|
||||
[merged.key, merged.name, merged.machineId, merged.isActive ? 1 : 0, id]
|
||||
`UPDATE apiKeys SET key = ?, name = ?, machineId = ?, ownerId = ?, isActive = ? WHERE id = ?`,
|
||||
[merged.key, merged.name, merged.machineId, merged.ownerId, merged.isActive ? 1 : 0, id]
|
||||
);
|
||||
result = merged;
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// pre-change safety backup in migrate.js: when the stored version is lower,
|
||||
// one lightweight DB backup is taken before applying schema changes. Forgetting
|
||||
// to bump only skips that backup — it does NOT break the additive auto-sync.
|
||||
export const SCHEMA_VERSION = 2;
|
||||
export const SCHEMA_VERSION = 3;
|
||||
|
||||
export const PRAGMA_SQL = `
|
||||
PRAGMA journal_mode = WAL;
|
||||
@@ -96,10 +96,14 @@ export const TABLES = {
|
||||
key: "TEXT UNIQUE NOT NULL",
|
||||
name: "TEXT",
|
||||
machineId: "TEXT",
|
||||
ownerId: "TEXT",
|
||||
isActive: "INTEGER DEFAULT 1",
|
||||
createdAt: "TEXT NOT NULL",
|
||||
},
|
||||
indexes: ["CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)"],
|
||||
indexes: [
|
||||
"CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_ak_owner ON apiKeys(ownerId)",
|
||||
],
|
||||
},
|
||||
combos: {
|
||||
columns: {
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@ export {
|
||||
createProviderNode, updateProviderNode, deleteProviderNode,
|
||||
getProxyPools, getProxyPoolById,
|
||||
createProxyPool, updateProxyPool, deleteProxyPool,
|
||||
getApiKeys, getApiKeyById, createApiKey, updateApiKey, deleteApiKey, validateApiKey,
|
||||
getApiKeys, getApiKeysByOwnerId, getApiKeyById, getApiKeyByIdAndOwnerId,
|
||||
createApiKey, updateApiKey, deleteApiKey, validateApiKey,
|
||||
getCombos, getComboById, getComboByName,
|
||||
createCombo, updateCombo, deleteCombo,
|
||||
getModelAliases, setModelAlias, deleteModelAlias,
|
||||
|
||||
Reference in New Issue
Block a user