This commit is contained in:
Louis Lam 2023-01-09 00:35:52 +08:00
parent 42f1a9ff34
commit 7bcef8edf9
9 changed files with 114 additions and 30 deletions

View File

@ -4,6 +4,7 @@ const { setSetting, setting } = require("./util-server");
const { log, sleep } = require("../src/util");
const dayjs = require("dayjs");
const knex = require("knex");
const { PluginsManager } = require("./plugins-manager");
/**
* Database & App Data Folder
@ -83,6 +84,13 @@ class Database {
static init(args) {
// Data Directory (must be end with "/")
Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/";
// Plugin feature is working only if the dataDir = "./data";
if (Database.dataDir !== "./data/") {
log.warn("PLUGIN", "Warning: In order to enable plugin feature, you need to use the default data directory: ./data/");
PluginsManager.disable = true;
}
Database.path = Database.dataDir + "kuma.db";
if (! fs.existsSync(Database.dataDir)) {
fs.mkdirSync(Database.dataDir, { recursive: true });

View File

@ -617,6 +617,15 @@ class Monitor extends BeanModel {
}
}
bean.ping = dayjs().valueOf() - startTime;
} else if (this.type in UptimeKumaServer.monitorTypeList) {
let startTime = dayjs().valueOf();
const monitorType = UptimeKumaServer.monitorTypeList[this.type];
await monitorType.check(this, bean);
if (!bean.ping) {
bean.ping = dayjs().valueOf() - startTime;
}
} else {
bean.msg = "Unknown Monitor Type";
bean.status = PENDING;

View File

@ -1,5 +1,19 @@
class MonitorType {
name = undefined;
/**
*
* @param {Monitor} monitor
* @param {Heartbeat} heartbeat
* @returns {Promise<void>}
*/
async check(monitor, heartbeat) {
throw new Error("You need to override check()");
}
}
module.exports = MonitorType;
module.exports = {
MonitorType,
};

13
server/plugin.js Normal file
View File

@ -0,0 +1,13 @@
class Plugin {
async install() {
}
async uninstall() {
}
}
module.exports = {
Plugin,
};

View File

@ -4,9 +4,11 @@ const path = require("path");
class PluginsManager {
static disable = false;
/**
* Plugin List
* @type {Plugin[]}
* @type {PluginWrapper[]}
*/
pluginList = [];
@ -20,28 +22,34 @@ class PluginsManager {
* @param {UptimeKumaServer} server
* @param {string} dir
*/
constructor(server, dir) {
this.pluginsDir = dir;
constructor(server) {
if (!PluginsManager.disable) {
this.pluginsDir = "./data/plugins/";
if (! fs.existsSync(this.pluginsDir)) {
fs.mkdirSync(this.pluginsDir, { recursive: true });
}
log.debug("plugin", "Scanning plugin directory");
let list = fs.readdirSync(this.pluginsDir);
this.pluginList = [];
for (let item of list) {
let plugin = new Plugin(server, this.pluginsDir + item);
try {
plugin.load();
this.pluginList.push(plugin);
} catch (e) {
log.error("plugin", "Failed to load plugin: " + this.pluginsDir + item);
log.error("plugin", "Reason: " + e.message);
if (! fs.existsSync(this.pluginsDir)) {
fs.mkdirSync(this.pluginsDir, { recursive: true });
}
log.debug("plugin", "Scanning plugin directory");
let list = fs.readdirSync(this.pluginsDir);
this.pluginList = [];
for (let item of list) {
let plugin = new PluginWrapper(server, this.pluginsDir + item);
try {
plugin.load();
this.pluginList.push(plugin);
} catch (e) {
log.error("plugin", "Failed to load plugin: " + this.pluginsDir + item);
log.error("plugin", "Reason: " + e.message);
}
}
} else {
log.warn("PLUGIN", "Skip scanning plugin directory");
}
}
/**
@ -71,7 +79,7 @@ class PluginsManager {
}
}
class Plugin {
class PluginWrapper {
server = undefined;
pluginDir = undefined;
@ -129,5 +137,5 @@ class Plugin {
module.exports = {
PluginsManager,
Plugin
PluginWrapper
};

View File

@ -167,7 +167,7 @@ let needSetup = false;
Database.init(args);
await initDatabase(testMode);
await server.initAfterDatabaseReady();
server.loadPlugins(Database.dataDir + "plugins/");
server.loadPlugins();
server.entryPage = await Settings.get("entryPage");
await StatusPage.loadDomainMappingList();

View File

@ -55,6 +55,14 @@ class UptimeKumaServer {
*/
pluginsManager = null;
/**
*
* @type {{}}
*/
static monitorTypeList = {
};
static getInstance(args) {
if (UptimeKumaServer.instance == null) {
UptimeKumaServer.instance = new UptimeKumaServer(args);
@ -249,11 +257,34 @@ class UptimeKumaServer {
}
loadPlugins(dir) {
this.pluginsManager = new PluginsManager(this, dir);
this.pluginsManager = new PluginsManager(this);
}
/**
*
* @param {MonitorType} monitorType
*/
addMonitorType(monitorType) {
// TODO
if (monitorType instanceof MonitorType && monitorType.name) {
if (monitorType.name in UptimeKumaServer.monitorTypeList) {
log.error("", "Conflict Monitor Type name");
}
UptimeKumaServer.monitorTypeList[monitorType.name] = monitorType;
} else {
log.error("", "Invalid Monitor Type: " + monitorType.name);
}
}
/**
*
* @param {MonitorType} monitorType
*/
removeMonitorType(monitorType) {
if (UptimeKumaServer.monitorTypeList[monitorType.name] === monitorType) {
delete UptimeKumaServer.monitorTypeList[monitorType.name];
} else {
log.error("", "Remove MonitorType failed: " + monitorType.name);
}
}
}
@ -264,3 +295,4 @@ module.exports = {
// Must be at the end
const MaintenanceTimeslot = require("./model/maintenance_timeslot");
const { MonitorType } = require("./monitor-types/monitor-type");

View File

@ -64,7 +64,7 @@
<optgroup :label="$t('Custom Monitor Type')">
<option value="browser">
HTTP(s) with Real Browsers (Chromium or Firefox)
(Early Access/WIP) HTTP(s) (Browser Engine)
</option>
</optgroup>
</select>
@ -77,7 +77,7 @@
</div>
<!-- URL -->
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3">
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'browser' " class="my-3">
<label for="url" class="form-label">{{ $t("URL") }}</label>
<input id="url" v-model="monitor.url" type="url" class="form-control" pattern="https?://.+" required>
</div>
@ -99,10 +99,10 @@
</div>
<!-- Keyword -->
<div v-if="monitor.type === 'keyword' || monitor.type === 'grpc-keyword' " class="my-3">
<div v-if="monitor.type === 'keyword' || monitor.type === 'grpc-keyword'" class="my-3">
<label for="keyword" class="form-label">{{ $t("Keyword") }}</label>
<input id="keyword" v-model="monitor.keyword" type="text" class="form-control" required>
<div class="form-text">
<div v-if="monitor.type === 'keyword' || monitor.type === 'grpc-keyword'" class="form-text">
{{ $t("keywordDescription") }}
</div>
</div>

Binary file not shown.