mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-08-09 03:58:27 +08:00
Added basic web interface for API keys
Web interfaces for manging API keys have been added however translation keys are still required. Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
This commit is contained in:
@@ -113,6 +113,31 @@ async function sendProxyList(socket) {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit API key list to client
|
||||
* @param {Socket} socket Socket.io socket instance
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function sendAPIKeyList(socket) {
|
||||
const timeLogger = new TimeLogger();
|
||||
|
||||
let result = [];
|
||||
const list = await R.find(
|
||||
"api_key",
|
||||
"user_id=?",
|
||||
[ socket.userID ],
|
||||
);
|
||||
|
||||
for (let bean of list) {
|
||||
result.push(bean.toPublicJSON());
|
||||
}
|
||||
|
||||
io.to(socket.userID).emit("apiKeyList", result);
|
||||
timeLogger.print("Sent API Key List");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits the version information to the client.
|
||||
* @param {Socket} socket Socket.io socket instance
|
||||
@@ -157,6 +182,7 @@ module.exports = {
|
||||
sendImportantHeartbeatList,
|
||||
sendHeartbeatList,
|
||||
sendProxyList,
|
||||
sendAPIKeyList,
|
||||
sendInfo,
|
||||
sendDockerHostList
|
||||
};
|
||||
|
75
server/model/api_key.js
Normal file
75
server/model/api_key.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
const { R } = require("redbean-node");
|
||||
|
||||
class APIKey extends BeanModel {
|
||||
/**
|
||||
* Get the current status of this API key
|
||||
*/
|
||||
getStatus() {
|
||||
let expired = false;
|
||||
if (expired) {
|
||||
return "expired";
|
||||
} else if (this.active) {
|
||||
return "active";
|
||||
} else if (!this.active) {
|
||||
return "inactive";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object that ready to parse to JSON
|
||||
* @returns {Object}
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
key: this.key,
|
||||
name: this.name,
|
||||
userID: this.user_id,
|
||||
createdDate: this.created_date,
|
||||
active: this.active,
|
||||
expires: this.expires,
|
||||
status: this.getStatus(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object that ready to parse to JSON with sensitive fields
|
||||
* removed
|
||||
* @returns {Object}
|
||||
*/
|
||||
toPublicJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
userID: this.user_id,
|
||||
createdDate: this.created_date,
|
||||
active: this.active,
|
||||
expires: this.expires,
|
||||
status: this.getStatus(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new API Key and store it in the database
|
||||
* @param {Object} key Object sent by client
|
||||
* @param {int} userID ID of socket user
|
||||
* @returns {Promise<bean>}
|
||||
*/
|
||||
static async save(key, userID) {
|
||||
let bean;
|
||||
bean = R.dispense("api_key");
|
||||
|
||||
bean.key = key.key;
|
||||
bean.name = key.name;
|
||||
bean.user_id = userID;
|
||||
bean.active = key.active;
|
||||
bean.expires = key.expires;
|
||||
|
||||
await R.store(bean);
|
||||
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIKey;
|
@@ -126,7 +126,7 @@ if (config.demoMode) {
|
||||
}
|
||||
|
||||
// Must be after io instantiation
|
||||
const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList, sendDockerHostList } = require("./client");
|
||||
const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList, sendDockerHostList, sendAPIKeyList } = require("./client");
|
||||
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
|
||||
const databaseSocketHandler = require("./socket-handlers/database-socket-handler");
|
||||
const TwoFA = require("./2fa");
|
||||
@@ -135,6 +135,7 @@ const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudfl
|
||||
const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler");
|
||||
const { dockerSocketHandler } = require("./socket-handlers/docker-socket-handler");
|
||||
const { maintenanceSocketHandler } = require("./socket-handlers/maintenance-socket-handler");
|
||||
const { apiKeySocketHandler } = require("./socket-handlers/api-key-socket-handler");
|
||||
const { generalSocketHandler } = require("./socket-handlers/general-socket-handler");
|
||||
const { Settings } = require("./settings");
|
||||
const { CacheableDnsHttpAgent } = require("./cacheable-dns-http-agent");
|
||||
@@ -1490,6 +1491,7 @@ let needSetup = false;
|
||||
proxySocketHandler(socket);
|
||||
dockerSocketHandler(socket);
|
||||
maintenanceSocketHandler(socket);
|
||||
apiKeySocketHandler(socket);
|
||||
generalSocketHandler(socket, server);
|
||||
|
||||
log.debug("server", "added all socket handlers");
|
||||
@@ -1597,6 +1599,7 @@ async function afterLogin(socket, user) {
|
||||
sendNotificationList(socket);
|
||||
sendProxyList(socket);
|
||||
sendDockerHostList(socket);
|
||||
sendAPIKeyList(socket);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
|
144
server/socket-handlers/api-key-socket-handler.js
Normal file
144
server/socket-handlers/api-key-socket-handler.js
Normal file
@@ -0,0 +1,144 @@
|
||||
const { checkLogin } = require("../util-server");
|
||||
const { log } = require("../../src/util");
|
||||
const { R } = require("redbean-node");
|
||||
const crypto = require("crypto");
|
||||
const passwordHash = require("../password-hash");
|
||||
const apicache = require("../modules/apicache");
|
||||
const APIKey = require("../model/api_key");
|
||||
const { sendAPIKeyList } = require("../client");
|
||||
|
||||
/**
|
||||
* Handlers for Maintenance
|
||||
* @param {Socket} socket Socket.io instance
|
||||
*/
|
||||
module.exports.apiKeySocketHandler = (socket) => {
|
||||
// Add a new api key
|
||||
socket.on("addAPIKey", async (key, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
let clearKey = crypto.randomUUID();
|
||||
let hashedKey = passwordHash.generate(clearKey);
|
||||
key["key"] = hashedKey;
|
||||
let bean = await APIKey.save(key, socket.userID);
|
||||
|
||||
log.debug("apikeys", "Added API Key");
|
||||
log.debug("apikeys", key);
|
||||
|
||||
// Append key ID to start of key seperated by -, used to get
|
||||
// correct hash when validating key.
|
||||
let formattedKey = bean.id + "-" + clearKey;
|
||||
await sendAPIKeyList(socket);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Added Successfully.",
|
||||
key: formattedKey,
|
||||
keyID: bean.id,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("getAPIKeyList", async (callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
await sendAPIKeyList(socket);
|
||||
callback({
|
||||
ok: true,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("deleteAPIKey", async (keyID, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
log.debug("apikeys", `Deleted API Key: ${keyID} User ID: ${socket.userID}`);
|
||||
|
||||
await R.exec("DELETE FROM api_key WHERE id = ? AND user_id = ? ", [
|
||||
keyID,
|
||||
socket.userID,
|
||||
]);
|
||||
|
||||
apicache.clear();
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Deleted Successfully.",
|
||||
});
|
||||
|
||||
await sendAPIKeyList(socket);
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("disableAPIKey", async (keyID, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
log.debug("apikeys", `Disabled Key: ${keyID} User ID: ${socket.userID}`);
|
||||
|
||||
await R.exec("UPDATE api_key SET active = 0 WHERE id = ? ", [
|
||||
keyID,
|
||||
]);
|
||||
|
||||
apicache.clear();
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Disabled Successfully.",
|
||||
});
|
||||
|
||||
await sendAPIKeyList(socket);
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("enableAPIKey", async (keyID, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
log.debug("apikeys", `Enabled Key: ${keyID} User ID: ${socket.userID}`);
|
||||
|
||||
await R.exec("UPDATE api_key SET active = 1 WHERE id = ? ", [
|
||||
keyID,
|
||||
]);
|
||||
|
||||
apicache.clear();
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Enabled Successfully",
|
||||
});
|
||||
|
||||
await sendAPIKeyList(socket);
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user