From 92ea4b64f90b7c0691a9321e4fbf7b1107e43e0a Mon Sep 17 00:00:00 2001 From: Raj Nandan Sharma Date: Wed, 27 Nov 2024 11:41:00 +0530 Subject: [PATCH 01/13] feat: first commit for the release of version 2.0.0 --- build.js | 152 +++++- docs/customize-site.md | 7 +- docs/incident-management.md | 10 +- docs/status-badges.md | 34 +- package-lock.json | 419 ++++++++++++++- package.json | 5 +- src/kener.css | 189 ++++++- src/lib/components/incident.svelte | 20 +- src/lib/components/monitor.svelte | 499 ++++++++---------- src/lib/server/alerting.js | 212 ++++++++ src/lib/server/check.js | 1 - src/lib/server/constants.js | 83 ++- src/lib/server/cron-minute.js | 227 +++++--- src/lib/server/db/db.js | 13 + src/lib/server/db/sqlite.js | 266 ++++++++++ src/lib/server/dns.js | 111 ++++ src/lib/server/github.js | 32 +- src/lib/server/ninety.js | 156 ------ src/lib/server/notification/discord.js | 89 ++++ src/lib/server/notification/notif.js | 29 + src/lib/server/notification/slack.js | 100 ++++ src/lib/server/notification/webhook.js | 54 ++ src/lib/server/page.js | 167 +++++- src/lib/server/startup.js | 31 +- src/lib/server/tool.js | 90 +++- src/lib/server/webhook.js | 70 +-- src/routes/(docs)/+layout.svelte | 6 +- src/routes/(kener)/+layout.server.js | 1 - src/routes/(kener)/+layout.svelte | 17 +- src/routes/(kener)/+page.server.js | 1 - src/routes/(kener)/+page.svelte | 2 +- src/routes/(kener)/api/incident/+server.js | 2 +- .../api/incident/[incidentNumber]/+server.js | 1 - .../[incidentNumber]/comment/+server.js | 1 - .../[incidentNumber]/status/+server.js | 2 - src/routes/(kener)/api/status/+server.js | 3 +- src/routes/(kener)/api/today/+server.js | 22 +- src/routes/(kener)/badge/[tag]/dot/+server.js | 16 +- .../(kener)/badge/[tag]/status/+server.js | 16 +- .../(kener)/badge/[tag]/uptime/+server.js | 54 +- .../category-[category]/+page.server.js | 2 - .../(kener)/embed-[tag]/+page.server.js | 2 - src/routes/(kener)/embed-[tag]/js/+server.js | 1 - .../(kener)/incident/[id]/+page.server.js | 1 - src/routes/(kener)/incident/[id]/+page.svelte | 2 +- .../(kener)/incident/[id]/comments/+server.js | 1 - 46 files changed, 2518 insertions(+), 701 deletions(-) create mode 100644 src/lib/server/alerting.js create mode 100644 src/lib/server/db/db.js create mode 100644 src/lib/server/db/sqlite.js create mode 100644 src/lib/server/dns.js delete mode 100644 src/lib/server/ninety.js create mode 100644 src/lib/server/notification/discord.js create mode 100644 src/lib/server/notification/notif.js create mode 100644 src/lib/server/notification/slack.js create mode 100644 src/lib/server/notification/webhook.js diff --git a/build.js b/build.js index 45d38ecf..7a5f585a 100644 --- a/build.js +++ b/build.js @@ -4,11 +4,14 @@ import axios from "axios"; import { IsValidURL, checkIfDuplicateExists, - getWordsStartingWithDollar, IsValidHTTPMethod, - ValidateIpAddress + ValidateIpAddress, + IsValidHost, + IsValidRecordType, + IsValidNameServer } from "./src/lib/server/tool.js"; import { API_TIMEOUT, AnalyticsProviders } from "./src/lib/server/constants.js"; +import { GetAllGHLabels, CreateGHLabel } from "./src/lib/server/github.js"; const configPathFolder = "./config"; const databaseFolder = process.argv[2] || "./database"; @@ -43,7 +46,10 @@ async function Build() { site.github.repo === undefined ) { console.log("github owner and repo are required"); - process.exit(1); + site.hasGithub = false; + // process.exit(1); + } else { + site.hasGithub = true; } const FOLDER_DB = databaseFolder; @@ -57,6 +63,7 @@ async function Build() { let tag = monitor.tag; let hasAPI = monitor.api !== undefined && monitor.api !== null; let hasPing = monitor.ping !== undefined && monitor.ping !== null; + let hasDNS = monitor.dns !== undefined && monitor.dns !== null; let folderName = name.replace(/[^a-z0-9]/gi, "-").toLowerCase(); monitors[i].folderName = folderName; @@ -121,6 +128,44 @@ async function Build() { } monitors[i].hasPing = true; } + if (hasDNS) { + let dnsData = monitor.dns; + let domain = dnsData.host; + //check if domain is valid + if (!!!domain || !IsValidHost(domain)) { + console.log("domain is not valid"); + process.exit(1); + } + + let recordType = dnsData.lookupRecord; + //check if recordType is valid + if (!!!recordType || !IsValidRecordType(recordType)) { + console.log("recordType is not valid"); + process.exit(1); + } + + let nameServer = dnsData.nameServer; + //check if nameserver is valid + if (!!nameServer && !IsValidNameServer(nameServer)) { + console.log("nameServer is not valid"); + process.exit(1); + } + + // matchType: "ANY" # ANY, ALL + let matchType = dnsData.matchType; + if (!!!matchType || (matchType !== "ANY" && matchType !== "ALL")) { + console.log("matchType is not valid"); + process.exit(1); + } + + //values array of string at least one + let values = dnsData.values; + if (!!!values || !Array.isArray(values) || values.length === 0) { + console.log("values is not valid"); + process.exit(1); + } + monitors[i].hasDNS = true; + } if (hasAPI) { let url = monitor.api.url; let method = monitor.api.method; @@ -227,25 +272,7 @@ async function Build() { } } - monitors[i].path0Day = `${FOLDER_DB}/${folderName}.0day.utc.json`; - monitors[i].path90Day = `${FOLDER_DB}/${folderName}.90day.utc.json`; monitors[i].hasAPI = hasAPI; - - //secrets can be in url/body/headers - //match in monitor.url if a words starts with $, get the word - const requiredSecrets = getWordsStartingWithDollar( - `${monitor.url} ${monitor.body} ${JSON.stringify(monitor.headers)}` - ).map((x) => x.substr(1)); - - //iterate over process.env - for (const [key, value] of Object.entries(process.env)) { - if (requiredSecrets.indexOf(key) !== -1) { - envSecrets.push({ - find: `$${key}`, - replace: value - }); - } - } } if (site.github.incidentSince === undefined || site.github.incidentSince === null) { @@ -260,6 +287,22 @@ async function Build() { if (site.themeToggle === undefined) { site.themeToggle = true; } + if (site.barStyle === undefined) { + site.barStyle = "FULL"; + } + if (site.barRoundness === undefined) { + site.barRoundness = "none"; + } else { + site.barRoundness = site.barRoundness.toLowerCase(); + } + if (site.summaryStyle === undefined) { + site.summaryStyle = "DAY"; + } + site.colors = { + UP: site.colors?.UP || "#4ead94", + DOWN: site.colors?.DOWN || "#ca3038", + DEGRADED: site.colors?.DEGRADED || "#e6ca61" + }; if (!!site.analytics) { const providers = {}; @@ -301,6 +344,73 @@ async function Build() { console.log(error); process.exit(1); } + + if (site.hasGithub) { + const ghowner = site.github.owner; + const ghrepo = site.github.repo; + const ghlabels = await GetAllGHLabels(ghowner, ghrepo); + const tagsAndDescription = monitors.map((monitor) => { + return { tag: monitor.tag, description: monitor.name }; + }); + //add incident label if does not exist + + if (ghlabels.indexOf("incident") === -1) { + await CreateGHLabel(ghowner, ghrepo, "incident", "Status of the site"); + } + if (ghlabels.indexOf("resolved") === -1) { + await CreateGHLabel(ghowner, ghrepo, "resolved", "Incident is resolved", "65dba6"); + } + if (ghlabels.indexOf("identified") === -1) { + await CreateGHLabel(ghowner, ghrepo, "identified", "Incident is Identified", "EBE3D5"); + } + if (ghlabels.indexOf("manual") === -1) { + await CreateGHLabel(ghowner, ghrepo, "manual", "Manually Created Incident", "6499E9"); + } + if (ghlabels.indexOf("auto") === -1) { + await CreateGHLabel( + ghowner, + ghrepo, + "auto", + "Automatically Created Incident", + "D6C0B3" + ); + } + if (ghlabels.indexOf("investigating") === -1) { + await CreateGHLabel( + ghowner, + ghrepo, + "investigating", + "Incident is investigated", + "D4E2D4" + ); + } + if (ghlabels.indexOf("incident-degraded") === -1) { + await CreateGHLabel( + ghowner, + ghrepo, + "incident-degraded", + "Status is degraded of the site", + "f5ba60" + ); + } + if (ghlabels.indexOf("incident-down") === -1) { + await CreateGHLabel( + ghowner, + ghrepo, + "incident-down", + "Status is down of the site", + "ea3462" + ); + } + //add tags if does not exist + for (let i = 0; i < tagsAndDescription.length; i++) { + const tag = tagsAndDescription[i].tag; + const description = tagsAndDescription[i].description; + if (ghlabels.indexOf(tag) === -1) { + await CreateGHLabel(ghowner, ghrepo, tag, description); + } + } + } } Build(); diff --git a/docs/customize-site.md b/docs/customize-site.md index 4f88871c..0c525faa 100644 --- a/docs/customize-site.md +++ b/docs/customize-site.md @@ -91,7 +91,12 @@ This is the location where someone will be taken when they click on the site nam ## theme -This is the default theme of the site that will be used when a user lands for the first time. It can be `light` or `dark` or `system`. Defaults to `system`. The user still gets the option to change the theme. +This is the default theme of the site that will be used when a user lands for the first time. It can be `light` or `dark` or `system` or `none`. Defaults to `system`. The user still gets the option to change the theme. + +- setting it to `light` will always set the theme to light when page loads +- setting it to `dark` will always set the theme to dark when page loads +- setting it to `system` will set the theme based on the system preference +- setting it to `none` will set the theme to last selected theme by the user ## themeToggle diff --git a/docs/incident-management.md b/docs/incident-management.md index f3ccd679..b9c9dab4 100644 --- a/docs/incident-management.md +++ b/docs/incident-management.md @@ -17,7 +17,11 @@ Kener auto creates labels for your monitors using the `tag` parameter - `resolved`: Use this tag to mark the incident has RESOLVED - `identified`: Use this tag to show that the root cause of the incident has been identified -## Creating your first incident +## Creating Incident + +Kener uses Github issues to create incidents. Here is how you can create an incident + +### Using Github - Go to your github repo of kener - Go to issues @@ -28,3 +32,7 @@ Kener auto creates labels for your monitors using the `tag` parameter If you clone the repo it gives you an issue template to create incidents Here is a [sample incident](https://github.com/rajnandan1/kener/issues/15) for your reference. + +### Using API + +You can also create incidents using the API. See the API [here](/kener-apis#create-an-incident---api) diff --git a/docs/status-badges.md b/docs/status-badges.md index d97cef67..21cd8f17 100644 --- a/docs/status-badges.md +++ b/docs/status-badges.md @@ -35,6 +35,16 @@ Example in MarkDown ![Status Badge](https://kener.ing/badge/[monitor.tag]/status) ``` +### Custom Label + +You can set custom label for the status badge. If you do not set a label it will default to the monitor name. + +![Earth Status](https://kener.ing/badge/earth/status?label=Gotcha) + +```md +![Earth Status](https://kener.ing/badge/earth/status?label=Gotcha) +``` + --- ## Live @@ -87,6 +97,8 @@ Example in MarkDown ### 15 Minute Uptime +Set `sinceLast` as query param to get uptime since last x seconds. + Example in HTML ```html @@ -99,11 +111,31 @@ Example in MarkDown ![Uptime Badge](https://kener.ing/badge/[monitor.tag]/uptime?sinceLast=900) ``` +### Custom Label + +You can set custom label for the uptime badge. If you do not set a label it will default to the **monitor name**. + +![Earth Uptime](https://kener.ing/badge/earth/uptime?label=Gotcha) + +```md +![Earth Uptime](https://kener.ing/badge/earth/uptime?label=Gotcha) +``` + +### Hide Duration + +You can hide the duration by setting `hideDuration=true` + +![Earth Uptime](https://kener.ing/badge/earth/uptime?hideDuration=true) + +```md +![Earth Uptime](https://kener.ing/badge/earth/uptime?hideDuration=true) +``` + --- ## Customize Badges -You can set different colors for badges and style. +You can set different colors for badges and style. It only works for `status` and `uptime` badges. ### With Custom Label Color diff --git a/package-lock.json b/package-lock.json index 6e2086d4..b7166742 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,23 @@ { "name": "kener", - "version": "0.0.16", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kener", - "version": "0.0.16", + "version": "2.0.0", "license": "MIT", "dependencies": { "@scalar/express-api-reference": "^0.4.167", "analytics": "^0.8.14", "axios": "^1.6.2", "badge-maker": "^3.3.1", + "better-sqlite3": "^11.5.0", "bits-ui": "^0.9.9", "clsx": "^2.0.0", "croner": "^7.0.5", + "dns2": "^2.1.0", "dotenv": "^16.4.5", "express": "^4.18.2", "fs-extra": "^11.1.1", @@ -28,7 +30,6 @@ "node-cache": "^5.1.2", "npm-run-all": "^4.1.5", "ping": "^0.4.4", - "prismjs": "^1.29.0", "queue": "^7.0.0", "randomstring": "^1.3.0", "svelte-legos": "^0.2.5", @@ -1230,6 +1231,37 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.5.0.tgz", + "integrity": "sha512-e/6eggfOutzoK0JWiU36jsisdWoHOfN9iWiW/SieKvb7SAa6aGNmBM/UKyp+/wWSXpLlWNN8tCPwoDNPhzUvuQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1243,6 +1275,15 @@ "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bits-ui": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.9.9.tgz", @@ -1255,6 +1296,17 @@ "svelte": "^4.0.0" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -1347,6 +1399,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -1506,6 +1582,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1691,10 +1773,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1832,6 +1915,30 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -1919,6 +2026,15 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/devalue": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", @@ -1935,6 +2051,12 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/dns2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dns2/-/dns2-2.1.0.tgz", + "integrity": "sha512-m27K11aQalRbmUs7RLaz6aPyceLjAoqjPRNTdE7qUouQpl+PC8Bi67O+i9SuJUPbQC8dxFrczAxfmTPuTKHNkw==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -1974,6 +2096,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2195,6 +2326,15 @@ "node": ">= 0.6" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "4.21.1", "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", @@ -2282,6 +2422,12 @@ "reusify": "^1.0.4" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2406,6 +2552,12 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", @@ -2517,6 +2669,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -2713,6 +2871,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2753,6 +2931,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -3314,6 +3498,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -3339,7 +3535,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3356,6 +3551,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/mode-watcher": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/mode-watcher/-/mode-watcher-0.4.1.tgz", @@ -3435,6 +3636,12 @@ "node": "^18 || >=20" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "license": "MIT" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -3449,6 +3656,30 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "license": "MIT" }, + "node_modules/node-abi": { + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-cache": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", @@ -3572,9 +3803,9 @@ "license": "MIT" }, "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "license": "MIT", "dependencies": { "nice-try": "^1.0.4", @@ -4056,6 +4287,32 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prettier": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", @@ -4187,6 +4444,16 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -4269,6 +4536,21 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4291,6 +4573,20 @@ "node": ">=4" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4717,6 +5013,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sirv": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", @@ -4802,6 +5143,15 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4916,6 +5266,15 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -5261,6 +5620,34 @@ "node": ">=10" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -5339,6 +5726,18 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/package.json b/package.json index 51a71e6c..e8de86b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kener", - "version": "0.0.16", + "version": "2.0.0", "private": false, "license": "MIT", "description": "Kener: An open-source Node.js status page application for real-time service monitoring, incident management, and customizable reporting. Simplify service outage tracking, enhance incident communication, and ensure a seamless user experience.", @@ -62,9 +62,11 @@ "analytics": "^0.8.14", "axios": "^1.6.2", "badge-maker": "^3.3.1", + "better-sqlite3": "^11.5.0", "bits-ui": "^0.9.9", "clsx": "^2.0.0", "croner": "^7.0.5", + "dns2": "^2.1.0", "dotenv": "^16.4.5", "express": "^4.18.2", "fs-extra": "^11.1.1", @@ -77,7 +79,6 @@ "node-cache": "^5.1.2", "npm-run-all": "^4.1.5", "ping": "^0.4.4", - "prismjs": "^1.29.0", "queue": "^7.0.0", "randomstring": "^1.3.0", "svelte-legos": "^0.2.5", diff --git a/src/kener.css b/src/kener.css index fa7f27bb..a377ccf1 100644 --- a/src/kener.css +++ b/src/kener.css @@ -45,7 +45,7 @@ background-image: linear-gradient(#444cf7 1px, transparent 1px), linear-gradient(to right, #444cf7 1px, var(--background-kener) 1px); -webkit-mask-image: linear-gradient( - to bottom, + 180deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.8) 20%, rgba(0, 0, 0, 0.6) 40%, @@ -70,8 +70,26 @@ left: 0; } .dark .squares-pattern::after { - background-image: linear-gradient(#677489 1px, transparent 1px), - linear-gradient(to right, #677489 1px, var(--background-kener) 1px); + -webkit-mask-image: linear-gradient( + to bottom, + rgba(0, 0, 0, 0.5) 0%, + rgba(0, 0, 0, 0.25) 20%, + rgba(0, 0, 0, 0.125) 40%, + rgba(0, 0, 0, 0.0625) 60%, + rgba(0, 0, 0, 0.03125) 80%, + rgba(0, 0, 0, 0) 100% + ); + mask-image: linear-gradient( + 180deg, + rgba(0, 0, 0, 0.5) 0%, + rgba(0, 0, 0, 0.25) 20%, + rgba(0, 0, 0, 0.125) 40%, + rgba(0, 0, 0, 0.0625) 60%, + rgba(0, 0, 0, 0.03125) 80%, + rgba(0, 0, 0, 0) 100% + ); + background-image: linear-gradient(#616fbf 1px, transparent 1px), + linear-gradient(to right, #616fbf 1px, var(--background-kener) 1px); } /*Needed to overlay content on top of dotted bg*/ @@ -85,19 +103,40 @@ section { box-shadow: 0 0 64px 64px var(--background-kener-rgba); } +:root { + --up-color: #4ead94; + --down-color: #ca3038; + --degraded-color: #e6ca61; +} + /*Colors for something UP*/ .bg-api-up { - background-color: #00dfa2; + background-color: var(--up-color); } .text-api-up { - color: #0aca97; + color: var(--up-color); } /*Colors for something DOWN*/ .bg-api-down { - background-color: #ff0060; + background-color: var(--down-color); +} +.bg-api-down-10 { + background: linear-gradient(0deg, var(--down-color) 10%, var(--up-color) 1%); +} +.bg-api-down-20 { + background: linear-gradient(0deg, var(--down-color) 20%, var(--up-color) 1%); +} +.bg-api-down-40 { + background: linear-gradient(0deg, var(--down-color) 40%, var(--up-color) 1%); +} +.bg-api-down-60 { + background: linear-gradient(0deg, var(--down-color) 60%, var(--up-color) 1%); +} +.bg-api-down-80 { + background: linear-gradient(0deg, var(--down-color) 80%, var(--up-color) 1%); } .text-api-down { - color: #ff0060; + color: var(--down-color); } /*Colors for something Not there*/ .bg-api-nodata { @@ -112,10 +151,26 @@ section { /*Colors for something degraded*/ .bg-api-degraded { - background-color: #ffb84c; + background-color: var(--degraded-color); } .text-api-degraded { - color: #ffb84c; + color: var(--degraded-color); +} + +.bg-api-degraded-10 { + background: linear-gradient(0deg, var(--degraded-color) 10%, var(--up-color) 1%); +} +.bg-api-degraded-20 { + background: linear-gradient(0deg, var(--degraded-color) 20%, var(--up-color) 1%); +} +.bg-api-degraded-40 { + background: linear-gradient(0deg, var(--degraded-color) 20%, var(--up-color) 1%); +} +.bg-api-degraded-60 { + background: linear-gradient(0deg, var(--degraded-color) 20%, var(--up-color) 1%); +} +.bg-api-degraded-80 { + background: linear-gradient(0deg, var(--degraded-color) 20%, var(--up-color) 1%); } /*Needed to show markdown properly*/ @@ -153,3 +208,119 @@ section { background-color: #feffac; color: #09090b; } +.daygrid90 { + -ms-overflow-style: none; /* Internet Explorer 10+ */ + scrollbar-width: none; /* Firefox */ +} +.daygrid90::-webkit-scrollbar { + display: none; /* Safari and Chrome */ +} +.oneline { + transition: transform 0.1s ease-in; + cursor: pointer; +} +.oneline:hover { + transform: scaleY(1.2); +} + +.show-hover { + top: 40px; + padding: 0px; + text-align: left; +} + +.today-sq + .hiddenx .message { + position: absolute; + white-space: nowrap; +} + +.today-sq + .hiddenx { + visibility: hidden; + z-index: 30; +} +.today-sq:hover + .hiddenx { + visibility: visible; +} +.today-sq:hover { + /* transform: scale(1.1); */ + box-shadow: + rgba(50, 50, 105, 0.15) 0px 2px 5px 0px, + rgba(0, 0, 0, 0.05) 0px 1px 1px 0px; + opacity: 0.75; + transition: all 0.1s ease-in; + cursor: pointer; +} + +.today-sq { + position: relative; + z-index: 0; +} + +.wiggle:hover { + -webkit-animation-name: wiggle; + -ms-animation-name: wiggle; + animation-name: wiggle; + animation-duration: 1000ms; + -ms-animation-duration: 1000ms; + -webkit-animation-duration: 1000ms; + animation-iteration-count: 1; + -webkit-animation-iteration-count: 1; + -ms-animation-iteration-count: 1; + animation-timing-function: ease-in-out; + -webkit-animation-timing-function: ease-in-out; + -ms-animation-timing-function: ease-in-out; +} + +@-webkit-keyframes wiggle { + 0% { + -webkit-transform: rotate(10deg); + } + 25% { + -webkit-transform: rotate(-10deg); + } + 50% { + -webkit-transform: rotate(20deg); + } + 75% { + -webkit-transform: rotate(-5deg); + } + 100% { + -webkit-transform: rotate(0deg); + } +} + +@-ms-keyframes wiggle { + 0% { + -ms-transform: rotate(1deg); + } + 25% { + -ms-transform: rotate(-1deg); + } + 50% { + -ms-transform: rotate(1.5deg); + } + 75% { + -ms-transform: rotate(-5deg); + } + 100% { + -ms-transform: rotate(0deg); + } +} + +@keyframes wiggle { + 0% { + transform: rotate(10deg); + } + 25% { + transform: rotate(-10deg); + } + 50% { + transform: rotate(20deg); + } + 75% { + transform: rotate(-5deg); + } + 100% { + transform: rotate(0deg); + } +} diff --git a/src/lib/components/incident.svelte b/src/lib/components/incident.svelte index 67d92f04..e6e13721 100644 --- a/src/lib/components/incident.svelte +++ b/src/lib/components/incident.svelte @@ -1,7 +1,6 @@
-
+