diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 342041d..aa90852 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,9 +1,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import {MainPageComponent} from "./pages/main-page/main-page.component"; +import {ServersPageComponent} from "./pages/servers-page/servers-page.component"; +import {ProfilePageComponent} from "./pages/profile-page/profile-page.component"; const routes: Routes = [ - { path: "", component: MainPageComponent} + { path: "", component: MainPageComponent}, + { path: "servers", component: ServersPageComponent }, + { path: "profile", component: ProfilePageComponent } ]; @NgModule({ diff --git a/src/app/app.component.html b/src/app/app.component.html index d3e96ff..ab50bb5 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,5 +1,7 @@ <mat-toolbar> - <span class="f13_color_primary" style="padding-left: calc((100% - 66%)/2)">факты</span><span class="f13_color_extra">13</span> + <div (click)="go2root()" style="padding-left: calc((100% - 66%)/2); cursor: pointer"> + <span class="f13_color_primary lato" >факты</span><span class="f13_color_extra lato">13</span> + </div> <span class="spacer"></span> <button class="f13_color_primary" style="margin-right: 5px" diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 22a7144..6286781 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import {BaseUtils} from "./utils/BaseUtils"; +import {Router} from "@angular/router"; @Component({ selector: 'app-root', @@ -14,5 +15,12 @@ export class AppComponent { {name: "Стим", link: "/steam"} ] + constructor(private router: Router) { + } + + go2root() { + this.router.navigate(["/"]) + } + } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c871edf..33e62f6 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -13,11 +13,16 @@ import {MatCardModule} from "@angular/material/card"; import {AnnonceService} from "./services/AnnonceService"; import {MatInputModule} from "@angular/material/input"; import {MatChipsModule} from "@angular/material/chips"; +import { ServersPageComponent } from './pages/servers-page/servers-page.component'; +import {MatExpansionModule} from "@angular/material/expansion"; +import { ProfilePageComponent } from './pages/profile-page/profile-page.component'; @NgModule({ declarations: [ AppComponent, - MainPageComponent + MainPageComponent, + ServersPageComponent, + ProfilePageComponent ], imports: [ BrowserModule, @@ -29,7 +34,8 @@ import {MatChipsModule} from "@angular/material/chips"; MatGridListModule, MatCardModule, MatInputModule, - MatChipsModule + MatChipsModule, + MatExpansionModule ], providers: [ AnnonceService diff --git a/src/app/entities/profile/PlayerProfile.ts b/src/app/entities/profile/PlayerProfile.ts new file mode 100644 index 0000000..4f02df1 --- /dev/null +++ b/src/app/entities/profile/PlayerProfile.ts @@ -0,0 +1,23 @@ +export class PlayerProfile { + ban: any|null = null; + gametime: {[srv_name: string]: {[map_name: string]: number}} = {}; + lastplay: {[srv_name: string]: {[map_name: string]: number}} = {}; + permition: any|null = null; + response_time: {[request: string]: number} = {}; + steam_data: any|null = null; + steamids: any|null = null; + play_on: any|null = null; + attached_discords: any[]|null = null; + donates: any[]|null = null; + ban_list: any[]|null = null; + killfeed: any|null = null; + killfeed_current: any = null; + messages: any = null; + reports: any = null; + + static fromData(data:any): PlayerProfile { + const p = new PlayerProfile(); + Object.assign(p, data); + return p; + } +} diff --git a/src/app/entities/profile/ProfileRequestData.ts b/src/app/entities/profile/ProfileRequestData.ts new file mode 100644 index 0000000..f1a1230 --- /dev/null +++ b/src/app/entities/profile/ProfileRequestData.ts @@ -0,0 +1,25 @@ +export class ProfileRequestData { + static PLAY_ON: ProfileRequestData = new ProfileRequestData("play_on", "Проверка играет ли сейчас"); + static STEAM_DATA: ProfileRequestData = new ProfileRequestData("steam_data", "Данные стима"); + static LAST_PLAY: ProfileRequestData = new ProfileRequestData("lastplay", "Последняя игра"); + static USER_TIME: ProfileRequestData = new ProfileRequestData("usertime", "Количество наигранного времени"); + static PERMITION: ProfileRequestData = new ProfileRequestData("permition", "Получить данные о правах"); + static BAN: ProfileRequestData = new ProfileRequestData("ban", "Получение текущего бана"); + static ATTACHED_DISCORD: ProfileRequestData = new ProfileRequestData("attached_discord", "Привязка к дискорду"); + static DONATES: ProfileRequestData = new ProfileRequestData("donates", "Получения данных о донатах"); + static BAN_LIST: ProfileRequestData = new ProfileRequestData("ban_list", "Получить список банов от пельменя"); + static KILLFEED: ProfileRequestData = new ProfileRequestData("killfeed", "Информация о убийствах, смертях, помощи"); + static REPORTS: ProfileRequestData = new ProfileRequestData("reports", "Информация о количестве репортах"); + static MESSAGES: ProfileRequestData = new ProfileRequestData("messages", "Информация о количестве сообщений"); + + param: string; + description: string; + default: boolean; + + + constructor(request_parameter: string, description_text: string) { + this.param = request_parameter; + this.description = description_text; + this.default = false; + } +} diff --git a/src/app/entities/servers/DockerStats.ts b/src/app/entities/servers/DockerStats.ts new file mode 100644 index 0000000..901842b --- /dev/null +++ b/src/app/entities/servers/DockerStats.ts @@ -0,0 +1,7 @@ +export interface DockerStats { + cpu: {percent: number}; + io: {input: number, output: number}; + mem: {percent: number, usage: number, limit: number}; + net: {input:number, output: number}; + utime: number; +} diff --git a/src/app/entities/servers/Player.ts b/src/app/entities/servers/Player.ts new file mode 100644 index 0000000..28018e6 --- /dev/null +++ b/src/app/entities/servers/Player.ts @@ -0,0 +1,15 @@ +export interface Player { + clz: number|null; + deads: number; + duration_seconds: number; + id: number; + loss: number; + name: string; + ping: number; + pos: number[]; + score: number; + state: string; + steam: any; + steam2: string; + team: number; +} diff --git a/src/app/entities/servers/Server.ts b/src/app/entities/servers/Server.ts new file mode 100644 index 0000000..646b0b5 --- /dev/null +++ b/src/app/entities/servers/Server.ts @@ -0,0 +1,22 @@ +import {DockerStats} from "./DockerStats"; +import {Uniq} from "./Uniq"; +import {Player} from "./Player"; + +export interface Server { + address: string; + city_pos: number[]; + color: string; + description: string; + dockerStats: DockerStats; + last_update: number; + map: string; + max_players: number; + name: string; + naming: string[]; + player_count: number; + players: Player[]; + preview: string; + status: boolean; + uniq: Uniq; + workshop: string|null; +} diff --git a/src/app/entities/servers/Uniq.ts b/src/app/entities/servers/Uniq.ts new file mode 100644 index 0000000..1fe6648 --- /dev/null +++ b/src/app/entities/servers/Uniq.ts @@ -0,0 +1,6 @@ +export interface Uniq { + day: number; + month: number; + year: number; + total: number; +} diff --git a/src/app/pages/main-page/main-page.component.html b/src/app/pages/main-page/main-page.component.html index e6a1ff5..4097c14 100644 --- a/src/app/pages/main-page/main-page.component.html +++ b/src/app/pages/main-page/main-page.component.html @@ -1,20 +1,18 @@ <!--тут меню кнопочки всякая хуйня--> <div - style="display: flex; width: 66%; padding-left: calc((100% - 66%)/2); padding-right: calc((100% - 66%)/2); margin: 0 auto; background: linear-gradient(to top, #f2a998, #e65e11);"> + class="content-in-center-header"> <div style="width: 15%"><img style="height: 250px; padding-top: 20px" src="assets/images/Engineertaunt1.png"></div> - <div style="width: 85%"> + <div style="width: 65%"> <!--кнопочки страниц--> <mat-toolbar style="background: unset !important; margin-bottom: 2%"> - <span class="spacer"></span> - <button *ngFor="let b of go2urls" mat-icon-button style="color: #fbf1d7; margin: 0 1%;"> + <button *ngFor="let b of go2urls" mat-icon-button style="color: #fbf1d7; margin: 0 1%;" class="spacer" (click)="go2url(b.url)"> <mat-icon>{{b.ico}}</mat-icon> <br> <p style="margin: 0 0; line-height: 10px">{{b.name}}</p> </button> - <span class="spacer"></span> </mat-toolbar> - <div > - <mat-grid-list cols="3" rowHeight="75px" style="color: #fbf1d7; padding-bottom: 1%"> + <div style="padding-bottom: 1%" > + <mat-grid-list cols="3" rowHeight="75px" style="color: #fbf1d7;"> <mat-grid-tile rowspan="1" colspan="1" style="border-radius: 15px; border: 0 solid black; background: rgba(255,255,255,0.25)"> <h2> <mat-icon>person</mat-icon> @@ -41,25 +39,25 @@ </mat-grid-tile> </mat-grid-list> </div> - <div> - <mat-form-field style="width: 100%" appearance="fill"> + <div style="padding-bottom: 1%"> + <mat-form-field style="width: 100%; background: white; border-radius: 15px" appearance="fill"> <mat-label>Введите ник...</mat-label> <input matInput placeholder="отдыхаем" value=""> </mat-form-field> </div> <div style="padding-bottom: 1%"> <mat-chip-list aria-label="Fish selection"> - <mat-chip>One fish</mat-chip> - <mat-chip>Two fish</mat-chip> - <mat-chip color="primary" selected>Primary fish</mat-chip> - <mat-chip color="accent" selected>Accent fish</mat-chip> + <mat-chip class="chips">One fish</mat-chip> + <mat-chip class="chips">Two fish</mat-chip> + <mat-chip class="chips" color="primary" selected>Primary fish</mat-chip> + <mat-chip class="chips" color="accent" selected>Accent fish</mat-chip> </mat-chip-list> </div> </div> </div> <!--тут продолжение главной страницы--> -<div style="width: 66%; margin: 0 auto"> +<div class="content-in-center"> <h2>Интереснные приколы</h2> <div class="card-annonces-container"> <mat-card diff --git a/src/app/pages/main-page/main-page.component.scss b/src/app/pages/main-page/main-page.component.scss index 457c8e3..46e2e75 100644 --- a/src/app/pages/main-page/main-page.component.scss +++ b/src/app/pages/main-page/main-page.component.scss @@ -10,3 +10,15 @@ color: white; } +::ng-deep .mat-form-field-underline { + display: none; +} + +::ng-deep .mat-form-field-wrapper { + padding-bottom: unset; +} + + +.chips { + border-radius: 8px 16px 16px 16px; +} diff --git a/src/app/pages/main-page/main-page.component.ts b/src/app/pages/main-page/main-page.component.ts index 75447a4..ed3f886 100644 --- a/src/app/pages/main-page/main-page.component.ts +++ b/src/app/pages/main-page/main-page.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import {AnnonceService} from "../../services/AnnonceService"; import {Annonce} from "../../entities/Annonce"; +import {Router} from "@angular/router"; @Component({ selector: 'app-main-page', @@ -9,18 +10,19 @@ import {Annonce} from "../../entities/Annonce"; }) export class MainPageComponent implements OnInit { go2urls: {ico: string, name: string, url: string}[] = [ - {ico: 'download', name: 'бля', url: '/'}, - {ico: 'download', name: 'бля', url: '/'}, - {ico: 'download', name: 'бля', url: '/'}, - {ico: 'download', name: 'бля', url: '/'}, - {ico: 'download', name: 'бля', url: '/'}, - {ico: 'download', name: 'бля', url: '/'}, - {ico: 'download', name: 'бля', url: '/'}, - {ico: 'download', name: 'бля', url: '/'} + {ico: 'download', name: 'Серверы', url: 'servers'}, + {ico: 'download', name: 'Правила', url: '/'}, + {ico: 'download', name: 'Банлист', url: '/'}, + {ico: 'download', name: 'VIP', url: '/'}, + {ico: 'download', name: 'Статистика', url: '/'}, + {ico: 'download', name: 'Сообщения', url: '/'}, + {ico: 'download', name: 'Киллфид', url: '/'}, + {ico: 'download', name: 'О нас', url: '/'} ] - constructor(private annoncesService: AnnonceService) { } + constructor(private annoncesService: AnnonceService, + private router: Router) { } ngOnInit(): void { } @@ -36,4 +38,8 @@ export class MainPageComponent implements OnInit { } } + go2url(url:string) { + this.router.navigate([url]) + } + } diff --git a/src/app/pages/profile-page/profile-page.component.html b/src/app/pages/profile-page/profile-page.component.html new file mode 100644 index 0000000..a296ae8 --- /dev/null +++ b/src/app/pages/profile-page/profile-page.component.html @@ -0,0 +1,7 @@ +<div class="content-in-center-header" style="flex-direction: column;"> + <h1>Вася убийца 2007</h1> + <h3>бибики</h3> +</div> + +<div class="content-in-center"> +</div> diff --git a/src/app/pages/profile-page/profile-page.component.scss b/src/app/pages/profile-page/profile-page.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/profile-page/profile-page.component.ts b/src/app/pages/profile-page/profile-page.component.ts new file mode 100644 index 0000000..4630843 --- /dev/null +++ b/src/app/pages/profile-page/profile-page.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import {ActivatedRoute} from "@angular/router"; +import {PlayerServiceService} from "../../services/player-service.service"; +import {PlayerProfile} from "../../entities/profile/PlayerProfile"; +import {MatSnackBar} from "@angular/material/snack-bar"; + +@Component({ + selector: 'app-profile-page', + templateUrl: './profile-page.component.html', + styleUrls: ['./profile-page.component.scss'] +}) +export class ProfilePageComponent implements OnInit { + + profile: PlayerProfile|null = null; + + constructor(private route: ActivatedRoute, + private playerService: PlayerServiceService, + private snack: MatSnackBar) { } + + ngOnInit(): void { + const steam64: string|null = this.route.snapshot.queryParamMap.get("steam64"); + this.loadPlayer(steam64); + } + + loadPlayer(steam64: string|null) { + this.playerService.getProfile(steam64, []).subscribe( + (res) => this.profile = res, + (err) => this.snack.open("Невозможно загрузить профиль") + ); + } +} diff --git a/src/app/pages/servers-page/servers-page.component.html b/src/app/pages/servers-page/servers-page.component.html new file mode 100644 index 0000000..82af44b --- /dev/null +++ b/src/app/pages/servers-page/servers-page.component.html @@ -0,0 +1,35 @@ +<div class="content-in-center-header" style="flex-direction: column;"> + <h1>Серверы</h1> + <h3>Информация о игроках и живых серверах</h3> +</div> + +<div class="content-in-center"> + <mat-accordion> + <mat-expansion-panel hideToggle *ngFor="let server of servers | keyvalue"> + <mat-expansion-panel-header> + <mat-panel-title> + {{server.value.name}} + </mat-panel-title> + <mat-panel-description> + {{server.value.player_count}}/{{server.value.max_players}} + </mat-panel-description> + </mat-expansion-panel-header> + <div> + <p>{{server.value.description}}</p> + <mat-expansion-panel hideToggle *ngFor="let player of server.value.players"> + <mat-expansion-panel-header> + <mat-panel-title> + {{player.name}} + </mat-panel-title> + <mat-panel-description> + {{player.score}} / {{player.deads}} / {{player.duration_seconds}} + </mat-panel-description> + </mat-expansion-panel-header> + <p>Ид игрока: {{player.id}}</p> + <p>Пинг: {{player.ping}}</p> + <p>Потери пакетов: {{player.loss}}</p> + </mat-expansion-panel> + </div> + </mat-expansion-panel> + </mat-accordion> +</div> diff --git a/src/app/pages/servers-page/servers-page.component.scss b/src/app/pages/servers-page/servers-page.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/servers-page/servers-page.component.ts b/src/app/pages/servers-page/servers-page.component.ts new file mode 100644 index 0000000..a1a4557 --- /dev/null +++ b/src/app/pages/servers-page/servers-page.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import {WebsocketServersListenerService} from "../../services/websocket-servers-listener.service"; +import {Server} from "../../entities/servers/Server"; + +@Component({ + selector: 'app-servers-page', + templateUrl: './servers-page.component.html', + styleUrls: ['./servers-page.component.scss'] +}) +export class ServersPageComponent implements OnInit { + + servers:{[srv_name: string]:Server} = {}; + + constructor(private servers_ws:WebsocketServersListenerService) { } + + ngOnInit(): void { + this.servers_ws.getServers().subscribe((r) => { + if (r.state == 0) this.servers = r.servers; + else { + for (let srv_name of Object.keys(r.servers)) { + Object.assign(this.servers[srv_name], r.servers[srv_name]); + } + } + }); + } + +} diff --git a/src/app/services/WebSockerServerMessage.ts b/src/app/services/WebSockerServerMessage.ts new file mode 100644 index 0000000..1c93232 --- /dev/null +++ b/src/app/services/WebSockerServerMessage.ts @@ -0,0 +1,6 @@ +import {Server} from "../entities/servers/Server"; + +export interface WebSockerServerMessage { + servers: {[srv_name: string]:Server}; + state: 0|1; +} diff --git a/src/app/services/player-service.service.ts b/src/app/services/player-service.service.ts new file mode 100644 index 0000000..fd7df29 --- /dev/null +++ b/src/app/services/player-service.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {ProfileRequestData} from "../entities/profile/ProfileRequestData"; +import * as http from "http"; +import {map, Observable} from "rxjs"; +import {PlayerProfile} from "../entities/profile/PlayerProfile"; + +@Injectable({ + providedIn: 'root' +}) +export class PlayerServiceService { + + constructor(private http: HttpClient) {} + + getProfile(steam64: string|null, request: ProfileRequestData[]): Observable<PlayerProfile> { + const params = { + requests: request.map((p) => p.param) + } + return this.http.get("api/profile/" + steam64 == null ? 'current' : 'web', {params: params}) + .pipe(map((r) => PlayerProfile.fromData(r))); + } +} diff --git a/src/app/services/websocket-servers-listener.service.ts b/src/app/services/websocket-servers-listener.service.ts new file mode 100644 index 0000000..c80a27d --- /dev/null +++ b/src/app/services/websocket-servers-listener.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import {WebSocketSubject} from "rxjs/internal/observable/dom/WebSocketSubject"; +import {webSocket} from "rxjs/webSocket"; +import {map, Observable} from "rxjs"; +import {WebSockerServerMessage} from "./WebSockerServerMessage"; + +@Injectable({ + providedIn: 'root' +}) +export class WebsocketServersListenerService { + private socket: WebSocketSubject<any>; + + constructor() { + this.socket = webSocket('wss://tf2.pblr-nyk.pro/ws/servers'); + } + + getServers(): Observable<WebSockerServerMessage> { + return this.socket.asObservable() + } +} diff --git a/src/assets/fonts/Lato-Black.ttf b/src/assets/fonts/Lato-Black.ttf new file mode 100644 index 0000000..a87109f Binary files /dev/null and b/src/assets/fonts/Lato-Black.ttf differ diff --git a/src/assets/fonts/Lato-Bold.ttf b/src/assets/fonts/Lato-Bold.ttf new file mode 100644 index 0000000..59c4843 Binary files /dev/null and b/src/assets/fonts/Lato-Bold.ttf differ diff --git a/src/assets/fonts/Lato-Regular.ttf b/src/assets/fonts/Lato-Regular.ttf new file mode 100644 index 0000000..f01f558 Binary files /dev/null and b/src/assets/fonts/Lato-Regular.ttf differ diff --git a/src/assets/fonts/Lato-Thin.ttf b/src/assets/fonts/Lato-Thin.ttf new file mode 100644 index 0000000..ca2cc0d Binary files /dev/null and b/src/assets/fonts/Lato-Thin.ttf differ diff --git a/src/styles.scss b/src/styles.scss index e704f76..0131bfc 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -35,8 +35,40 @@ $f13_ang_web-theme: mat.define-light-theme(( /* You can add global styles to this file, and also import other style files */ +@font-face { + font-family: Lato; + src: url('assets/fonts/Lato-Regular.ttf') format('truetype'); +} + +@font-face { + font-family: LatoBlack; + src: url('assets/fonts/Lato-Black.ttf') format('truetype'); +} + +@font-face { + font-family: LatoThin; + src: url('assets/fonts/Lato-Thin.ttf') format('truetype'); +} + + html, body { height: 100%; } -body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } +body { margin: 0; font-family: Lato, Roboto, "Helvetica Neue", sans-serif; } + +h1,h2,h3,h4,h5,h6 { + font-family: LatoBlack, Roboto, "Helvetica Neue", sans-serif; +} + +span { + font-family: LatoThin, Roboto, "Helvetica Neue", sans-serif; +} + +.lato { + font-family: LatoBlack, Roboto, "Helvetica Neue", sans-serif; +} + +.mat-card-subtitle { + font-family: LatoThin, Roboto, "Helvetica Neue", sans-serif; +} .spacer { flex: 1 1 auto; @@ -53,3 +85,11 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } .f13_color_extra { color: #5e7edf; } + +.content-in-center { + width: 66%; margin: 0 auto +} + +.content-in-center-header { + display: flex; width: 66%; padding-left: calc((100% - 66%)/2); padding-right: calc((100% - 66%)/2); margin: 0 auto; background: linear-gradient(to top, #f2a998, #e65e11); +}