diff --git a/entities/src/host_overrides.rs b/entities/src/host_overrides.rs index 60b36ee..9413c53 100644 --- a/entities/src/host_overrides.rs +++ b/entities/src/host_overrides.rs @@ -3,16 +3,11 @@ use sea_orm::entity::prelude::*; use serde::Serialize; -use crate::host; - /// Module of override keys pub mod keys { use std::{collections::HashMap, sync::LazyLock}; - use sea_orm::{ - ActiveModelTrait, ActiveValue, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, - QueryFilter, - }; + use sea_orm::{ActiveValue, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter}; use sea_query::OnConflict; use serde::Serialize; use tracing::warn; @@ -37,8 +32,12 @@ pub mod keys { pub static NOT_LOCKED_DEFAULTS: &'static [&'static str] = &[KEY_HOST_HEALTH_PATH, KEY_HOST_BEARER, KEY_HOST_HEALTH_QUERY]; - pub static ALL_OVERRIDES: &'static [&'static str] = - &[KEY_BAD_HOST, KEY_HOST_BEARER, KEY_HOST_HEALTH_PATH, KEY_HOST_HEALTH_QUERY]; + pub static ALL_OVERRIDES: &'static [&'static str] = &[ + KEY_BAD_HOST, + KEY_HOST_BEARER, + KEY_HOST_HEALTH_PATH, + KEY_HOST_HEALTH_QUERY, + ]; #[derive(Serialize, Clone, Copy)] pub enum ValueType { @@ -131,7 +130,7 @@ pub mod keys { pub fn health_path(&self) -> Option<&str> { self.get(KEY_HOST_HEALTH_PATH) } - + /// Retrieve [KEY_HOST_HEALTH_QUERY] pub fn health_query(&self) -> Option<&str> { self.get(KEY_HOST_HEALTH_QUERY) @@ -139,9 +138,7 @@ pub mod keys { /// Retrieve override value for key pub fn get(&self, key: &str) -> Option<&str> { - self.entries - .get(key) - .and_then(|v| v.value.as_deref()) + self.entries.get(key).and_then(|v| v.value.as_deref()) } pub fn entries(&self) -> &HashMap { diff --git a/entities/src/instance_stats.rs b/entities/src/instance_stats.rs index 43ded7b..13aa3c2 100644 --- a/entities/src/instance_stats.rs +++ b/entities/src/instance_stats.rs @@ -135,7 +135,7 @@ pub struct StatsCSVEntry { impl StatsCSVEntry { /// Fetch health check graph data for all or selected hosts in the selected time range. - pub async fn fetch(db: &DatabaseConnection,hosts: Option<&[i32]>,) -> Result, DbErr> { + pub async fn fetch(db: &DatabaseConnection, hosts: Option<&[i32]>) -> Result, DbErr> { let builder = db.get_database_backend(); let columns = ["limited_accs", "total_accs", "total_requests"]; let mut stmt: sea_query::SelectStatement = Query::select(); diff --git a/entities/src/mod.rs b/entities/src/mod.rs index 7bdd915..718e067 100644 --- a/entities/src/mod.rs +++ b/entities/src/mod.rs @@ -10,4 +10,4 @@ pub mod instance_stats; pub mod log; // has to be re-added on entity regeneration -pub mod state; \ No newline at end of file +pub mod state; diff --git a/migration/src/m20250216_185501_overrides.rs b/migration/src/m20250216_185501_overrides.rs index 5eeb2b9..4ac56a2 100644 --- a/migration/src/m20250216_185501_overrides.rs +++ b/migration/src/m20250216_185501_overrides.rs @@ -14,7 +14,8 @@ impl MigrationTrait for Migration { CONSTRAINT "pk_override_hostkey" PRIMARY KEY ("host", "key"), FOREIGN KEY ("host") REFERENCES "host" ("id") ON DELETE CASCADE ON UPDATE CASCADE ) STRICT;"#; - let cmd_overrides_index = r#"CREATE INDEX "index_overrides_key" ON host_overrides ("key");"#; + let cmd_overrides_index = + r#"CREATE INDEX "index_overrides_key" ON host_overrides ("key");"#; let cmd_log = r#"CREATE TABLE "log" ( "user_host" integer NOT NULL, "host_affected" integer, diff --git a/scanner/src/cache_update.rs b/scanner/src/cache_update.rs index b9d24f5..0f5cd8d 100644 --- a/scanner/src/cache_update.rs +++ b/scanner/src/cache_update.rs @@ -1,19 +1,19 @@ // SPDX-License-Identifier: AGPL-3.0-only use std::cmp; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use chrono::{Days, Utc}; use chrono::{Duration, TimeZone}; use entities::host_overrides::keys::{KEY_BAD_HOST, VAL_BOOL_TRUE}; -use entities::{host, host_overrides}; use entities::prelude::*; use entities::state::CacheData; use entities::state::CacheHost; -use sea_orm::{ColumnTrait, QuerySelect, QueryTrait}; +use entities::{host, host_overrides}; use sea_orm::EntityTrait; use sea_orm::QueryFilter; use sea_orm::QueryOrder; use sea_orm::{prelude::DateTimeUtc, DbBackend, FromQueryResult, Statement}; +use sea_orm::{ColumnTrait, QuerySelect}; use crate::version_check::fetch_git_state; use crate::LatestCheck; @@ -193,8 +193,11 @@ impl Scanner { async fn query_bad_hosts(&self) -> Result> { let res: Vec = HostOverrides::find() - .filter(host_overrides::Column::Key.eq(KEY_BAD_HOST) - .and(host_overrides::Column::Value.eq(VAL_BOOL_TRUE))) + .filter( + host_overrides::Column::Key + .eq(KEY_BAD_HOST) + .and(host_overrides::Column::Value.eq(VAL_BOOL_TRUE)), + ) .select_only() .column(host_overrides::Column::Host) .into_tuple() diff --git a/scanner/src/cleanup.rs b/scanner/src/cleanup.rs index 6799581..13efc63 100644 --- a/scanner/src/cleanup.rs +++ b/scanner/src/cleanup.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-only + use entities::check_errors; use entities::host; use sea_orm::ColumnTrait; diff --git a/scanner/src/instance_check.rs b/scanner/src/instance_check.rs index d9b3549..5ea0c18 100644 --- a/scanner/src/instance_check.rs +++ b/scanner/src/instance_check.rs @@ -3,10 +3,10 @@ use std::time::Instant; use chrono::Utc; -use entities::state::error_cache::HostError; -use entities::{check_errors, health_check, host_overrides}; -use entities::{host, prelude::*}; use entities::host_overrides::keys::HostOverrides; +use entities::state::error_cache::HostError; +use entities::{check_errors, health_check}; +use entities::{host, prelude::*}; use reqwest::Url; use sea_orm::prelude::DateTimeUtc; use sea_orm::ColumnTrait; @@ -40,8 +40,9 @@ impl Scanner { .find(|v| v.host == model.id) .map_or(false, |check| !check.healthy); join_set.spawn(async move { - if let Err(e) = scanner.health_check_host(model, muted_host).await { - + if let Err(error) = scanner.health_check_host(model, muted_host).await { + // should only happen if for example the DB fails + tracing::error!(%error,"Failed to perform host health check"); } }); } @@ -191,7 +192,12 @@ impl Scanner { } /// Check nitter version - pub(crate) async fn nitter_version(&self, url: &mut Url, api_token: Option<&str>, mute: bool) -> Option { + pub(crate) async fn nitter_version( + &self, + url: &mut Url, + api_token: Option<&str>, + mute: bool, + ) -> Option { url.set_path(&self.inner.config.about_path); match self.fetch_url(url.as_str(), api_token).await { Ok((code, content)) => match self.inner.about_parser.parse_about_version(&content) { diff --git a/scanner/src/lib.rs b/scanner/src/lib.rs index 83d926e..4472ddd 100644 --- a/scanner/src/lib.rs +++ b/scanner/src/lib.rs @@ -6,21 +6,16 @@ use std::{ use about_parser::AboutParser; use chrono::{DateTime, Duration, TimeZone, Utc}; -use entities::{ - health_check, host, state::{error_cache::HostError, scanner::ScannerConfig, AppState} -}; +use entities::state::{error_cache::HostError, scanner::ScannerConfig, AppState}; use instance_parser::InstanceParser; use miette::{Context, IntoDiagnostic}; use profile_parser::ProfileParser; use regex::{Regex, RegexBuilder}; use reqwest::{ header::{HeaderMap, HeaderValue}, - Client, ClientBuilder, Url, -}; -use sea_orm::{ - ColumnTrait, ConnectionTrait, DatabaseConnection, DbBackend, EntityTrait, FromQueryResult, - QuerySelect, Statement, + Client, ClientBuilder, }; +use sea_orm::{ConnectionTrait, DatabaseConnection, DbBackend, FromQueryResult, Statement}; use thiserror::Error; use tokio::time::sleep; @@ -33,8 +28,8 @@ mod instance_check; mod instance_parser; mod list_update; mod profile_parser; -mod version_check; mod update_stats; +mod version_check; const CAPTCHA_TEXT: &'static str = "Enable JavaScript and cookies to continue"; const CAPTCHA_CODE: u16 = 403; @@ -73,7 +68,7 @@ pub enum ScannerError { InstanceUrlParse, /// Failed to join a tokio thread in a JoinSet #[error("Failed to join worker task {0}")] - JoinError(#[from] tokio::task::JoinError) + JoinError(#[from] tokio::task::JoinError), } #[derive(Error, Debug)] @@ -208,16 +203,16 @@ impl Scanner { .build() .into_diagnostic()?; - let last_uptime_check = Self::query_last_entry_for_table(&db,"health_check") + let last_uptime_check = Self::query_last_entry_for_table(&db, "health_check") .await .into_diagnostic() .wrap_err("Fetching last uptime check failed!")?; - let last_stats_check = Self::query_last_entry_for_table(&db,"instance_stats") + let last_stats_check = Self::query_last_entry_for_table(&db, "instance_stats") .await .into_diagnostic() .wrap_err("Fetching last stats check failed!")?; - tracing::info!(?last_uptime_check, ?last_stats_check);// TODO: wrong timestamp + tracing::info!(?last_uptime_check, ?last_stats_check); // TODO: wrong timestamp let scanner = Self { inner: Arc::new(InnerScanner { db, @@ -247,7 +242,10 @@ impl Scanner { } /// Retrieves the last stats fetching that happened - pub async fn query_last_entry_for_table(db: &DatabaseConnection, table: &str) -> Result> { + pub async fn query_last_entry_for_table( + db: &DatabaseConnection, + table: &str, + ) -> Result> { #[derive(Debug, FromQueryResult, Default)] pub(crate) struct TimeMax { pub max: Option, @@ -315,12 +313,20 @@ impl Scanner { } async fn sleep_till_deadline(&self) { - let delay_instance_check = self.last_uptime_check() + self.inner.config.instance_check_interval; + let delay_instance_check = + self.last_uptime_check() + self.inner.config.instance_check_interval; let delay_list_update = self.last_list_fetch() + self.inner.config.list_fetch_interval; - let delay_stats_update = self.last_stats_fetch() + self.inner.config.instance_stats_interval; - tracing::debug!(?delay_list_update, ?delay_instance_check, ?delay_stats_update); + let delay_stats_update = + self.last_stats_fetch() + self.inner.config.instance_stats_interval; + tracing::debug!( + ?delay_list_update, + ?delay_instance_check, + ?delay_stats_update + ); - let next_deadline = delay_instance_check.min(delay_list_update).min(delay_stats_update); + let next_deadline = delay_instance_check + .min(delay_list_update) + .min(delay_stats_update); let now = Utc::now(); let sleep_time = next_deadline.signed_duration_since(now); if sleep_time <= Duration::zero() { @@ -363,11 +369,17 @@ impl Scanner { } async fn fetch_instance_list(&self) -> Result { - let (_, body) = self.fetch_url(&self.inner.config.instance_list_url, None).await?; + let (_, body) = self + .fetch_url(&self.inner.config.instance_list_url, None) + .await?; Ok(body) } - async fn fetch_url(&self, url: &str, api_token: Option<&str>) -> std::result::Result<(u16, String), FetchError> { + async fn fetch_url( + &self, + url: &str, + api_token: Option<&str>, + ) -> std::result::Result<(u16, String), FetchError> { let mut request = self.inner.client.get(url); if let Some(token) = api_token { request = request.bearer_auth(token); @@ -389,24 +401,34 @@ impl Scanner { } if code == 403 && body_text.contains("You have been blocked") { // cloudflare block - return Err(FetchError::KnownHttpResponseStatus(code, message, body_text)); + return Err(FetchError::KnownHttpResponseStatus( + code, message, body_text, + )); } if code == 429 && body_text.contains("Instance has been rate limited") { // out of non-limited accounts - return Err(FetchError::KnownHttpResponseStatus(code, message, body_text)); + return Err(FetchError::KnownHttpResponseStatus( + code, message, body_text, + )); } if code == 404 { // don't spam the body on 404s - return Err(FetchError::KnownHttpResponseStatus(code, message, body_text)); + return Err(FetchError::KnownHttpResponseStatus( + code, message, body_text, + )); } if code >= 502 && code <= 504 { // don't spam the body on Bad Gateway/Service Unavailable/Gateway Timeout - return Err(FetchError::KnownHttpResponseStatus(code, message, body_text)); + return Err(FetchError::KnownHttpResponseStatus( + code, message, body_text, + )); } if code >= 520 && code <= 527 { // don't spam the body on Cloudflare errors // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes - return Err(FetchError::KnownHttpResponseStatus(code, message, body_text)); + return Err(FetchError::KnownHttpResponseStatus( + code, message, body_text, + )); } return Err(FetchError::HttpResponseStatus(code, message, body_text)); } @@ -481,7 +503,7 @@ mod test { let scanner = Scanner::new(db, Config::test_defaults(), entities::state::new()) .await .unwrap(); - let (_, res) = scanner.fetch_url("example.com/jack",None).await.unwrap(); + let (_, res) = scanner.fetch_url("example.com/jack", None).await.unwrap(); let mut file = File::create("test_data/blocked.html").await.unwrap(); file.write_all(&res.as_bytes()).await.unwrap(); } diff --git a/scanner/src/list_update.rs b/scanner/src/list_update.rs index f0cf708..891fee6 100644 --- a/scanner/src/list_update.rs +++ b/scanner/src/list_update.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-only //! Updates the list of available instances, fetching all required fields -use std::collections::HashMap; use std::time::Duration; use std::time::Instant; diff --git a/scanner/src/update_stats.rs b/scanner/src/update_stats.rs index aed03f0..7608bc3 100644 --- a/scanner/src/update_stats.rs +++ b/scanner/src/update_stats.rs @@ -27,8 +27,11 @@ struct InstanceStats { struct InstanceStatsAccs { total: i32, limited: i32, + #[allow(unused)] oldest: DateTimeUtc, + #[allow(unused)] newest: DateTimeUtc, + #[allow(unused)] average: DateTimeUtc, } @@ -102,7 +105,7 @@ impl Scanner { host: &host::Model, ) -> Result { let overrides = HostOverrides::load(&host, &self.inner.db).await?; - let mut url = Url::parse(&host.url).map_err(|e| ScannerError::InstanceUrlParse)?; + let mut url = Url::parse(&host.url).map_err(|_e| ScannerError::InstanceUrlParse)?; url.set_path(".health"); if let Some(url_override) = overrides.health_path() { url.set_path(url_override); diff --git a/server/src/admin/logs.rs b/server/src/admin/logs.rs index 0ddd8d5..0f27548 100644 --- a/server/src/admin/logs.rs +++ b/server/src/admin/logs.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashMap, - sync::Arc, -}; +use std::{collections::HashMap, sync::Arc}; use crate::{Result, ServerError}; use axum::{ @@ -9,10 +6,7 @@ use axum::{ response::{Html, IntoResponse}, }; use chrono::{TimeZone, Utc}; -use entities::{ - host, - log, -}; +use entities::{host, log}; use sea_orm::EntityTrait; use sea_orm::{DatabaseConnection, QueryOrder, QuerySelect}; use serde::Serialize; diff --git a/server/src/admin/mod.rs b/server/src/admin/mod.rs index 94b1d31..ae77684 100644 --- a/server/src/admin/mod.rs +++ b/server/src/admin/mod.rs @@ -1,7 +1,6 @@ use std::collections::HashSet; use std::sync::Arc; -use axum::extract::Path; use axum::extract::State; use axum::response::Html; use axum::response::IntoResponse; @@ -24,7 +23,6 @@ use sea_orm::ColumnTrait; use sea_orm::DatabaseConnection; use sea_orm::EntityTrait; use sea_orm::QueryFilter; -use sea_orm::QueryOrder; use serde::Deserialize; use serde::Serialize; use sha2::{Digest, Sha256}; @@ -42,21 +40,21 @@ use crate::ADMIN_OVERVIEW_URL; use crate::LOGIN_URL; mod errors; -mod settings; mod locks; mod logs; +mod settings; mod stats; pub use errors::errors_view; -pub use settings::post_settings; -pub use settings::settings_view; pub use locks::locks_view; pub use locks::post_locks; pub use logs::log_view; -pub use stats::stats_view; +pub use settings::post_settings; +pub use settings::settings_view; pub use stats::health_csv_api; -pub use stats::stats_csv_api; pub use stats::overview_csv_api; +pub use stats::stats_csv_api; +pub use stats::stats_view; /// Stored session login information #[derive(Serialize, Deserialize, Default)] @@ -368,34 +366,6 @@ pub async fn history_json( .into_response()) } -pub async fn history_view( - State(ref app_state): State, - State(ref template): State>, - State(ref db): State, - Path(host): Path, - session: Session, -) -> Result { - tracing::info!(?session); - - let (host, _login) = get_specific_login_host(host, &session, db).await?; - - let mut context = tera::Context::new(); - let res = { - let guard = app_state - .cache - .read() - .map_err(|_| ServerError::MutexFailure)?; - let time = guard.last_update.format("%Y.%m.%d %H:%M").to_string(); - context.insert("last_updated", &time); - context.insert("HOST_DOMAIN", &host.domain); - - let res = Html(template.render("errors_admin.html.j2", &context)?).into_response(); - drop(guard); - res - }; - Ok(res) -} - /// Get all [host::Model] for current [Session], optionally return all hosts for admins async fn get_all_login_hosts( session: &Session, diff --git a/server/src/admin/settings.rs b/server/src/admin/settings.rs index 6a01ce5..878b6fb 100644 --- a/server/src/admin/settings.rs +++ b/server/src/admin/settings.rs @@ -1,7 +1,4 @@ -use std::{ - sync::Arc, - time::{SystemTime, UNIX_EPOCH}, -}; +use std::sync::Arc; use crate::{Result, ServerError}; use axum::{ diff --git a/server/src/admin/stats.rs b/server/src/admin/stats.rs index fb7abec..18e8fce 100644 --- a/server/src/admin/stats.rs +++ b/server/src/admin/stats.rs @@ -1,27 +1,16 @@ -use std::{collections::HashMap, fmt::Write, sync::Arc}; +use std::{fmt::Write as _, sync::Arc}; use axum::{ extract::{Path, State}, http::HeaderValue, - response::{Html, IntoResponse, Redirect}, - Form, + response::{Html, IntoResponse}, }; use chrono::{TimeZone, Utc}; -use entities::{ - health_check::{self, ResponseTimeStat}, - host_overrides::{ - self, - keys::{HostOverrides, LOCKED_FALSE, LOCKED_TRUE}, - }, - instance_stats, -}; -use sea_orm::{sea_query::OnConflict, ActiveValue, DatabaseConnection, EntityTrait}; +use entities::{health_check::ResponseTimeStat, instance_stats}; +use sea_orm::DatabaseConnection; use tower_sessions::Session; -use crate::{ - admin::{get_all_login_hosts, get_session_login}, - Result, ServerError, -}; +use crate::{admin::get_all_login_hosts, Result, ServerError}; use super::get_specific_login_host; diff --git a/server/src/api.rs b/server/src/api.rs index a0fcb48..fb500fe 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -2,13 +2,8 @@ use crate::{Result, ServerError}; use axum::response::IntoResponse; use axum::{extract::State, Json}; -use chrono::{TimeZone, Utc}; use entities::state::AppState; -use entities::{health_check, instance_stats}; use hyper::http::HeaderValue; -use sea_orm::DatabaseConnection; -use serde::Serialize; -use std::fmt::Write; use std::sync::Arc; pub async fn instances( diff --git a/server/src/website.rs b/server/src/website.rs index 8bafd86..05f556c 100644 --- a/server/src/website.rs +++ b/server/src/website.rs @@ -78,26 +78,3 @@ pub async fn about( ); Ok(res) } - -pub async fn rip( - State(ref app_state): State, - State(ref template): State>, - State(ref scanner_config): State, -) -> Result { - let mut context = tera::Context::new(); - { - let guard = app_state - .cache - .read() - .map_err(|_| ServerError::MutexFailure)?; - let time = guard.last_update.format("%Y.%m.%d %H:%M").to_string(); - context.insert("last_updated", &time); - } - - let mut res = Html(template.render("rip.html.j2", &context)?).into_response(); - res.headers_mut().insert( - "cache-control", - HeaderValue::from_static("public, max-age=900"), - ); - Ok(res) -} diff --git a/server/static/admin.js b/server/static/admin.js index ca4491b..506bb8c 100644 --- a/server/static/admin.js +++ b/server/static/admin.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-only document.addEventListener("DOMContentLoaded", function() { const startDateInput = document.getElementById('startDate'); const endDateInput = document.getElementById('endDate'); diff --git a/server/static/admin_index.js b/server/static/admin_index.js index 86035d4..cf8d85c 100644 --- a/server/static/admin_index.js +++ b/server/static/admin_index.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-only document.addEventListener("DOMContentLoaded", function (event) { const startDateInput = document.getElementById('startDate'); const endDateInput = document.getElementById('endDate'); diff --git a/server/static/history.2.js b/server/static/history.2.js index d6ad1cb..8c9eb1b 100644 --- a/server/static/history.2.js +++ b/server/static/history.2.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-only document.addEventListener("DOMContentLoaded", function (event) { var loadGraphBtn = document.getElementById("loadGraphBtn"); if (loadGraphBtn) { diff --git a/server/static/instance.js b/server/static/instance.js index c2d2017..bd3d98d 100644 --- a/server/static/instance.js +++ b/server/static/instance.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-only let chart = undefined; document.addEventListener("DOMContentLoaded", function() { const startDateInput = document.getElementById('startDate'); diff --git a/server/static/login_key.js b/server/static/login_key.js index 7e7aecd..88d1acc 100644 --- a/server/static/login_key.js +++ b/server/static/login_key.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-only async function sha256(message) { const msgBuffer = new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); diff --git a/server/static/stats.js b/server/static/stats.js index a33ae46..ab6c313 100644 --- a/server/static/stats.js +++ b/server/static/stats.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-only document.addEventListener("DOMContentLoaded", function (event) { var host_id = document.getElementById("body").getAttribute("data-hid"); var loadGraphBtn = document.getElementById("loadGraphBtn"); @@ -19,7 +20,7 @@ async function loadHealthGraph(host_id) { try { let g = new Dygraph( graphDiv, - "/admin/api/graphs/health/"+host_id, + "/admin/api/graphs/health/" + host_id, { title: 'Historic Instance Healthiness', showRangeSelector: true, @@ -34,6 +35,7 @@ async function loadHealthGraph(host_id) { ylabel: 'Nitter Instances', } ); + // fixme: find valid annotation dates, regardless of the host g.ready(function () { g.setAnnotations([ { @@ -66,7 +68,7 @@ async function loadStatsGraph(host_id) { try { let g = new Dygraph( graphDiv, - "/admin/api/graphs/stats/"+host_id, + "/admin/api/graphs/stats/" + host_id, { title: 'Historic Average Instance Statistics', showRangeSelector: true, diff --git a/server/static/table_toggle.js b/server/static/table_toggle.js index 81566eb..da4beee 100644 --- a/server/static/table_toggle.js +++ b/server/static/table_toggle.js @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-only + // Get the table and checkboxes const dataTable = document.getElementById('status-tbl'); const toggleColumnCheckboxes = document.querySelectorAll('.toggle-column'); diff --git a/server/templates/dygraph_fix.html.j2 b/server/templates/dygraph_fix.html.j2 index 1aac667..eaaedf0 100644 --- a/server/templates/dygraph_fix.html.j2 +++ b/server/templates/dygraph_fix.html.j2 @@ -1,3 +1,4 @@ +{#- SPDX-License-Identifier: AGPL-3.0-only #}