├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENCE ├── README.md ├── _scripts ├── config │ ├── config.build.json │ └── config.dev.json ├── csgo-configs │ ├── knife │ │ └── default.txt │ └── main │ │ ├── competitive-esl5v5.txt │ │ ├── dangerzone-default.txt │ │ └── wingman-esl2v2.txt └── webpack │ └── webpack.config.js ├── app ├── src │ ├── bundle.js │ ├── config │ │ ├── baseConfig.js │ │ ├── index.js │ │ └── version │ │ │ └── index.js │ ├── controllers │ │ ├── Api │ │ │ ├── BaseController.js │ │ │ ├── CsgoController.js │ │ │ └── IndexController.js │ │ └── Web │ │ │ ├── BaseController.js │ │ │ └── IndexController.js │ ├── log │ │ └── .gitkeep │ ├── modules │ │ ├── assets.js │ │ ├── challonge.js │ │ ├── csgoConfig.js │ │ ├── database.js │ │ ├── logger.js │ │ ├── queue.js │ │ ├── rcon.js │ │ ├── router.js │ │ ├── socket.js │ │ └── web.js │ ├── routers │ │ ├── Api.js │ │ └── Web.js │ ├── server.js │ ├── utils │ │ ├── Arrays.js │ │ └── Strings.js │ └── views │ │ ├── general │ │ └── notfound.ejs │ │ ├── index.ejs │ │ ├── index │ │ └── index.ejs │ │ └── partials │ │ └── head.ejs └── test │ └── functions.test.js ├── frontend ├── components │ ├── About.js │ ├── Create.js │ ├── Detail.js │ ├── Edit.js │ ├── Home.js │ ├── NotFound.js │ ├── Servers.js │ ├── Settings.js │ ├── flags │ │ ├── DE.js │ │ ├── FR.js │ │ ├── GB.js │ │ └── NL.js │ ├── icons │ │ ├── Add.js │ │ ├── Close.js │ │ ├── Details.js │ │ ├── Edit.js │ │ ├── Home.js │ │ ├── Language.js │ │ ├── Notification.js │ │ ├── Servers.js │ │ └── Settings.js │ ├── integrations │ │ ├── Archive.js │ │ ├── Challonge.js │ │ ├── Csv.js │ │ ├── ForceArchive.js │ │ └── MatchGroups.js │ ├── partials │ │ ├── Alert.js │ │ ├── Breadcrumbs.js │ │ ├── Footer.js │ │ ├── Header.js │ │ └── Notification.js │ └── settings │ │ └── Log.js ├── lang │ ├── de │ │ ├── about.json │ │ ├── create.json │ │ ├── detail.json │ │ ├── edit.json │ │ ├── general.json │ │ ├── home.json │ │ ├── index.js │ │ ├── servers.json │ │ └── settings.json │ ├── en │ │ ├── about.json │ │ ├── create.json │ │ ├── detail.json │ │ ├── edit.json │ │ ├── general.json │ │ ├── home.json │ │ ├── index.js │ │ ├── servers.json │ │ └── settings.json │ ├── fr │ │ ├── about.json │ │ ├── create.json │ │ ├── detail.json │ │ ├── edit.json │ │ ├── general.json │ │ ├── home.json │ │ ├── index.js │ │ ├── servers.json │ │ └── settings.json │ ├── index.js │ └── nl │ │ ├── about.json │ │ ├── create.json │ │ ├── detail.json │ │ ├── edit.json │ │ ├── general.json │ │ ├── home.json │ │ ├── index.js │ │ ├── servers.json │ │ └── settings.json ├── main.js ├── modules │ ├── language.js │ ├── socket.js │ ├── storage.js │ ├── store.js │ └── systemNotification.js ├── scss │ ├── components │ │ ├── create.scss │ │ ├── detail.scss │ │ ├── home.scss │ │ ├── icons │ │ │ ├── add.scss │ │ │ ├── close.scss │ │ │ ├── details.scss │ │ │ ├── edit.scss │ │ │ ├── home.scss │ │ │ ├── language.scss │ │ │ ├── notification.scss │ │ │ ├── servers.scss │ │ │ └── settings.scss │ │ ├── partials │ │ │ ├── alert.scss │ │ │ ├── breadcrumbs.scss │ │ │ ├── footer.scss │ │ │ ├── header.scss │ │ │ └── notification.scss │ │ └── settings │ │ │ └── log.scss │ ├── global │ │ ├── base.scss │ │ ├── normalize.scss │ │ └── utils.scss │ ├── mixins │ │ ├── helpers.scss │ │ ├── mediaqueries.scss │ │ └── typography.scss │ ├── style.scss │ └── variables │ │ ├── animations.scss │ │ ├── breakpoints.scss │ │ ├── colors.scss │ │ └── fonts.scss ├── test │ └── lang.test.js └── utils │ ├── Arrays.js │ └── Strings.js ├── package-lock.json ├── package.json └── public ├── assets └── csgo-remote_matches.csv └── manifest.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | 4 | # Set default charset with unix-style newlines with a newline ending every file 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | # PHP PSR2 overrides 12 | [*.php] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | # JS overrides 17 | [*.js] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | # SCSS and JSON overrides 22 | [*.{scss,json}] 23 | indent_style = space 24 | indent_size = 2 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | liberapay: glenndehaan 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # PhpStorm 2 | .idea 3 | 4 | # General files 5 | *~ 6 | *.DS_STORE 7 | 8 | # Js & css build files 9 | dist/ 10 | 11 | # Dependency managers 12 | node_modules/ 13 | npm-debug.log 14 | 15 | # App 16 | csgo-rcon.json 17 | *.log 18 | config.json 19 | app/src/config/version/version.txt 20 | 21 | # Tests 22 | coverage/ 23 | .nyc_output/ 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Glenn de Haan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Counter-Strike Global Offensive Web Panel 2 | 3 | A web panel to control a CS:GO server 4 | 5 | [![Build Status](https://travis-ci.org/glenndehaan/csgo-rcon-nodejs.svg?branch=master)](https://travis-ci.org/glenndehaan/csgo-rcon-nodejs) [![release](https://img.shields.io/github/release/glenndehaan/csgo-rcon-nodejs.svg)](https://github.com/glenndehaan/csgo-rcon-nodejs/releases) [![dependencies](https://david-dm.org/glenndehaan/csgo-rcon-nodejs.svg)](https://github.com/glenndehaan/csgo-rcon-nodejs/blob/master/package.json) [![license](https://img.shields.io/github/license/glenndehaan/csgo-rcon-nodejs.svg)](https://github.com/glenndehaan/csgo-rcon-nodejs/blob/master/LICENCE) 6 | 7 | ## Backend Structure 8 | - NodeJS 9 | - Simple Node Logger 10 | - srcds-rcon 11 | - Json DB 12 | - Express 13 | - Express-WS 14 | 15 | ## Frontend Structure 16 | - Webpack 17 | - Preact 18 | - Preact Router 19 | - Bootstrap 20 | - Sass 21 | - Sockette 22 | 23 | ## Basic Usage 24 | - Download the latest version from the releases page on GitHub 25 | - Save the binary in it's own folder 26 | - Run the binary (this will create some additional files/folders) 27 | - Adjust the `config.json` 28 | - Restart the binary 29 | 30 | Then open up a webbrowser and go to the site 31 | 32 | ## Development Usage 33 | - Install NodeJS 8.0 or higher 34 | - Copy the `_scripts/config/config.dev.json` to here `app/src/config/config.json` 35 | - Run `npm install` in the root project folder 36 | - Run `npm run webpack` in the root project folder 37 | - Run `npm run dev` in the root project folder 38 | 39 | Then open up a webbrowser and go to the site 40 | 41 | ## Logging 42 | All logs will be written to the `csgo-rcon.log` file in the node folder. 43 | 44 | To increase the logging change the logger level in the `config.json` file from `info` to `debug`. 45 | 46 | ## Database 47 | To make this as simple as it is I use a local Json database. 48 | 49 | Checkout `csgo-rcon.json` since this is the db file. 50 | 51 | ## Plugin 52 | To enable livescoring and auto match configuration please install the SourceMod Plugin: 53 | 54 | https://github.com/glenndehaan/csgo-rcon-plugin 55 | 56 | ## Language Support 57 | - English 58 | - French 59 | - German 60 | - Dutch 61 | 62 | ## config.json Explanation 63 | ``` 64 | { 65 | "application": { 66 | "companyName": "A Company", <<- This name will be prefixed in the servername 67 | "baseUrl": "http://CURRENTIP:3542" <<- Change 'CURRENTIP' to the IP of the CSGO-Remote server. Your CS:GO servers must be able to connect to the CSGO-Remote app 68 | }, 69 | "servers": [ <<- Put all your CS:GO server in this block 70 | { <<- A server block 71 | "ip": "192.168.1.xx", <<- CS:GO server IP/Hostname 72 | "port": 27015, <<- CS:GO server Port 73 | "password": "anrconpassword", <<- CS:GO server password 74 | "default_map": "de_dust2", <<- CS:GO server default loading map 75 | "server_restore_config": "server" <<- Leave this default 76 | } 77 | ], 78 | "broadcaster": { <<- The broadcaster sends messages to the CS:GO server chat 79 | "enabled": true, <<- Enables the broadcaster 80 | "speed": 120, <<- After how long do we need to send the next message in seconds 81 | "messages": [ <<- This block contains all the messages 82 | "This is message 1", <<- This is one message 83 | "This is message 2", 84 | "This is message 3" 85 | ] 86 | }, 87 | "authentication": { <<- This block is for the /settings pages 88 | "username": "root", <<- Username for the /settings pages 89 | "password": "password123!" <<- Password for the /settings pages 90 | } 91 | } 92 | ``` 93 | 94 | ## v2 TODO's 95 | * GitHub Request: https://github.com/glenndehaan/csgo-rcon-nodejs/issues/2 96 | * ~~Wingman: `Connect server` needs to change map to wingman map~~ 97 | * ~~Dangerzone: `Connect server` needs to change map to dangerzone map~~ 98 | * ~~Dangerzone: `Knife round` needs to be disabled~~ 99 | * ~~Dangerzone: `Start match` needs to stop warmup~~ 100 | * ~~Dangerzone/Wingman: Restore game_type/game_mode to competitive~~ 101 | * ~~Healthcheck needs to be faster~~ 102 | * ~~Translations add socket error messages~~ 103 | * ~~Add frontend translations~~ 104 | * Add Bo1, Bo2, Bo3, Bo4, Bo5 support 105 | * ~~Add wingman and dangerzone support (maps, configs)~~ 106 | * Add support for uploading scores back to challonge 107 | * Replace basic auth with frontend login 108 | * Add livescoring to Home/Servers view 109 | * ~~Add Readme `config.json` explanation~~ 110 | * Implement LoadBalancing (Redis)? 111 | * Implement MongoDB? 112 | * ~~Implement lang files tests~~ 113 | * ~~Implement basic tests~~ 114 | * ~~Implement NodeJS server logs web interface~~ 115 | * ~~Implement NodeJS server controls~~ (Not possible) 116 | * ~~Better GitHub release integration~~ 117 | * ~~Rcon Healthcheck (Auto reinit Rcon connection)~~ 118 | * ~~Rcon Command error/timeout (https://github.com/randunel/node-srcds-rcon#specify-command-timeout)~~ 119 | 120 | ## v1 TODO's 121 | * ~~Lock matches on same server when one match is running.~~ 122 | * ~~Fix production PKG build.~~ 123 | * ~~Add say to admin interface.~~ 124 | * ~~Add broadcaster to config file.~~ 125 | * ~~Fix restore server config file.~~ (Restore config is now optional) 126 | * ~~Catch srcds error's. (Send cmd if rcon fails)~~ 127 | * ~~Create one cmd for rcon. (Use in startMatch, reset) Let others use that.~~ 128 | * ~~Rcon reconnect?~~ (Not possible with current package!) 129 | * ~~Rcon server/match status? (V2)~~ 130 | * ~~Challonge (API/Webhook) match import?~~ 131 | * ~~Rewrite (server modules) to ES6 classes?~~ 132 | * ~~Bootstrap Notification bar. (Showing that we are sending something)~~ 133 | * ~~Add match groups~~ 134 | * ~~Edit match~~ 135 | * ~~Add native system notifications~~ 136 | * ~~Add SVG's to replace bulky buttons~~ 137 | * ~~Protect /settings page with basic auth (Username/Password in config file)~~ 138 | * ~~Challonge import server option: Next available server~~ 139 | * ~~Challonge import complete notification update~~ 140 | * ~~Settings icon active state stuck~~ 141 | * ~~Restart game implement are you sure dialog~~ 142 | * ~~Rewrite queue module~~ 143 | * ~~Filter matches on homepage (Not started, Running, Completed)~~ 144 | * ~~/settings add archive complete matches function~~ 145 | * ~~Disable match/server controls if match isn't started~~ 146 | * ~~Server overview page to so available server where no matches are running~~ 147 | * ~~Add breadcrumbs~~ 148 | * ~~Add /settings force archive match~~ 149 | * ~~/about page with software info~~ 150 | * ~~Version update available based on GIT (GitHub)~~ 151 | * ~~Show development/production version~~ 152 | * Autosetup server (V3) 153 | * Match control password protect at match create 154 | * ~~Plugin: Lock match data after match_end~~ 155 | * ~~Plugin: Split connect server/start match~~ 156 | * ~~Plugin: Add start warmup button~~ 157 | * ~~Plugin: Autoflow Connect Server->Warmup->Start Knife->Knife (End)->Warmup->Start match->Match end->Auto restore server~~ 158 | * ~~CSV Import matches~~ 159 | 160 | ## License 161 | 162 | MIT 163 | -------------------------------------------------------------------------------- /_scripts/config/config.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "application": { 3 | "companyName": "A Company", 4 | "baseUrl": "http://CURRENTIP:3542" 5 | }, 6 | "servers": [ 7 | { 8 | "ip": "192.168.1.xx", 9 | "port": 27015, 10 | "password": "anrconpassword", 11 | "default_map": "de_dust2", 12 | "server_restore_config": "server" 13 | } 14 | ], 15 | "broadcaster": { 16 | "enabled": true, 17 | "speed": 120, 18 | "messages": [ 19 | "This is message 1", 20 | "This is message 2", 21 | "This is message 3" 22 | ] 23 | }, 24 | "authentication": { 25 | "username": "root", 26 | "password": "password123!" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /_scripts/config/config.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "application": { 3 | "companyName": "A Company", 4 | "csgoConfigFolder": "../../_scripts/csgo-configs", 5 | "baseUrl": "http://CURRENTIP:3542" 6 | }, 7 | "logger": { 8 | "level": "trace" 9 | }, 10 | "servers": [ 11 | { 12 | "ip": "192.168.1.xx", 13 | "port": 27015, 14 | "password": "anrconpassword", 15 | "default_map": "de_dust2", 16 | "server_restore_config": "server" 17 | } 18 | ], 19 | "broadcaster": { 20 | "enabled": true, 21 | "speed": 120, 22 | "messages": [ 23 | "This is message 1", 24 | "This is message 2", 25 | "This is message 3" 26 | ] 27 | }, 28 | "authentication": { 29 | "username": "root", 30 | "password": "password123!" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /_scripts/csgo-configs/knife/default.txt: -------------------------------------------------------------------------------- 1 | mp_warmup_end 1 2 | mp_ct_default_secondary "" 3 | mp_free_armor 1 4 | mp_give_player_c4 0 5 | mp_maxmoney 0 6 | mp_respawn_on_death_ct 0 7 | mp_respawn_on_death_t 0 8 | mp_roundtime 1.92 9 | mp_roundtime_defuse 1.92 10 | mp_roundtime_hostage 1.92 11 | mp_t_default_secondary "" 12 | bot_quota 0 13 | -------------------------------------------------------------------------------- /_scripts/csgo-configs/main/dangerzone-default.txt: -------------------------------------------------------------------------------- 1 | // Reset Knife data 2 | mp_t_default_secondary "weapon_glock" 3 | mp_ct_default_secondary "weapon_hkp2000" 4 | mp_give_player_c4 1 5 | // End Reset Knife data 6 | 7 | // Gamemode 8 | game_mode 0 9 | game_type 6 10 | // End Gamemode 11 | 12 | // Change map 13 | changelevel dz_blacksite 14 | -------------------------------------------------------------------------------- /_scripts/csgo-configs/main/wingman-esl2v2.txt: -------------------------------------------------------------------------------- 1 | // ESL - www.eslgaming.com 2 | // CS:GO 2on2 Ladder Config 3 | // 14.01.2016 4 | 5 | // Reset Knife data 6 | mp_t_default_secondary "weapon_glock" 7 | mp_ct_default_secondary "weapon_hkp2000" 8 | mp_give_player_c4 1 9 | // End Reset Knife data 10 | 11 | // Gamemode 12 | game_mode 2 13 | game_type 0 14 | // End Gamemode 15 | 16 | ammo_grenade_limit_default 1 17 | ammo_grenade_limit_flashbang 2 18 | ammo_grenade_limit_total 4 19 | 20 | bot_quota "0" // Determines the total number of bots in the game 21 | 22 | cash_team_terrorist_win_bomb 3500 23 | cash_team_elimination_bomb_map 3250 24 | cash_team_win_by_defusing_bomb 3500 25 | cash_team_win_by_hostage_rescue 3500 26 | cash_team_loser_bonus 1400 27 | cash_team_loser_bonus_consecutive_rounds 500 28 | cash_team_rescued_hostage 750 29 | cash_team_hostage_alive 150 30 | cash_team_planted_bomb_but_defused 800 31 | cash_team_hostage_interaction 150 32 | cash_player_killed_teammate -300 33 | cash_player_killed_enemy_default 300 34 | cash_player_killed_enemy_factor 1 35 | cash_player_bomb_planted 300 36 | cash_player_bomb_defused 300 37 | cash_player_rescued_hostage 1000 38 | cash_player_interact_with_hostage 150 39 | cash_player_damage_hostage -30 40 | cash_player_killed_hostage -1000 41 | 42 | ff_damage_reduction_grenade 0.85 // How much to reduce damage done to teammates by a thrown grenade. Range is from 0 - 1 (with 1 being damage equal to what is done to an enemy) 43 | ff_damage_reduction_bullets 0.33 // How much to reduce damage done to teammates when shot. Range is from 0 - 1 (with 1 being damage equal to what is done to an enemy) 44 | ff_damage_reduction_other 0.4 // How much to reduce damage done to teammates by things other than bullets and grenades. Range is from 0 - 1 (with 1 being damage equal to what is done to an enemy) 45 | ff_damage_reduction_grenade_self 1 // How much to damage a player does to himself with his own grenade. Range is from 0 - 1 (with 1 being damage equal to what is done to an enemy) 46 | 47 | mp_afterroundmoney 0 // amount of money awared to every player after each round 48 | mp_autokick 0 // Kick idle/team-killing players 49 | mp_autoteambalance 0 50 | mp_buytime 15 // How many seconds after round start players can buy items for. 51 | mp_c4timer 40 // How long from when the C4 is armed until it blows 52 | mp_death_drop_defuser 1 // Drop defuser on player death 53 | mp_death_drop_grenade 2 // Which grenade to drop on player death: 0=none, 1=best, 2=current or best 54 | mp_death_drop_gun 1 // Which gun to drop on player death: 0=none, 1=best, 2=current or best 55 | mp_defuser_allocation 0 // How to allocate defusers to CTs at start or round: 0=none, 1=random, 2=everyone 56 | mp_do_warmup_period 1 // Whether or not to do a warmup period at the start of a match. 57 | mp_forcecamera 1 // Restricts spectator modes for dead players 58 | mp_force_pick_time 160 // The amount of time a player has on the team screen to make a selection before being auto-teamed 59 | mp_free_armor 0 // Determines whether armor and helmet are given automatically. 60 | mp_freezetime 10 // How many seconds to keep players frozen when the round starts 61 | mp_friendlyfire 1 // Allows team members to injure other members of their team 62 | mp_halftime 1 // Determines whether or not the match has a team-swapping halftime event. 63 | mp_halftime_duration 15 // Number of seconds that halftime lasts 64 | mp_join_grace_time 30 // Number of seconds after round start to allow a player to join a game 65 | mp_limitteams 0 // Max # of players 1 team can have over another (0 disables check) 66 | mp_logdetail 3 // Logs attacks. Values are: 0=off, 1=enemy, 2=teammate, 3=both) 67 | mp_match_can_clinch 1 // Can a team clinch and end the match by being so far ahead that the other team has no way to catching up 68 | mp_match_end_restart 1 // At the end of the match, perform a restart instead of loading a new map 69 | mp_maxmoney 16000 // maximum amount of money allowed in a player's account 70 | mp_maxrounds 30 // max number of rounds to play before server changes maps 71 | mp_molotovusedelay 0 // Number of seconds to delay before the molotov can be used after acquiring it 72 | mp_playercashawards 1 // Players can earn money by performing in-game actions 73 | mp_playerid 0 // Controls what information player see in the status bar: 0 all names; 1 team names; 2 no names 74 | mp_playerid_delay 0.5 // Number of seconds to delay showing information in the status bar 75 | mp_playerid_hold 0.25 // Number of seconds to keep showing old information in the status bar 76 | mp_round_restart_delay 5 // Number of seconds to delay before restarting a round after a win 77 | mp_roundtime 1.92 // How many minutes each round takes. 78 | mp_roundtime_defuse 1.92 // How many minutes each round takes on defusal maps. 79 | mp_solid_teammates 1 // Determines whether teammates are solid or not. 80 | mp_startmoney 800 // amount of money each player gets when they reset 81 | mp_teamcashawards 1 // Teams can earn money by performing in-game actions 82 | mp_timelimit 0 // game time per map in minutes 83 | mp_tkpunish 0 // Will a TK'er be punished in the next round? {0=no, 1=yes} 84 | mp_warmuptime 1 // If true, there will be a warmup period/round at the start of each match to allow 85 | mp_weapons_allow_map_placed 1 // If this convar is set, when a match starts, the game will not delete weapons placed in the map. 86 | mp_weapons_allow_zeus 1 // Determines whether the Zeus is purchasable or not. 87 | mp_win_panel_display_time 15 // The amount of time to show the win panel between matches / halfs 88 | 89 | spec_freeze_time 2.0 // Time spend frozen in observer freeze cam. 90 | spec_freeze_panel_extended_time 0 // Time spent with the freeze panel still up after observer freeze cam is done. 91 | spec_freeze_time_lock 2 92 | spec_freeze_deathanim_time 0 93 | 94 | sv_accelerate 5.5 // ( def. "10" ) client notify replicated 95 | sv_stopspeed 80 // 96 | sv_allow_votes 0 // Allow voting? 97 | sv_allow_wait_command 0 // Allow or disallow the wait command on clients connected to this server. 98 | sv_alltalk 0 // Players can hear all other players' voice communication, no team restrictions 99 | sv_alternateticks 0 // If set, server only simulates entities on even numbered ticks. 100 | sv_cheats 0 // Allow cheats on server 101 | sv_clockcorrection_msecs 15 // The server tries to keep each player's m_nTickBase withing this many msecs of the server absolute tickcount 102 | sv_consistency 0 // Whether the server enforces file consistency for critical files 103 | sv_contact 0 // Contact email for server sysop 104 | sv_damage_print_enable 0 // Turn this off to disable the player's damage feed in the console after getting killed. 105 | sv_dc_friends_reqd 0 // Set this to 0 to allow direct connects to a game in progress even if no presents 106 | sv_deadtalk 0 // Dead players can speak (voice, text) to the living 107 | sv_forcepreload 0 // Force server side preloading. 108 | sv_friction 5.2 // World friction. 109 | sv_full_alltalk 0 // Any player (including Spectator team) can speak to any other player 110 | sv_gameinstructor_disable 1 // Force all clients to disable their game instructors. 111 | sv_ignoregrenaderadio 0 // Turn off Fire in the hole messages 112 | sv_kick_players_with_cooldown 0 // (0: do not kick; 1: kick Untrusted players; 2: kick players with any cooldown) 113 | sv_kick_ban_duration 0 // How long should a kick ban from the server should last (in minutes) 114 | sv_lan 0 // Server is a lan server ( no heartbeat, no authentication, no non-class C addresses ) 115 | sv_log_onefile 0 // Log server information to only one file. 116 | sv_logbans 1 // Log server bans in the server logs. 117 | sv_logecho 1 // Echo log information to the console. 118 | sv_logfile 1 // Log server information in the log file. 119 | sv_logflush 0 // Flush the log file to disk on each write (slow). 120 | sv_logsdir logfiles // Folder in the game directory where server logs will be stored. 121 | sv_maxrate 0 // min. 0.000000 max. 30000.000000 replicated Max bandwidth rate allowed on server, 0 == unlimited 122 | sv_mincmdrate 30 // This sets the minimum value for cl_cmdrate. 0 == unlimited. 123 | sv_minrate 20000 // Min bandwidth rate allowed on server, 0 == unlimited 124 | sv_competitive_minspec 1 // Enable to force certain client convars to minimum/maximum values to help prevent competitive advantages. 125 | sv_pausable 1 // Is the server pausable. 126 | sv_pure 1 127 | sv_pure_kick_clients 1 // If set to 1, the server will kick clients with mismatching files. Otherwise, it will issue a warning to the client. 128 | sv_pure_trace 0 // If set to 1, the server will print a message whenever a client is verifying a CR 129 | sv_spawn_afk_bomb_drop_time 30 // Players that spawn and don't move for longer than sv_spawn_afk_bomb_drop_time (default 15 seconds) will automatically drop the bomb. 130 | sv_steamgroup_exclusive 0 // If set, only members of Steam group will be able to join the server when it's empty, public people will be able to join the server only if it has players. 131 | sv_voiceenable 0 132 | 133 | say "> ESL CS:GO 2v2 Ladder Config loaded - 14.01.2016 <" 134 | 135 | // Change map 136 | changelevel de_shortdust 137 | -------------------------------------------------------------------------------- /_scripts/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const ManifestPlugin = require('webpack-manifest-plugin'); 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 6 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 7 | const SizePlugin = require('size-plugin'); 8 | 9 | const projectRoot = path.join(__dirname, '../..'); 10 | const buildDirectory = path.join(projectRoot, 'frontend'); 11 | const distDirectory = path.join(projectRoot, 'public/dist'); 12 | 13 | const prod = process.env.NODE_ENV === "production"; 14 | 15 | const config = { 16 | entry: { 17 | main: [ 18 | path.join(buildDirectory, 'main.js'), 19 | path.join(buildDirectory, 'scss/style.scss') 20 | ] 21 | }, 22 | devtool: !prod ? 'inline-source-map' : '', 23 | output: { 24 | path: distDirectory, 25 | filename: '[name].[hash:6].js' 26 | }, 27 | module: { 28 | rules: [ 29 | { 30 | enforce: "pre", 31 | test: /\.js$/, 32 | exclude: /node_modules/, 33 | loader: 'eslint-loader', 34 | options: { 35 | failOnError: true, 36 | failOnWarning: false 37 | } 38 | }, 39 | { 40 | test: /\.js$/, 41 | exclude: /node_modules/, 42 | use: { 43 | loader: 'babel-loader', 44 | query: { 45 | presets: [ 46 | require.resolve('babel-preset-env'), 47 | require.resolve('babel-preset-react') 48 | ], 49 | plugins: [ 50 | [require.resolve('babel-plugin-transform-react-jsx'), {pragma: 'h'}] 51 | ] 52 | } 53 | } 54 | }, 55 | { 56 | test: /\.scss$/, 57 | use: [ 58 | MiniCssExtractPlugin.loader, 59 | {loader: 'css-loader', options: {minimize: prod, url: false, sourceMap: !prod}}, 60 | {loader: 'sass-loader', options: {sourceMap: !prod}} 61 | ] 62 | } 63 | ] 64 | }, 65 | plugins: [ 66 | new MiniCssExtractPlugin({ 67 | filename: '[name].[hash:6].css' 68 | }), 69 | new ManifestPlugin({ 70 | fileName: 'rev-manifest.json' 71 | }), 72 | new webpack.DefinePlugin({ 73 | 'process.env': { 74 | 'NODE_ENV': !prod ? JSON.stringify("development") : JSON.stringify("production") 75 | } 76 | }), 77 | new SizePlugin() 78 | ] 79 | }; 80 | 81 | if(prod) { 82 | config.plugins.push( 83 | new UglifyJsPlugin({ 84 | uglifyOptions: { 85 | mangle: true, 86 | compress: { 87 | negate_iife: false, 88 | unsafe_comps: true, 89 | properties: true, 90 | keep_fargs: false, 91 | pure_getters: true, 92 | collapse_vars: true, 93 | unsafe: true, 94 | warnings: false, 95 | sequences: true, 96 | dead_code: true, 97 | drop_debugger: true, 98 | comparisons: true, 99 | conditionals: true, 100 | evaluate: true, 101 | booleans: true, 102 | loops: true, 103 | unused: true, 104 | hoist_funs: true, 105 | if_return: true, 106 | join_vars: true, 107 | drop_console: true, 108 | pure_funcs: ['classCallCheck', 'invariant', 'warning'] 109 | } 110 | } 111 | }), 112 | new OptimizeCssAssetsPlugin({ 113 | assetNameRegExp: /\.css$/g, 114 | cssProcessor: require('cssnano'), 115 | cssProcessorOptions: { 116 | discardComments: {removeAll: true} 117 | } 118 | }) 119 | ); 120 | } 121 | 122 | module.exports = config; 123 | -------------------------------------------------------------------------------- /app/src/bundle.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = "production"; 2 | require(__dirname + '/server.js'); 3 | -------------------------------------------------------------------------------- /app/src/config/baseConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if we are using the dev version 3 | */ 4 | const dev = process.env.NODE_ENV !== 'production'; 5 | 6 | /** 7 | * Exports the base config 8 | */ 9 | module.exports = { 10 | application: { 11 | name: "CSGO Remote", 12 | env: dev ? " (local)" : "", 13 | basePath: "/", 14 | csgoConfigFolder: "./csgo-configs", 15 | host: "0.0.0.0", 16 | port: 3542 17 | }, 18 | logger: { 19 | location: "./log", 20 | level: "info" 21 | }, 22 | maps: { 23 | competitive: [ 24 | "de_inferno", 25 | "de_train", 26 | "de_mirage", 27 | "de_nuke", 28 | "de_overpass", 29 | "de_cache", 30 | "de_dust2" 31 | ], 32 | wingman: [ 33 | "de_cbble", 34 | "de_inferno", 35 | "de_lake", 36 | "de_overpass", 37 | "gd_rialto", 38 | "de_shortdust", 39 | "de_shortnuke", 40 | "de_train" 41 | ], 42 | dangerzone: [ 43 | "dz_blacksite" 44 | ] 45 | }, 46 | integrations: { 47 | challonge: { 48 | enabled: false, 49 | username: "challonge username", 50 | key: "api key", 51 | default_country: "NL" 52 | }, 53 | csv: { 54 | default_country: "NL" 55 | } 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /app/src/config/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import base config 3 | */ 4 | const baseConfig = require("./baseConfig"); 5 | 6 | /** 7 | * Import base packages 8 | */ 9 | const fs = require('fs'); 10 | const deepmerge = require('deepmerge'); 11 | 12 | /** 13 | * Check if we are using the dev version 14 | */ 15 | const dev = process.env.NODE_ENV !== 'production'; 16 | 17 | /** 18 | * Export the main config 19 | */ 20 | try { 21 | module.exports = deepmerge(baseConfig, eval('require')(dev ? __dirname + '/config.json' : process.cwd() + '/config.json')); 22 | } catch (e) { 23 | const config = fs.readFileSync(__dirname + '/../../../_scripts/config/config.build.json', 'utf8'); 24 | fs.writeFileSync(dev ? __dirname + '/config.json' : process.cwd() + '/config.json', config); 25 | 26 | module.exports = deepmerge(baseConfig, JSON.parse(config)); 27 | } 28 | -------------------------------------------------------------------------------- /app/src/config/version/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import base packages 3 | */ 4 | //eslint-disable-next-line 5 | const fs = require('fs'); 6 | 7 | /** 8 | * Export the main config 9 | */ 10 | try { 11 | module.exports = eval('fs.readFileSync')(__dirname + '/version.txt', 'utf8').split("\n")[0]; 12 | } catch (e) { 13 | module.exports = "__DEV__"; 14 | } 15 | -------------------------------------------------------------------------------- /app/src/controllers/Api/BaseController.js: -------------------------------------------------------------------------------- 1 | class BaseController { 2 | constructor() { 3 | 4 | } 5 | 6 | /** 7 | * Send a response to express 8 | * 9 | * @param response 10 | * @param status 11 | * @param data 12 | * @param contentType 13 | */ 14 | jsonResponse(response, status, data, contentType = 'application/json') { 15 | response.type(contentType); 16 | response.status(status); 17 | response.json(data); 18 | } 19 | } 20 | 21 | module.exports = BaseController; 22 | -------------------------------------------------------------------------------- /app/src/controllers/Api/CsgoController.js: -------------------------------------------------------------------------------- 1 | const baseController = require('./BaseController'); 2 | const log = require('../../modules/logger'); 3 | const db = require('../../modules/database').db; 4 | const socket = require('../../modules/socket'); 5 | const rcon = require('../../modules/rcon'); 6 | const {findIndexById, checkServerAvailability} = require('../../utils/Arrays'); 7 | 8 | class CsgoController extends baseController { 9 | constructor() { 10 | super(); 11 | } 12 | 13 | /** 14 | * Action for the default api route 15 | * 16 | * @param req 17 | * @param res 18 | */ 19 | indexAction(req, res) { 20 | const match = checkServerAvailability(`${req.params.ip}:${req.params.port}`, db.getData("/match")); 21 | const index = findIndexById(db.getData("/match"), match.id); 22 | 23 | if(match && index) { 24 | if(typeof match.server_data.locked === "undefined" || match.server_data.locked === false) { 25 | log.info(`[API][${req.params.ip}:${req.params.port}] Server send live score update`); 26 | console.log('JSON.stringify(req.body)', JSON.stringify(req.body)); 27 | 28 | // Update general match info on round_update 29 | if(req.body.instruction === "round_update") { 30 | const serverData = {}; 31 | serverData.status = req.body.data.status; 32 | serverData.locked = req.body.data.locked; 33 | serverData.match = req.body.data.match; 34 | serverData.killFeed = []; 35 | serverData.CT = { 36 | team_name: !req.body.data.half_time ? req.body.data.ct_name : req.body.data.t_name, 37 | players: [] 38 | }; 39 | serverData.T = { 40 | team_name: !req.body.data.half_time ? req.body.data.t_name : req.body.data.ct_name, 41 | players: [] 42 | }; 43 | 44 | for (let player = 0; player < req.body.data.players.length; player++) { 45 | const playerData = req.body.data.players[player]; 46 | 47 | if (playerData.team === 3) { 48 | serverData.CT.players.push(playerData); 49 | } 50 | 51 | if (playerData.team === 2) { 52 | serverData.T.players.push(playerData); 53 | } 54 | } 55 | 56 | db.push(`/match[${index}]/server_data`, serverData); 57 | socket.sendGeneralUpdate(); 58 | } 59 | 60 | // Update kill feed based on player_killed 61 | if(req.body.instruction === "player_killed") { 62 | //todo implement 63 | console.log('Waiting here!'); 64 | } 65 | 66 | // Auto start warmup after knife round 67 | if(req.body.instruction === "round_update") { 68 | if (match.status === 2 && (req.body.data.match.CT === 1 || req.body.data.match.T === 1)) { 69 | log.info(`[API][${req.params.ip}:${req.params.port}] Pausing match since knife round is over!`); 70 | db.push(`/match[${index}]/status`, 20); 71 | rcon.cmd(`${req.params.ip}:${req.params.port}`, "say 'Waiting for admin to start match...'", 'Auto pause match say'); 72 | rcon.cmd(`${req.params.ip}:${req.params.port}`, 'exec gamemode_competitive', 'Gamemode update'); 73 | rcon.startWarmup(`${req.params.ip}:${req.params.port}`); 74 | } 75 | } 76 | 77 | // Auto restore server after the match is complete 78 | if(req.body.instruction === "round_update") { 79 | if (match.status === 3 && req.body.data.status === "match_end") { 80 | log.info(`[API][${req.params.ip}:${req.params.port}] Restoring server since match is over!`); 81 | db.push(`/match[${index}]/status`, 99); 82 | rcon.reset(`${req.params.ip}:${req.params.port}`); 83 | } 84 | } 85 | } 86 | } 87 | 88 | this.jsonResponse(res, 200, { 'message': 'OK' }); 89 | } 90 | } 91 | 92 | module.exports = new CsgoController(); 93 | -------------------------------------------------------------------------------- /app/src/controllers/Api/IndexController.js: -------------------------------------------------------------------------------- 1 | const baseController = require('./BaseController'); 2 | 3 | class IndexController extends baseController { 4 | constructor() { 5 | super(); 6 | } 7 | 8 | /** 9 | * Action for the default api route 10 | * 11 | * @param req 12 | * @param res 13 | */ 14 | indexAction(req, res) { 15 | this.jsonResponse(res, 200, { 'message': 'Default API route!' }); 16 | } 17 | } 18 | 19 | module.exports = new IndexController(); 20 | -------------------------------------------------------------------------------- /app/src/controllers/Web/BaseController.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const version = require("../../config/version"); 3 | const assets = require("../../modules/assets"); 4 | 5 | class BaseController { 6 | constructor() { 7 | this.baseConfig = { 8 | config: config, 9 | protocol: '', 10 | hostname: '', 11 | baseUrl: '', 12 | appName: '', 13 | env: '', 14 | version: '', 15 | assets: { 16 | js: false, 17 | css: false 18 | } 19 | } 20 | } 21 | 22 | /** 23 | * Returns the complete config base + page specific 24 | * 25 | * @param request 26 | * @param pageSpecificConfig 27 | */ 28 | mergePageConfig(request, pageSpecificConfig) { 29 | const files = assets(); 30 | 31 | this.baseConfig.hostname = request.get('host'); 32 | this.baseConfig.protocol = request.protocol; 33 | this.baseConfig.baseUrl = `${request.protocol}://${request.get('host')}${config.application.basePath}`; 34 | this.baseConfig.appName = config.application.name; 35 | this.baseConfig.env = config.application.env; 36 | this.baseConfig.version = version; 37 | 38 | this.baseConfig.assets.js = files["main.js"]; 39 | this.baseConfig.assets.css = files["main.css"]; 40 | 41 | return Object.assign(this.baseConfig, pageSpecificConfig); 42 | } 43 | } 44 | 45 | module.exports = BaseController; 46 | -------------------------------------------------------------------------------- /app/src/controllers/Web/IndexController.js: -------------------------------------------------------------------------------- 1 | const baseController = require('./BaseController'); 2 | 3 | class IndexController extends baseController { 4 | /** 5 | * Renders the Home page 6 | * 7 | * @param req 8 | * @param res 9 | */ 10 | indexAction(req, res) { 11 | res.render('index', this.mergePageConfig(req, { 12 | template: 'index/index', 13 | pageTitle: 'Home' 14 | })); 15 | } 16 | 17 | /** 18 | * Renders the 404 page 19 | * 20 | * @param req 21 | * @param res 22 | */ 23 | notFoundAction(req, res) { 24 | res.render('index', this.mergePageConfig(req, { 25 | template: 'general/notfound', 26 | pageTitle: 'Not Found' 27 | })); 28 | } 29 | } 30 | 31 | module.exports = new IndexController(); 32 | -------------------------------------------------------------------------------- /app/src/log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenndehaan/csgo-rcon-nodejs/8a4529da6041bc44b9935a0fa5e25e4c4d42d61e/app/src/log/.gitkeep -------------------------------------------------------------------------------- /app/src/modules/assets.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = `${__dirname}/../../../public/dist`; 3 | 4 | /** 5 | * Function to get the active asset files for the frontend 6 | * 7 | * @return {any} 8 | */ 9 | module.exports = () => { 10 | return JSON.parse(fs.existsSync(path) ? fs.readFileSync(`${path}/rev-manifest.json`) : "{}"); 11 | }; 12 | -------------------------------------------------------------------------------- /app/src/modules/challonge.js: -------------------------------------------------------------------------------- 1 | const uuidv4 = require('uuid/v4'); 2 | const fetch = require("node-fetch"); 3 | const log = require("../modules/logger"); 4 | const db = require("../modules/database").db; 5 | const {findByChallonge, getAllChallonge} = require("../utils/Arrays"); 6 | const {findServerConfig, findServerConfigIndex} = require("../utils/Strings"); 7 | const config = require("../config"); 8 | 9 | class challonge { 10 | constructor() { 11 | this.tournaments = []; 12 | } 13 | 14 | /** 15 | * Get all tournaments from Challonge 16 | */ 17 | init() { 18 | fetch(`https://${config.integrations.challonge.username}:${config.integrations.challonge.key}@api.challonge.com/v1/tournaments.json`) 19 | .then(res => res.json()) 20 | .then(body => { 21 | if (body.length > 0) { 22 | this.tournaments = body; 23 | } else { 24 | this.tournaments = []; 25 | } 26 | 27 | log.info(`[CHALLONGE] ${this.tournaments.length} Tournament(s) found!`); 28 | }) 29 | .catch((error) => { 30 | log.error(`[CHALLONGE] Error: ${error}`); 31 | }); 32 | } 33 | 34 | /** 35 | * Import all matches from a tournament into the DB 36 | * 37 | * @param tournamentId 38 | * @param server 39 | * @param maxGames 40 | * @param gameMode 41 | * @param knifeConfig 42 | * @param mainConfig 43 | * @param matchGroup 44 | * @param callback 45 | */ 46 | importMatches(tournamentId, server, maxGames, gameMode, knifeConfig, mainConfig, matchGroup, callback) { 47 | fetch(`https://${config.integrations.challonge.username}:${config.integrations.challonge.key}@api.challonge.com/v1/tournaments/${tournamentId}/matches.json`) 48 | .then(res => res.json()) 49 | .then(body => { 50 | const dbData = db.getData("/match"); 51 | let imported = 0; 52 | let completed = 0; 53 | 54 | for (let item = 0; item < body.length; item++) { 55 | const exists = findByChallonge(dbData, body[item].match.id); 56 | const matchId = body[item].match.id; 57 | const teamId1 = body[item].match.player1_id; 58 | const teamId2 = body[item].match.player2_id; 59 | 60 | if (!exists) { 61 | if(teamId1 !== null && teamId2 !== null) { 62 | imported++; 63 | 64 | this.getTeamName(tournamentId, teamId1, (teamName1) => { 65 | this.getTeamName(tournamentId, teamId2, (teamName2) => { 66 | let serverDetails = false; 67 | 68 | if(server !== "next") { 69 | serverDetails = findServerConfig(server); 70 | } else { 71 | const dbMatches = getAllChallonge(); 72 | 73 | if(dbMatches.length > 0) { 74 | const server = findServerConfigIndex(dbMatches[dbMatches.length - 1].server); 75 | let serverIndex = 0; 76 | 77 | if((server + 1) < config.servers.length) { 78 | serverIndex = server + 1; 79 | } 80 | 81 | serverDetails = findServerConfig(`${config.servers[serverIndex].ip}:${config.servers[serverIndex].port}`); 82 | } else { 83 | serverDetails = findServerConfig(`${config.servers[0].ip}:${config.servers[0].port}`); 84 | } 85 | } 86 | 87 | db.push("/match[]", { 88 | id: uuidv4(), 89 | team1: { 90 | name: teamName1, 91 | country: config.integrations.challonge.default_country 92 | }, 93 | team2: { 94 | name: teamName2, 95 | country: config.integrations.challonge.default_country 96 | }, 97 | match_group: matchGroup, 98 | map: serverDetails.default_map, 99 | max_games: parseInt(maxGames), 100 | game_mode: gameMode, 101 | knife_config: knifeConfig, 102 | match_config: mainConfig, 103 | server: `${serverDetails.ip}:${serverDetails.port}`, 104 | status: 0, 105 | challonge: matchId, 106 | server_data: false 107 | }); 108 | 109 | completed++; 110 | 111 | if(imported === completed) { 112 | callback(imported); 113 | log.info(`[CHALLONGE] ${body.length} Matches(s) found! ${imported} Matches(s) imported!`); 114 | } 115 | }); 116 | }); 117 | } 118 | } 119 | } 120 | 121 | if(imported === 0) { 122 | callback(imported); 123 | log.info(`[CHALLONGE] No new matches(s) found!`); 124 | } 125 | }) 126 | .catch((error) => { 127 | log.error(`[CHALLONGE] Error: ${error}`); 128 | }); 129 | } 130 | 131 | /** 132 | * Returns the team name 133 | * 134 | * @param tournamentId 135 | * @param teamId 136 | * @param callback 137 | */ 138 | getTeamName(tournamentId, teamId, callback) { 139 | fetch(`https://${config.integrations.challonge.username}:${config.integrations.challonge.key}@api.challonge.com/v1/tournaments/${tournamentId}/participants/${teamId}.json`) 140 | .then(res => res.json()) 141 | .then(body => { 142 | callback(body.participant.name); 143 | }) 144 | .catch((error) => { 145 | log.error(`[CHALLONGE] Error: ${error}`); 146 | callback(false); 147 | }); 148 | } 149 | } 150 | 151 | module.exports = new challonge(); 152 | -------------------------------------------------------------------------------- /app/src/modules/csgoConfig.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const log = require("./logger"); 3 | const config = require("../config"); 4 | 5 | class csgoConfig { 6 | /** 7 | * Constructor 8 | */ 9 | constructor() { 10 | this.dev = process.env.NODE_ENV !== 'production'; 11 | } 12 | 13 | /** 14 | * Writes some default csgo configs to the filesystem 15 | */ 16 | init() { 17 | if (!fs.existsSync(`${process.cwd()}/${config.application.csgoConfigFolder}`)) { 18 | fs.mkdirSync(`${process.cwd()}/${config.application.csgoConfigFolder}`); 19 | fs.mkdirSync(`${process.cwd()}/${config.application.csgoConfigFolder}/main`); 20 | fs.mkdirSync(`${process.cwd()}/${config.application.csgoConfigFolder}/knife`); 21 | 22 | const main_files = fs.readdirSync(`${__dirname}/../../../_scripts/csgo-configs/main`); 23 | const knife_files = fs.readdirSync(`${__dirname}/../../../_scripts/csgo-configs/knife`); 24 | 25 | for (let main = 0; main < main_files.length; main++) { 26 | log.info(`[CSGO-CONFIG] Copy file: ${__dirname}/../../../_scripts/csgo-configs/main -> ${process.cwd()}/${config.application.csgoConfigFolder}/main/${main_files[main]} `); 27 | 28 | fs.writeFileSync(`${process.cwd()}/${config.application.csgoConfigFolder}/main/${main_files[main]}`, fs.readFileSync(`${__dirname}/../../../_scripts/csgo-configs/main/${main_files[main]}`, 'utf8')); 29 | } 30 | 31 | for (let knife = 0; knife < knife_files.length; knife++) { 32 | log.info(`[CSGO-CONFIG] Copy file: ${__dirname}/../../../_scripts/csgo-configs/knife/${knife_files[knife]} -> ${process.cwd()}/${config.application.csgoConfigFolder}/knife/${knife_files[knife]}`); 33 | 34 | fs.writeFileSync(`${process.cwd()}/${config.application.csgoConfigFolder}/knife/${knife_files[knife]}`, fs.readFileSync(`${__dirname}/../../../_scripts/csgo-configs/knife/${knife_files[knife]}`, 'utf8')); 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * Grabs CSGO configs from file system 41 | * 42 | * @param callback 43 | */ 44 | index(callback) { 45 | fs.readdir(`${this.dev ? __dirname + '/..' : process.cwd()}/${config.application.csgoConfigFolder}/main`, (err, main_files) => { 46 | if (err) { 47 | throw err; 48 | } 49 | 50 | for(let item = 0; item < main_files.length; item++) { 51 | main_files[item] = main_files[item].replace(/\.[^/.]+$/, ""); 52 | } 53 | 54 | fs.readdir(`${this.dev ? __dirname + '/..' : process.cwd()}/${config.application.csgoConfigFolder}/knife`, (err, knife_files) => { 55 | if (err) { 56 | throw err; 57 | } 58 | 59 | for(let item = 0; item < knife_files.length; item++) { 60 | knife_files[item] = knife_files[item].replace(/\.[^/.]+$/, ""); 61 | } 62 | 63 | const files = { 64 | main: main_files, 65 | knife: knife_files 66 | }; 67 | 68 | callback(files); 69 | }); 70 | }); 71 | } 72 | 73 | /** 74 | * Grabs CSGO configs from file system 75 | * 76 | * @param config_name 77 | * @param type 78 | * @param callback 79 | */ 80 | load(config_name, type = "main", callback) { 81 | fs.readFile(`${this.dev ? __dirname + '/..' : process.cwd()}/${config.application.csgoConfigFolder}/${type}/${config_name}.txt`, 'utf8', (err, data) => { 82 | if (err) { 83 | throw err; 84 | } 85 | 86 | callback(this.process(data)); 87 | }); 88 | } 89 | 90 | /** 91 | * Removes all comments and other unneeded stuff 92 | * 93 | * @param data 94 | * @return array 95 | */ 96 | process(data) { 97 | data = data.replace(/^\/\/.*$/m, ''); 98 | data = data.split("\n"); 99 | const newData = []; 100 | for (let i = 0; i < data.length; i += 1) { 101 | const line = data[i].trim(); 102 | const segments = line.split(' '); 103 | 104 | if(segments[0] === 'say') { 105 | newData.push(line); 106 | } else if (segments[0] !== '' && segments[0] !== '//') { 107 | newData.push(`${segments[0]} ${segments[1].split('\t')[0]}`); 108 | } 109 | } 110 | 111 | return newData; 112 | } 113 | } 114 | 115 | module.exports = new csgoConfig(); 116 | -------------------------------------------------------------------------------- /app/src/modules/database.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import base packages 3 | */ 4 | const JsonDB = require('node-json-db'); 5 | const log = require("./logger"); 6 | 7 | class database { 8 | /** 9 | * Constructor 10 | */ 11 | constructor() { 12 | this.db = new JsonDB('csgo-rcon', true, false); 13 | } 14 | 15 | /** 16 | * Initial function 17 | */ 18 | init() { 19 | /** 20 | * Init the DB object if we launch the app for the first time 21 | */ 22 | if(Object.keys(this.db.getData("/")).length === 0 && this.db.getData("/").constructor === Object){ 23 | this.db.push("/match", []); 24 | this.db.push("/group", []); 25 | 26 | log.info("[DATABASE] Initialize database for the first time!"); 27 | } 28 | 29 | log.info("[DATABASE] Ready!"); 30 | } 31 | } 32 | 33 | module.exports = new database(); 34 | -------------------------------------------------------------------------------- /app/src/modules/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import base packages 3 | */ 4 | const fs = require('fs'); 5 | const config = require("../config"); 6 | 7 | /** 8 | * Check if we are using the dev version 9 | */ 10 | const dev = process.env.NODE_ENV !== 'production'; 11 | 12 | /** 13 | * Create callback storage for socket 14 | */ 15 | const callbacks = []; 16 | 17 | /** 18 | * Create log dir if it doesn't exists 19 | */ 20 | if (!fs.existsSync(`${dev ? __dirname + '/../' : process.cwd() + '/'}${config.logger.location}`)){ 21 | fs.mkdirSync(`${dev ? __dirname + '/../' : process.cwd() + '/'}${config.logger.location}`); 22 | } 23 | 24 | /** 25 | * Setup logger 26 | */ 27 | const log = require('simple-node-logger').createSimpleLogger({ 28 | logFilePath: `${dev ? __dirname + '/../' : process.cwd() + '/'}${config.logger.location}/server.log`, 29 | timestampFormat: 'YYYY-MM-DD HH:mm:ss.SSS' 30 | }); 31 | 32 | /** 33 | * Set log level from config 34 | */ 35 | log.setLevel(config.logger.level); 36 | 37 | /** 38 | * Fix zero prefixing 39 | * 40 | * @param number 41 | * @return {*} 42 | */ 43 | const fixTimeDateCalculation = (number) => { 44 | if (number <= 9) { 45 | return `0${number}`; 46 | } 47 | 48 | return number; 49 | }; 50 | 51 | /** 52 | * Fix zero prefixing 53 | * 54 | * @param number 55 | * @return {*} 56 | */ 57 | const fixMilisecondsCalculation = (number) => { 58 | if (number <= 9) { 59 | return `00${number}`; 60 | } 61 | 62 | if (number <= 99) { 63 | return `0${number}`; 64 | } 65 | 66 | return number; 67 | }; 68 | 69 | /** 70 | * Return the current time/date string 71 | * 72 | * @return {string} 73 | */ 74 | const currentDateTime = () => { 75 | const current = new Date(); 76 | const date = `${current.getFullYear()}-${fixTimeDateCalculation(current.getMonth() + 1)}-${fixTimeDateCalculation(current.getDate())}`; 77 | const time = `${fixTimeDateCalculation(current.getHours())}:${fixTimeDateCalculation(current.getMinutes())}:${fixTimeDateCalculation(current.getSeconds())}.${fixMilisecondsCalculation(current.getMilliseconds())}`; 78 | 79 | return `${date} ${time}`; 80 | }; 81 | 82 | /** 83 | * Add a callback to the callback storage 84 | * 85 | * @param callback 86 | */ 87 | const addCallback = (callback) => { 88 | callbacks.push(callback); 89 | }; 90 | 91 | /** 92 | * Trace provider 93 | */ 94 | const trace = (message) => { 95 | log.trace(message); 96 | 97 | for(let item = 0; item < callbacks.length; item++) { 98 | callbacks[item](`${currentDateTime()} TRACE ${message}`); 99 | } 100 | }; 101 | 102 | /** 103 | * Debug provider 104 | */ 105 | const debug = (message) => { 106 | log.debug(message); 107 | 108 | for(let item = 0; item < callbacks.length; item++) { 109 | callbacks[item](`${currentDateTime()} DEBUG ${message}`); 110 | } 111 | }; 112 | 113 | /** 114 | * Debug provider 115 | */ 116 | const info = (message) => { 117 | log.info(message); 118 | 119 | for(let item = 0; item < callbacks.length; item++) { 120 | callbacks[item](`${currentDateTime()} INFO ${message}`); 121 | } 122 | }; 123 | 124 | /** 125 | * Warn provider 126 | */ 127 | const warn = (message) => { 128 | log.warn(message); 129 | 130 | for(let item = 0; item < callbacks.length; item++) { 131 | callbacks[item](`${currentDateTime()} WARN ${message}`); 132 | } 133 | }; 134 | 135 | /** 136 | * Error provider 137 | */ 138 | const error = (message) => { 139 | log.error(message); 140 | 141 | for(let item = 0; item < callbacks.length; item++) { 142 | callbacks[item](`${currentDateTime()} ERROR ${message}`); 143 | } 144 | }; 145 | 146 | /** 147 | * Fatal provider 148 | */ 149 | const fatal = (message) => { 150 | log.fatal(message); 151 | 152 | for(let item = 0; item < callbacks.length; item++) { 153 | callbacks[item](`${currentDateTime()} FATAL ${message}`); 154 | } 155 | }; 156 | 157 | module.exports = { 158 | trace, 159 | debug, 160 | info, 161 | warn, 162 | error, 163 | fatal, 164 | addCallback 165 | }; 166 | -------------------------------------------------------------------------------- /app/src/modules/queue.js: -------------------------------------------------------------------------------- 1 | const log = require("./logger"); 2 | const config = require("../config"); 3 | 4 | class queue { 5 | /** 6 | * Constructor 7 | */ 8 | constructor() { 9 | this.queueFailMax = 35; 10 | this.activeQueue = {}; 11 | this.commandRunning = {}; 12 | this.queueFailCurrent = {}; 13 | 14 | this.init(); 15 | } 16 | 17 | /** 18 | * Init function to add the servers to the global objects 19 | */ 20 | init() { 21 | for (let item = 0; item < config.servers.length; item++) { 22 | this.activeQueue[`${config.servers[item].ip}:${config.servers[item].port}`] = []; 23 | this.commandRunning[`${config.servers[item].ip}:${config.servers[item].port}`] = false; 24 | this.queueFailCurrent[`${config.servers[item].ip}:${config.servers[item].port}`] = 0; 25 | } 26 | 27 | /** 28 | * Loop over the commands and execute them when possible 29 | */ 30 | setInterval(() => { 31 | for (let item = 0; item < config.servers.length; item++) { 32 | if (this.activeQueue[`${config.servers[item].ip}:${config.servers[item].port}`].length > 0) { 33 | if (!this.commandRunning[`${config.servers[item].ip}:${config.servers[item].port}`] || this.queueFailCurrent[`${config.servers[item].ip}:${config.servers[item].port}`] === this.queueFailMax) { 34 | this.commandRunning[`${config.servers[item].ip}:${config.servers[item].port}`] = true; 35 | 36 | this.activeQueue[`${config.servers[item].ip}:${config.servers[item].port}`][0](); 37 | this.activeQueue[`${config.servers[item].ip}:${config.servers[item].port}`].splice(0, 1); 38 | 39 | if (this.queueFailCurrent[`${config.servers[item].ip}:${config.servers[item].port}`] === this.queueFailMax) { 40 | this.queueFailCurrent[`${config.servers[item].ip}:${config.servers[item].port}`] = 0; 41 | this.complete(`${config.servers[item].ip}:${config.servers[item].port}`); 42 | 43 | log.warn(`[QUEUE][${config.servers[item].ip}:${config.servers[item].port}] Max Queue Fail reached! Cleaning up...`); 44 | } 45 | } else { 46 | this.queueFailCurrent[`${config.servers[item].ip}:${config.servers[item].port}`]++; 47 | } 48 | } 49 | } 50 | }, 10); 51 | } 52 | 53 | /** 54 | * Function to add a command to the queue 55 | * 56 | * @param server string 57 | * @param command Function 58 | */ 59 | add(server, command) { 60 | this.activeQueue[server].push(command); 61 | } 62 | 63 | /** 64 | * Function that must be run when a command is done 65 | * 66 | * @param server 67 | */ 68 | complete(server) { 69 | this.commandRunning[server] = false; 70 | } 71 | } 72 | 73 | module.exports = new queue(); 74 | -------------------------------------------------------------------------------- /app/src/modules/router.js: -------------------------------------------------------------------------------- 1 | const config = require("../config"); 2 | 3 | class router { 4 | /** 5 | * An easy to use function to add multiple routes to the Express router 6 | * 7 | * @param router 8 | * @param routes 9 | * @param type 10 | */ 11 | routesToRouter(router, routes, type) { 12 | for (let item = 0; item < routes.length; item += 1) { 13 | const route = routes[item]; 14 | const controller = route.controller.charAt(0).toUpperCase() + route.controller.slice(1); 15 | let auth = ''; 16 | if (route.secured) { 17 | auth = `basicAuth({users:{${config.authentication.username}:'${config.authentication.password}'},challenge: true}),`; 18 | } 19 | 20 | eval( 21 | ` 22 | ${route.secured ? 'const basicAuth = require("express-basic-auth");' : ''} 23 | const ${route.controller}Controller = require('../controllers/${type}/${controller}Controller'); 24 | router.${route.method}('${route.route}', ${auth} (req, res) => { 25 | ${route.controller}Controller.${route.action}Action(req, res); 26 | }); 27 | ` 28 | ); 29 | } 30 | } 31 | } 32 | 33 | module.exports = new router(); 34 | -------------------------------------------------------------------------------- /app/src/modules/web.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import base packages 3 | */ 4 | const express = require('express'); 5 | const app = express(); 6 | const bodyParser = require('body-parser'); 7 | const compression = require('compression'); 8 | 9 | /** 10 | * Import own packages 11 | */ 12 | const log = require("./logger"); 13 | const config = require("../config"); 14 | const socket = require("./socket"); 15 | const webRouter = require('../routers/Web'); 16 | const apiRouter = require('../routers/Api'); 17 | const indexController = require('../controllers/Web/IndexController'); 18 | 19 | class web { 20 | /** 21 | * Init the express app 22 | */ 23 | init() { 24 | /** 25 | * Trust proxy 26 | */ 27 | app.enable('trust proxy'); 28 | 29 | /** 30 | * Set template engine 31 | */ 32 | app.set('view engine', 'ejs'); 33 | app.set('views', `${__dirname}/../views`); 34 | 35 | /** 36 | * Setup socket 37 | */ 38 | socket.init(app); 39 | 40 | /** 41 | * Enable compression 42 | */ 43 | app.use(compression({ threshold: 0 })); 44 | 45 | /** 46 | * Serve static public dir 47 | */ 48 | app.use(express.static(`${__dirname}/../../../public`)); 49 | 50 | /** 51 | * Configure app to use bodyParser() 52 | */ 53 | app.use(bodyParser.urlencoded({extended: true})); 54 | app.use(bodyParser.json()); 55 | 56 | /** 57 | * Request logger 58 | */ 59 | app.use((req, res, next) => { 60 | log.trace(`[WEB][REQUEST]: ${req.originalUrl}`); 61 | next(); 62 | }); 63 | 64 | /** 65 | * Configure routers 66 | */ 67 | app.use('/', webRouter.router); 68 | app.use('/api', apiRouter.router); 69 | 70 | /** 71 | * Setup default 404 message 72 | */ 73 | app.use((req, res) => { 74 | res.status(404); 75 | 76 | // respond with json 77 | if (req.originalUrl.split('/')[1] === 'api') { 78 | 79 | /** 80 | * API 404 not found 81 | */ 82 | res.send({error: 'This API route is not implemented yet'}); 83 | return; 84 | } 85 | 86 | indexController.notFoundAction(req, res); 87 | }); 88 | 89 | /** 90 | * Disable powered by header for security reasons 91 | */ 92 | app.disable('x-powered-by'); 93 | 94 | /** 95 | * Start listening on port 96 | */ 97 | app.listen(config.application.port, config.application.host, () => { 98 | log.info(`[WEB] App is running on: ${config.application.host}:${config.application.port}`); 99 | }); 100 | } 101 | } 102 | 103 | module.exports = new web(); 104 | -------------------------------------------------------------------------------- /app/src/routers/Api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import base packages 3 | */ 4 | const express = require('express'); 5 | const router = express.Router(); 6 | const routerUtils = require('../modules/router'); 7 | 8 | /** 9 | * Define routes 10 | */ 11 | const routes = [ 12 | { 13 | route: '/', 14 | method: 'get', 15 | controller: 'Index', 16 | action: 'index' 17 | }, 18 | { 19 | route: '/csgo/:ip/:port', 20 | method: 'post', 21 | controller: 'Csgo', 22 | action: 'index' 23 | } 24 | ]; 25 | 26 | routerUtils.routesToRouter(router, routes, 'Api'); 27 | 28 | module.exports = {router, routes}; 29 | -------------------------------------------------------------------------------- /app/src/routers/Web.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import base packages 3 | */ 4 | const express = require('express'); 5 | const router = express.Router(); 6 | const routerUtils = require('../modules/router'); 7 | 8 | /** 9 | * Define routes 10 | */ 11 | const routes = [ 12 | { 13 | route: '/', 14 | method: 'get', 15 | controller: 'Index', 16 | action: 'index' 17 | }, 18 | { 19 | route: '/servers', 20 | method: 'get', 21 | controller: 'Index', 22 | action: 'index' 23 | }, 24 | { 25 | route: '/match/create', 26 | method: 'get', 27 | controller: 'Index', 28 | action: 'index' 29 | }, 30 | { 31 | route: '/match/:id', 32 | method: 'get', 33 | controller: 'Index', 34 | action: 'index' 35 | }, 36 | { 37 | route: '/match/:id/edit', 38 | method: 'get', 39 | controller: 'Index', 40 | action: 'index' 41 | }, 42 | { 43 | route: '/settings', 44 | method: 'get', 45 | controller: 'Index', 46 | action: 'index', 47 | secured: true 48 | }, 49 | { 50 | route: '/settings/log', 51 | method: 'get', 52 | controller: 'Index', 53 | action: 'index', 54 | secured: true 55 | }, 56 | { 57 | route: '/about', 58 | method: 'get', 59 | controller: 'Index', 60 | action: 'index' 61 | } 62 | ]; 63 | 64 | routerUtils.routesToRouter(router, routes, 'Web'); 65 | 66 | module.exports = {router, routes}; 67 | -------------------------------------------------------------------------------- /app/src/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import external modules 3 | */ 4 | const fs = require("fs"); 5 | 6 | /** 7 | * Import own modules 8 | */ 9 | const version = require("./config/version"); 10 | const config = require("./config"); 11 | const log = require("./modules/logger"); 12 | const database = require("./modules/database"); 13 | const csgoConfig = require("./modules/csgoConfig"); 14 | const queue = require("./modules/queue"); 15 | const web = require("./modules/web"); 16 | const challonge = require("./modules/challonge"); 17 | 18 | /** 19 | * Hack since the srcds-rcon package isn't handling rejections 20 | */ 21 | process.on('unhandledRejection', () => {}); 22 | 23 | /** 24 | * Check if we are running as dev 25 | */ 26 | const dev = process.env.NODE_ENV !== 'production'; 27 | 28 | /** 29 | * Init modules 30 | */ 31 | if(!dev) { 32 | csgoConfig.init(); 33 | } 34 | database.init(); 35 | 36 | log.info("[SYSTEM] App running"); 37 | log.info(`[SYSTEM] Version: ${version}`); 38 | log.info(`[SYSTEM] Support and Help: https://github.com/glenndehaan/csgo-rcon-nodejs`); 39 | 40 | /** 41 | * Check if this is the first time running the app 42 | */ 43 | if(!dev) { 44 | if (!fs.existsSync(`${process.cwd()}/LICENCE`) || !fs.existsSync(`${process.cwd()}/README.md`)) { 45 | fs.writeFileSync(process.cwd() + '/LICENCE', fs.readFileSync(__dirname + '/../../LICENCE', 'utf8')); 46 | fs.writeFileSync(process.cwd() + '/README.md', fs.readFileSync(__dirname + '/../../README.md', 'utf8')); 47 | 48 | log.info("------------------------------------------"); 49 | log.info("Hi and thank you for using this piece of software!"); 50 | log.info("Go ahead and update the config.json to your needs then relaunch the software!"); 51 | log.info("The software will close in 5 seconds!"); 52 | log.info("------------------------------------------"); 53 | 54 | setTimeout(() => { 55 | process.exit(0); 56 | }, 5000) 57 | } else { 58 | queue.init(); 59 | web.init(); 60 | if(config.integrations.challonge.enabled) { 61 | challonge.init(); 62 | } 63 | } 64 | } else { 65 | queue.init(); 66 | web.init(); 67 | if(config.integrations.challonge.enabled) { 68 | challonge.init(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/utils/Arrays.js: -------------------------------------------------------------------------------- 1 | const database = require("../modules/database").db; 2 | 3 | /** 4 | * Find index by id in an array 5 | * 6 | * @param array 7 | * @param id 8 | * @return bool|object 9 | */ 10 | function findIndexById(array, id) { 11 | for(let item = 0; item < array.length; item++) { 12 | if(array[item].id === id) { 13 | return item; 14 | } 15 | } 16 | 17 | return false; 18 | } 19 | 20 | /** 21 | * Find the challonge param in an array 22 | * 23 | * @param array 24 | * @param challonge 25 | * @return bool|object 26 | */ 27 | function findByChallonge(array, challonge) { 28 | for(let item = 0; item < array.length; item++) { 29 | if(array[item].challonge === challonge) { 30 | return array[item]; 31 | } 32 | } 33 | 34 | return false; 35 | } 36 | 37 | /** 38 | * Get all matches from challonge that are stored in the DB 39 | * 40 | * @return {*} 41 | */ 42 | function getAllChallonge() { 43 | const matches = database.getData("/match"); 44 | const challongeMatches = []; 45 | 46 | for(let item = 0; item < matches.length; item++) { 47 | if(matches[item].challonge !== false) { 48 | challongeMatches.push(matches[item]); 49 | } 50 | } 51 | 52 | return challongeMatches; 53 | } 54 | 55 | /** 56 | * Checks if a server is in use by a match 57 | * 58 | * @param server 59 | * @param matches 60 | * @return {*} 61 | */ 62 | function checkServerAvailability(server, matches) { 63 | for(let item = 0; item < matches.length; item++) { 64 | const match = matches[item]; 65 | 66 | if(match.status > 0 && match.status < 99 && match.server === server) { 67 | return match; 68 | } 69 | } 70 | 71 | return false; 72 | } 73 | 74 | module.exports = {findIndexById, findByChallonge, getAllChallonge, checkServerAvailability}; 75 | -------------------------------------------------------------------------------- /app/src/utils/Strings.js: -------------------------------------------------------------------------------- 1 | const config = require("../config"); 2 | 3 | /** 4 | * Function to split the string by byte length 5 | * 6 | * @param data 7 | * @param length 8 | * @param lineEndChar 9 | * @return {*} 10 | */ 11 | function splitByByteLength(data, length = 1024, lineEndChar = '') { 12 | const lines = data; 13 | const exportedLines = []; 14 | let index = 0; 15 | 16 | for(let item = 0; item < lines.length; item++) { 17 | if(typeof exportedLines[index] === "undefined") { 18 | exportedLines[index] = ""; 19 | } 20 | 21 | const lineFormatted = `${lines[item]}${lineEndChar}`; 22 | const lineBytes = Buffer.byteLength(lineFormatted, 'utf8'); 23 | const bufferBytes = Buffer.byteLength(exportedLines[index], 'utf8'); 24 | 25 | if((bufferBytes + lineBytes) < length) { 26 | exportedLines[index] += lineFormatted; 27 | } else { 28 | index++; 29 | } 30 | } 31 | 32 | return exportedLines; 33 | } 34 | 35 | /** 36 | * Splits a string by line break and returns an array 37 | * 38 | * @param data 39 | * @return {*} 40 | */ 41 | function splitByLinkBreak(data) { 42 | return data.split('\n'); 43 | } 44 | 45 | /** 46 | * Function find the server config that belongs to the server name 47 | * 48 | * @param server 49 | * @return object 50 | */ 51 | function findServerConfig(server) { 52 | for(let item = 0; item < config.servers.length; item++) { 53 | const splitted = server.split(":"); 54 | 55 | if(config.servers[item].ip === splitted[0] && config.servers[item].port === parseInt(splitted[1])) { 56 | return config.servers[item]; 57 | } 58 | } 59 | 60 | return {}; 61 | } 62 | 63 | /** 64 | * Function find the server config that belongs to the server name 65 | * 66 | * @param server 67 | * @return int 68 | */ 69 | function findServerConfigIndex(server) { 70 | for(let item = 0; item < config.servers.length; item++) { 71 | const splitted = server.split(":"); 72 | 73 | if(config.servers[item].ip === splitted[0] && config.servers[item].port === parseInt(splitted[1])) { 74 | return item; 75 | } 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | module.exports = {splitByByteLength, splitByLinkBreak, findServerConfig, findServerConfigIndex}; 82 | -------------------------------------------------------------------------------- /app/src/views/general/notfound.ejs: -------------------------------------------------------------------------------- 1 |

404 Not Found !

2 | -------------------------------------------------------------------------------- /app/src/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include ./partials/head.ejs %> 5 | 6 | 7 | 30 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/views/index/index.ejs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenndehaan/csgo-rcon-nodejs/8a4529da6041bc44b9935a0fa5e25e4c4d42d61e/app/src/views/index/index.ejs -------------------------------------------------------------------------------- /app/src/views/partials/head.ejs: -------------------------------------------------------------------------------- 1 | <%= pageTitle %> | <%= config.application.name %> <%= config.application.env %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /app/test/functions.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Import test suite 3 | */ 4 | const should = require('should'); 5 | const request = require('request'); 6 | 7 | /** 8 | * Import packages needed for tests 9 | */ 10 | const log = require("../src/modules/logger"); 11 | const database = require("../src/modules/database"); 12 | const csgoConfig = require("../src/modules/csgoConfig"); 13 | const challonge = require("../src/modules/challonge"); 14 | 15 | /** 16 | * Launch test 17 | */ 18 | describe("APP - Functions", () => { 19 | it('./modules/logger should have a all logger functions', (done) => { 20 | log.trace.should.be.an.Function(); 21 | log.debug.should.be.an.Function(); 22 | log.info.should.be.an.Function(); 23 | log.warn.should.be.an.Function(); 24 | log.error.should.be.an.Function(); 25 | log.fatal.should.be.an.Function(); 26 | done(); 27 | }); 28 | 29 | it('./modules/database should have an init function', (done) => { 30 | database.init.should.be.a.Function(); 31 | done(); 32 | }); 33 | 34 | it('./modules/csgoConfig should have an init function', (done) => { 35 | csgoConfig.init.should.be.a.Function(); 36 | done(); 37 | }); 38 | 39 | it('./modules/challonge should have an init function', (done) => { 40 | challonge.init.should.be.a.Function(); 41 | done(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /frontend/components/About.js: -------------------------------------------------------------------------------- 1 | import {h, Component} from 'preact'; 2 | import fetch from 'unfetch'; 3 | import {connect} from "unistore/preact"; 4 | 5 | class About extends Component { 6 | /** 7 | * Constructor 8 | */ 9 | constructor() { 10 | super(); 11 | 12 | this.state = { 13 | currentCommit: window.expressConfig.version, 14 | latestCommit: false, 15 | dev: process.env.NODE_ENV !== 'production' 16 | }; 17 | } 18 | 19 | /** 20 | * Runs then component mounts 21 | */ 22 | componentDidMount() { 23 | this.updateGeneralPageData(); 24 | this.checkCurrentVersion(); 25 | } 26 | 27 | /** 28 | * Runs when component updates 29 | */ 30 | componentDidUpdate() { 31 | this.updateGeneralPageData(); 32 | } 33 | 34 | /** 35 | * Updates some general page data 36 | */ 37 | updateGeneralPageData() { 38 | document.title = `${this.props.lang.about.title} | ${window.expressConfig.appName} ${window.expressConfig.env}`; 39 | window.events.emit('breadcrumbs', [ 40 | { 41 | "name": this.props.lang.home.title, 42 | "url": "/" 43 | }, 44 | { 45 | "name": this.props.lang.about.title, 46 | "url": false 47 | } 48 | ]); 49 | } 50 | 51 | /** 52 | * Fetch the latest version from GitHub 53 | */ 54 | checkCurrentVersion() { 55 | fetch('https://api.github.com/repos/glenndehaan/csgo-rcon-nodejs/releases') 56 | .then(r => r.json()) 57 | .then(data => { 58 | if(data.length > 0) { 59 | if(data[0].tag_name) { 60 | this.setState({ 61 | latestCommit: data[0].tag_name 62 | }) 63 | } 64 | } 65 | }) 66 | } 67 | 68 | /** 69 | * Preact render function 70 | * 71 | * @returns {*} 72 | */ 73 | render() { 74 | return ( 75 |
76 |

{this.props.lang.about.subtitle}

77 | {this.state.dev &&
Warning: {this.props.lang.about.devWarning}
} 78 | {this.state.currentCommit !== this.state.latestCommit &&
Warning: {this.props.lang.about.oldWarning}
} 79 | {(this.state.currentCommit !== this.state.latestCommit || this.state.dev) &&
} 80 |
81 |

{this.props.lang.about.descriptionTitle}

82 | {this.props.lang.about.description}
83 | 84 |
85 | 86 |

{this.props.lang.about.version}

87 | {this.props.lang.about.currentVersion}: {this.state.dev ? '__DEV__' : this.state.currentCommit}
88 | {this.props.lang.about.latestVersion}: {!this.state.latestCommit ? 'Checking...' : this.state.latestCommit}
89 | 90 |
91 | 92 |

{this.props.lang.about.contributors}

93 | glenndehaan (GitHub)
94 | ChrisEKN (GitHub)
95 | 96 |
97 | 98 |

{this.props.lang.about.project}

99 | GitHub: https://github.com/glenndehaan/csgo-rcon-nodejs
100 | Star: