mirror of
https://github.com/louislam/uptime-kuma.git
synced 2026-06-22 20:00:35 +00:00
feat: add tracking support on status pages for rybbit analytics (#7525)
This commit is contained in:
committed by
GitHub
parent
a2ac12fb65
commit
a57aabf8c3
@@ -0,0 +1,33 @@
|
||||
const newValues = ["google", "umami", "plausible", "matomo", "rybbit"];
|
||||
const oldValues = ["google", "umami", "plausible", "matomo"];
|
||||
|
||||
/**
|
||||
* Rebuild the status_page.analytics_type enum with the given values, keeping existing data.
|
||||
* The column is dropped and re-created because SQLite's .enu().alter() does not replace the old CHECK constraint.
|
||||
* @param {import("knex").Knex} knex The knex instance
|
||||
* @param {string[]} allowedValues Allowed analytics_type values
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function rebuildAnalyticsType(knex, allowedValues) {
|
||||
const rows = await knex("status_page").whereNotNull("analytics_type").select("id", "analytics_type");
|
||||
|
||||
await knex.schema.alterTable("status_page", (table) => {
|
||||
table.dropColumn("analytics_type");
|
||||
});
|
||||
await knex.schema.alterTable("status_page", (table) => {
|
||||
table.enu("analytics_type", allowedValues).defaultTo(null);
|
||||
});
|
||||
|
||||
for (const row of rows) {
|
||||
await knex("status_page").where("id", row.id).update({ analytics_type: row.analytics_type });
|
||||
}
|
||||
}
|
||||
|
||||
exports.up = function (knex) {
|
||||
return rebuildAnalyticsType(knex, newValues);
|
||||
};
|
||||
|
||||
exports.down = async function (knex) {
|
||||
await knex("status_page").where("analytics_type", "rybbit").update({ analytics_type: null });
|
||||
await rebuildAnalyticsType(knex, oldValues);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ const googleAnalytics = require("./google-analytics");
|
||||
const umamiAnalytics = require("./umami-analytics");
|
||||
const plausibleAnalytics = require("./plausible-analytics");
|
||||
const matomoAnalytics = require("./matomo-analytics");
|
||||
const rybbitAnalytics = require("./rybbit-analytics");
|
||||
|
||||
/**
|
||||
* Returns a string that represents the javascript that is required to insert the selected Analytics' script
|
||||
@@ -22,6 +23,8 @@ function getAnalyticsScript(statusPage) {
|
||||
);
|
||||
case "matomo":
|
||||
return matomoAnalytics.getMatomoAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId);
|
||||
case "rybbit":
|
||||
return rybbitAnalytics.getRybbitAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -39,6 +42,7 @@ function isValidAnalyticsConfig(statusPage) {
|
||||
case "umami":
|
||||
case "plausible":
|
||||
case "matomo":
|
||||
case "rybbit":
|
||||
return statusPage.analyticsId != null && statusPage.analyticsScriptUrl != null;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
const jsesc = require("jsesc");
|
||||
const { escape } = require("html-escaper");
|
||||
|
||||
/**
|
||||
* Returns a string that represents the javascript that is required to insert the Rybbit Analytics script
|
||||
* into a webpage.
|
||||
* @param {string} scriptUrl the Rybbit Analytics script url.
|
||||
* @param {string} siteId Site ID to use with the Rybbit Analytics script.
|
||||
* @returns {string} HTML script tags to inject into page
|
||||
*/
|
||||
function getRybbitAnalyticsScript(scriptUrl, siteId) {
|
||||
let escapedScriptUrlJS = jsesc(scriptUrl, { isScriptContext: true });
|
||||
let escapedSiteIdJS = jsesc(siteId, { isScriptContext: true });
|
||||
|
||||
if (escapedScriptUrlJS) {
|
||||
escapedScriptUrlJS = escapedScriptUrlJS.trim();
|
||||
}
|
||||
|
||||
if (escapedSiteIdJS) {
|
||||
escapedSiteIdJS = escapedSiteIdJS.trim();
|
||||
}
|
||||
|
||||
// Escape the Script url for use in an HTML attribute.
|
||||
let escapedScriptUrlHTMLAttribute = escape(escapedScriptUrlJS);
|
||||
|
||||
// Escape the site id for use in an HTML attribute.
|
||||
let escapedSiteIdHTMLAttribute = escape(escapedSiteIdJS);
|
||||
|
||||
return `
|
||||
<script defer src="${escapedScriptUrlHTMLAttribute}" data-site-id="${escapedSiteIdHTMLAttribute}"></script>
|
||||
`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getRybbitAnalyticsScript,
|
||||
};
|
||||
@@ -339,7 +339,7 @@ module.exports.statusPageSocketHandler = (socket) => {
|
||||
statusPage.modified_date = R.isoDateTime();
|
||||
statusPage.analytics_id = config.analyticsId;
|
||||
statusPage.analytics_script_url = config.analyticsScriptUrl;
|
||||
const validAnalyticsTypes = ["google", "umami", "plausible", "matomo"];
|
||||
const validAnalyticsTypes = ["google", "umami", "plausible", "matomo", "rybbit"];
|
||||
if (config.analyticsType !== null && !validAnalyticsTypes.includes(config.analyticsType)) {
|
||||
throw new Error("Invalid analytics type");
|
||||
}
|
||||
|
||||
@@ -1383,6 +1383,7 @@
|
||||
"Plausible": "Plausible",
|
||||
"Matomo": "Matomo",
|
||||
"Umami": "Umami",
|
||||
"Rybbit": "Rybbit",
|
||||
"Disable URL in Notification": "Disable URL in Notification",
|
||||
"Suppress Notifications": "Suppress Notifications",
|
||||
"discordSuppressNotificationsHelptext": "When enabled, messages will be posted to the channel but won't trigger push or desktop notifications for recipients.",
|
||||
|
||||
@@ -161,6 +161,7 @@
|
||||
<option value="umami">{{ $t("Umami") }}</option>
|
||||
<option value="plausible">{{ $t("Plausible") }}</option>
|
||||
<option value="matomo">{{ $t("Matomo") }}</option>
|
||||
<option value="rybbit">{{ $t("Rybbit") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user