diff --git a/server/src/admin/mod.rs b/server/src/admin/mod.rs index 9daef41..94b1d31 100644 --- a/server/src/admin/mod.rs +++ b/server/src/admin/mod.rs @@ -56,6 +56,7 @@ pub use logs::log_view; pub use stats::stats_view; pub use stats::health_csv_api; pub use stats::stats_csv_api; +pub use stats::overview_csv_api; /// Stored session login information #[derive(Serialize, Deserialize, Default)] @@ -332,7 +333,7 @@ pub async fn overview( context.insert("instances", &hosts); context.insert("is_admin", &login.admin); - let res = Html(template.render("admin.html.j2", &context)?).into_response(); + let res = Html(template.render("admin_index.html.j2", &context)?).into_response(); drop(guard); res }; @@ -367,41 +368,6 @@ pub async fn history_json( .into_response()) } -pub async fn history_json_specific( - State(ref db): State, - session: Session, - Path(host): Path, - Json(input): Json, -) -> Result { - let (host, _login) = get_specific_login_host(host, &session, db).await?; - #[derive(Debug, Serialize)] - struct ReturnData { - pub stats: Vec, - pub health: Vec, - } - - let history_health: Vec = health_check::Entity::find() - .filter(health_check::Column::Host.eq(host.id)) - .order_by_asc(health_check::Column::Time) - .filter(health_check::Column::Time.between(input.start.timestamp(), input.end.timestamp())) - .all(db) - .await?; - let history_stats: Vec = instance_stats::Entity::find() - .filter(instance_stats::Column::Host.eq(host.id)) - .order_by_asc(instance_stats::Column::Time) - .filter( - instance_stats::Column::Time.between(input.start.timestamp(), input.end.timestamp()), - ) - .all(db) - .await?; - - Ok(Json(ReturnData { - health: history_health, - stats: history_stats, - }) - .into_response()) -} - pub async fn history_view( State(ref app_state): State, State(ref template): State>, diff --git a/server/static/admin_index.js b/server/static/admin_index.js new file mode 100644 index 0000000..86035d4 --- /dev/null +++ b/server/static/admin_index.js @@ -0,0 +1,73 @@ +document.addEventListener("DOMContentLoaded", function (event) { + const startDateInput = document.getElementById('startDate'); + const endDateInput = document.getElementById('endDate'); + const submitTime = document.getElementById('submitDateRange'); + + // Set initial values in UTC + const initialEndDate = moment.utc(); + const initialStartDate = moment().subtract(30, 'days').utc(); + startDateInput.value = initialStartDate.format('YYYY-MM-DD'); + endDateInput.value = initialEndDate.format('YYYY-MM-DD');; + + submitTime.addEventListener('click', function() { + const startDate = moment(startDateInput.value, 'YYYY-MM-DD'); + const endDate = moment(endDateInput.value, 'YYYY-MM-DD'); + + if (!startDate.isValid() || !endDate.isValid()) { + alert('Invalid date format. Please use the YYYY-MM-DD format.'); + } else if (startDate.isAfter(endDate)) { + alert('Invalid date range. Start date must be before the end date.'); + } else { + fetchDataAndCreateChart(startDate,endDate); + } + }); + fetchDataAndCreateChart(initialStartDate,initialEndDate); +}); + +async function fetchDataAndCreateChart(startDate,endDate) { + let graphDiv = document.getElementById('graph-health'); + try { + let g = new Dygraph( + graphDiv, + "/admin/api/graphs/overview", + { + title: 'Health Overview Instances', + showRangeSelector: true, + width: '100%', + rangeSelectorPlotFillColor: 'MediumSlateBlue', + rangeSelectorPlotFillGradientColor: 'rgba(123, 104, 238, 0)', + colorValue: 0.9, + fillAlpha: 0.4, + drawPoints: true, + strokeWidth: 0.0, + colors: ['#008000', '#ffa500'], + ylabel: 'Nitter Instances', + } + ); + g.ready(function () { + g.setAnnotations([ + { + series: "Healthy", + x: "2023-08-15T07:10:17Z", + shortText: "G", + text: "First API Change" + }, + { + series: "Dead", + x: "2023-10-21T14:37:44Z", + shortText: "C", + text: "Wiki Cleanup" + }, + { + series: "Healthy", + x: "2024-01-25T15:24:16Z", + shortText: "R", + text: "API Shutdown" + } + ]); + }); + } catch (error) { + console.error('Failed to fetch data:', error); + graphDiv.textContent = "Failed to load data."; + } +} \ No newline at end of file diff --git a/server/static/instance.js b/server/static/instance.js index 9baa065..c2d2017 100644 --- a/server/static/instance.js +++ b/server/static/instance.js @@ -25,24 +25,6 @@ document.addEventListener("DOMContentLoaded", function() { fetchDataAndCreateChart(initialStartDate,initialEndDate); }); -function mapKey(key) { - const keyMappings = { - "limited_accs": "Limited Accs", - "total_accs": "Total Accs", - "total_requests": "Total Requests", - "req_photo_rail": "PhotoRail Requests", - "req_user_screen_name": "UserScreeName Req.", - "req_search": "Search Requests", - "req_list_tweets": "ListTeweets Req.", - "req_user_media": "UserMedia Req.", - "req_tweet_detail": "TweetDetail Req.", - "req_list": "List Requests", - "req_user_tweets": "UserTweets Req.", - "req_user_tweets_and_replies": "UserTweetsAndReplies Req.", - } - return keyMappings[key]; -} - async function fetchDataAndCreateChart(startDate,endDate) { let jsonData; try { diff --git a/server/templates/admin.html.j2 b/server/templates/admin.html.j2 deleted file mode 100644 index 9319822..0000000 --- a/server/templates/admin.html.j2 +++ /dev/null @@ -1,59 +0,0 @@ -{# SPDX-License-Identifier: AGPL-3.0-only #} - - - - - - - - - Admin Interface - - -
-

Admin Interface

-

Logout Add more instances

- {% if is_admin %}

Settings Logs

{% endif %} - -

Instances

- {% if is_admin %}

root mode

{% endif %} -
-
-
-
-
- - -
-
- - -
-
- -
-
-
- - - - - - - - {% for instance in instances -%} - - - - - - {%- endfor %} - -
Instance
{{instance.domain}}StatsErrors Settings
-
- - - - - - diff --git a/server/templates/admin_index.html.j2 b/server/templates/admin_index.html.j2 new file mode 100644 index 0000000..39aa99a --- /dev/null +++ b/server/templates/admin_index.html.j2 @@ -0,0 +1,69 @@ +{# SPDX-License-Identifier: AGPL-3.0-only #} + + + + + + + + {% include "dygraph_fix.html.j2" %} + + + + Admin Interface + + + +
+

Admin Interface

+

Logout Add more instances

+ {% if is_admin %}

Settings Logs

{% endif %} + +

Instances

+ {% if is_admin %}

root mode

{% endif %} +
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+ + + + + + + + {% for instance in instances -%} + + + + + + {%- endfor %} + +
Instance
{{instance.domain}}StatsErrors Settings
+
+
+ + + + + + \ No newline at end of file