mirror of
https://codeberg.org/demostf/frontend.git
synced 2026-06-03 18:24:12 +02:00
api page
This commit is contained in:
parent
6ba687d9db
commit
15f38a6872
8 changed files with 443 additions and 2 deletions
|
|
@ -17,7 +17,6 @@ ready(async () => {
|
||||||
const demoListBody = document.querySelector('.demolist tbody');
|
const demoListBody = document.querySelector('.demolist tbody');
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const steamIds = (searchParams.get("players") || "").split(",").filter(id => id);
|
const steamIds = (searchParams.get("players") || "").split(",").filter(id => id);
|
||||||
console.log(steamIds);
|
|
||||||
let players = [];
|
let players = [];
|
||||||
|
|
||||||
if (steamIds.length) {
|
if (steamIds.length) {
|
||||||
|
|
|
||||||
12
src/main.rs
12
src/main.rs
|
|
@ -15,6 +15,7 @@ use crate::data::steam_id::SteamId;
|
||||||
use crate::data::user::User;
|
use crate::data::user::User;
|
||||||
use crate::fragments::demo_list::DemoList;
|
use crate::fragments::demo_list::DemoList;
|
||||||
use crate::pages::about::AboutPage;
|
use crate::pages::about::AboutPage;
|
||||||
|
use crate::pages::api::ApiPage;
|
||||||
use crate::pages::demo::DemoPage;
|
use crate::pages::demo::DemoPage;
|
||||||
use crate::pages::index::{DemoListScript, Index};
|
use crate::pages::index::{DemoListScript, Index};
|
||||||
use crate::pages::upload::{UploadPage, UploadScript};
|
use crate::pages::upload::{UploadPage, UploadScript};
|
||||||
|
|
@ -92,6 +93,7 @@ async fn main() -> Result<()> {
|
||||||
.route(LogoSvg::route(), get(serve_asset::<LogoSvg>))
|
.route(LogoSvg::route(), get(serve_asset::<LogoSvg>))
|
||||||
.route("/fragments/demo-list", get(demo_list))
|
.route("/fragments/demo-list", get(demo_list))
|
||||||
.route("/about", get(about))
|
.route("/about", get(about))
|
||||||
|
.route("/api", get(api))
|
||||||
.route("/login/callback", get(login_callback))
|
.route("/login/callback", get(login_callback))
|
||||||
.route("/login", get(login))
|
.route("/login", get(login))
|
||||||
.route("/logout", get(logout))
|
.route("/logout", get(logout))
|
||||||
|
|
@ -165,6 +167,16 @@ async fn about(State(_app): State<Arc<App>>, session: SessionData) -> Result<Mar
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn api(State(app): State<Arc<App>>, session: SessionData) -> Result<Markup> {
|
||||||
|
Ok(render(
|
||||||
|
ApiPage {
|
||||||
|
steam_id: session.steam_id().unwrap_or(SteamId::Id(76561198024494988)),
|
||||||
|
api_base: &app.api,
|
||||||
|
},
|
||||||
|
session,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
async fn demo(
|
async fn demo(
|
||||||
State(app): State<Arc<App>>,
|
State(app): State<Arc<App>>,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
|
|
|
||||||
412
src/pages/api.rs
Normal file
412
src/pages/api.rs
Normal file
|
|
@ -0,0 +1,412 @@
|
||||||
|
use crate::data::steam_id::SteamId;
|
||||||
|
use crate::pages::Page;
|
||||||
|
use maud::{html, Markup};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub struct ApiPage<'a> {
|
||||||
|
pub api_base: &'a str,
|
||||||
|
pub steam_id: SteamId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiPage<'_> {
|
||||||
|
fn api_link(&self, endpoint: &str) -> String {
|
||||||
|
format!("{}{}", self.api_base, endpoint)
|
||||||
|
}
|
||||||
|
fn api_link_params(&self, endpoint: &str, params: impl Display) -> String {
|
||||||
|
format!("{}{}{}", self.api_base, endpoint, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page for ApiPage<'_> {
|
||||||
|
fn title(&self) -> Cow<'static, str> {
|
||||||
|
"API - demos.tf".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> Markup {
|
||||||
|
html! {
|
||||||
|
div {
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "API" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Demos.tf provides a REST api that allows 3rd parties to the demo information stored on the site which is located at."
|
||||||
|
}
|
||||||
|
pre { (self.api_base) }
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Listing Demos" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"There are three api endpoints that can be used to retrieve a list of demos."
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
a href = (self.api_link("demos/")) { "/demos/" }
|
||||||
|
" lists all demos."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link_params("uploads/", &self.steam_id)) { " /uploads/" (self.steam_id) }
|
||||||
|
" lists demos uploaded by a user."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link_params("profiles/", &self.steam_id)) { " /profiles/" (self.steam_id) }
|
||||||
|
" lists demos in which a user played."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Users are identified by their steam id in the code "
|
||||||
|
code { "steamid64" }
|
||||||
|
" ("
|
||||||
|
code { "7656xxxxxxxxxxxxx" }
|
||||||
|
") format."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Filters" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Each of the three list end points accept the following filters to search for demos."
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
a href = (self.api_link("demos/?map=cp_granary")) { "map=xxxx" }
|
||||||
|
" only show demos for a specific map."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link("demos/?type=6v6")) { "type=xxx" }
|
||||||
|
" only show "
|
||||||
|
code { "4v4" }
|
||||||
|
", "
|
||||||
|
code { "6v6" }
|
||||||
|
" or "
|
||||||
|
code { "hl" }
|
||||||
|
" demos."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link_params("demos/?players[]=", &self.steam_id)) { "players[]=xxxx" }
|
||||||
|
" only show demos where a specific player has played."
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
"Multiple player filters can be specified to find demos where all of the given players have played."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
"Note that when using the "
|
||||||
|
code { "/profiles/$steamid" }
|
||||||
|
" endpoint the user for the endpoint is added to the filter."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link("demos/?before=1454455622")) { "before=xxx" }
|
||||||
|
" only show demos uploaded before a certain time."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link("demos/?after=1454455622")) { "after=xxx" }
|
||||||
|
" only show demos uploaded after a certain time."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link("demos/?before_id=12345")) { "before_id=xxx" }
|
||||||
|
" only show demos with an id lower than the provided one."
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
a href = (self.api_link("demos/?after_id=12345")) { "after_id=xxx" }
|
||||||
|
" only show demos with an id higher than the provided one."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"All filters should be provided as query parameter and can be combined in any combination."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Sorting" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"By default the demo listing will be sorted in descending order, meaning newer demos will be listed first, this can be changed by adding "
|
||||||
|
a href = (self.api_link("demos/?order=ASC")) { "order=ASC" }
|
||||||
|
"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Paging" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"All the list endpoints limit the number of items returned and accept a "
|
||||||
|
code { "page" }
|
||||||
|
" query parameter for retrieving larger number of results."
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
"As an alternative to using "
|
||||||
|
code { "page" }
|
||||||
|
" to offset the results you can also use the "
|
||||||
|
code { "after_id" }
|
||||||
|
" or "
|
||||||
|
code { "before_id" }
|
||||||
|
" to manually paginate your queries."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "List response" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"The response from a list endpoint consists of a list containing demo items in the following format."
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
id: 3314,
|
||||||
|
url: "https://static.demos.tf/...",
|
||||||
|
name: "stvdemos/22046_6v6-2015-08-02-15-21-blu_vs_red-cp_gullywash_final1.dem",
|
||||||
|
server: "TF2Pickup.net | #4.NL | 6v6 | Powered by SimRai.com",
|
||||||
|
duration: 1809,
|
||||||
|
nick: "SourceTV Demo",
|
||||||
|
map: "cp_gullywash_final1",
|
||||||
|
time: 1438523578,
|
||||||
|
red: "RED",
|
||||||
|
blue: "BLU",
|
||||||
|
redScore: 1,
|
||||||
|
blueScore: 5,
|
||||||
|
playerCount: 12,
|
||||||
|
uploader: 2565
|
||||||
|
}"#
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
code { "id" }
|
||||||
|
" the unique id of the demo"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "url" }
|
||||||
|
" the download url for the demo file"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "name" }
|
||||||
|
" the filename of the demo file"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "server" }
|
||||||
|
" the server name during the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "duration" }
|
||||||
|
" the length of the match in seconds"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "nick" }
|
||||||
|
" the nickname of the user recording the demo"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "map" }
|
||||||
|
" the map on which the match was played"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "time" }
|
||||||
|
" the time when the demo was uploaded as unix timestamp"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "red" }
|
||||||
|
" the name of the RED team during the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "blue" }
|
||||||
|
" the name of the BLU team during the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "redScore" }
|
||||||
|
" the number of points scored by the red team"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "blueScore" }
|
||||||
|
" the number of points scored by the blue team"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "playerCount" }
|
||||||
|
" the number of players in the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "uploader" }
|
||||||
|
" the unique id of the user which uploaded the demo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Demo info" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"The full information of a demo can be found at "
|
||||||
|
a href = (self.api_link("demos/314")) { "/demos/$id" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Demo response" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"The response from a demo endpoint is in the following format."
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
id: 314,
|
||||||
|
url: "https://static.demos.tf/...",
|
||||||
|
name: "match-20150323-1937-cp_process_final.dem",
|
||||||
|
server: "UGC 6v6 Match",
|
||||||
|
duration: 1809,
|
||||||
|
nick: "SourceTV Demo",
|
||||||
|
map: "cp_process_final",
|
||||||
|
time: 1427159270,
|
||||||
|
red: "TITS!",
|
||||||
|
blue: "BLU",
|
||||||
|
redScore: 3,
|
||||||
|
blueScore: 1,
|
||||||
|
playerCount: 12,
|
||||||
|
uploader: {
|
||||||
|
id: 1052,
|
||||||
|
steamid: "76561198028052915",
|
||||||
|
name: "Reƒraction"
|
||||||
|
},
|
||||||
|
players: [
|
||||||
|
{
|
||||||
|
id: 4364,
|
||||||
|
user_id: 1614,
|
||||||
|
name: "dankest memes",
|
||||||
|
team: "red",
|
||||||
|
'class': "scout",
|
||||||
|
steamid: "76561198070261020",
|
||||||
|
avatar: "http://cdn.akamai.steamstatic.com/steamcommunity/...",
|
||||||
|
kills: 10,
|
||||||
|
assists: 0,
|
||||||
|
deaths: 19
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}"#
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"The first 12 items are the same as the items in the list response."
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
code { "uploader" }
|
||||||
|
" information about the user who uploaded the demo"
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
code { "id" }
|
||||||
|
" the unique id for the user"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "steamid" }
|
||||||
|
" the steamid for the user"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "name" }
|
||||||
|
" the name of the uploader"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "players" }
|
||||||
|
" the information about the players of the match"
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
code { "id" }
|
||||||
|
" the unique id for user in this id"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "user_id" }
|
||||||
|
" the unique id for the user"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "name" }
|
||||||
|
" the name of the player during the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "class" }
|
||||||
|
" the class the player played during the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "steamid" }
|
||||||
|
" the steamid of the user"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "avatar" }
|
||||||
|
" the avatar for the user"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "kills" }
|
||||||
|
" the number of kills made by the player during the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "assists" }
|
||||||
|
" the number of assists made by the player during the match"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "deaths" }
|
||||||
|
" the number of deaths during the game"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Uploading Demos" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Demos can be uploaded by making a "
|
||||||
|
code { "POST" }
|
||||||
|
" request to "
|
||||||
|
code { (self.api_link("upload/")) }
|
||||||
|
" with the following fields set as form data."
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
code { "key" }
|
||||||
|
" the api key of the user uploading the demo"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "name" }
|
||||||
|
" the name of the demo file"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "red" }
|
||||||
|
" the name of the RED team"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "blu" }
|
||||||
|
" the name of the BLU team"
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
code { "demo" }
|
||||||
|
" the demo file to be uploaded, as form file upload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "Database Dump" }
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"If you're planning on analysing data from demos.tf, a public "
|
||||||
|
a href = "https://freezer.demos.tf/database/demostf.sql.gz" { "database dump" }
|
||||||
|
" for PostgreSQL is available for download."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod about;
|
pub mod about;
|
||||||
|
pub mod api;
|
||||||
pub mod demo;
|
pub mod demo;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
mod plugin_section;
|
mod plugin_section;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::data::steam_id::SteamId;
|
||||||
use crate::data::user::User;
|
use crate::data::user::User;
|
||||||
use crate::{App, Result};
|
use crate::{App, Result};
|
||||||
use async_session::SessionStore as _;
|
use async_session::SessionStore as _;
|
||||||
|
|
@ -22,6 +23,12 @@ impl SessionData {
|
||||||
SessionData::UnAuthenticated => None,
|
SessionData::UnAuthenticated => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn steam_id(&self) -> Option<SteamId> {
|
||||||
|
match self {
|
||||||
|
SessionData::Authenticated(user) => Some(user.steam_id.clone()),
|
||||||
|
SessionData::UnAuthenticated => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|
|
||||||
8
style/pages/api.css
Normal file
8
style/pages/api.css
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
li > a:first-child > code, li > code:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ section > div > h3 {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
section > div > h3:before,
|
section > div > h3:before,
|
||||||
|
|
@ -22,7 +23,7 @@ section > div > h3:after {
|
||||||
display: block;
|
display: block;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 45%;
|
top: calc(50% - 1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
section > div > h3:before {
|
section > div > h3:before {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
@import 'pages/index.css';
|
@import 'pages/index.css';
|
||||||
@import 'pages/demo.css';
|
@import 'pages/demo.css';
|
||||||
@import 'pages/about.css';
|
@import 'pages/about.css';
|
||||||
|
@import 'pages/api.css';
|
||||||
@import 'pages/upload.css';
|
@import 'pages/upload.css';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue