├── postman ├── .gitignore ├── README.md └── accservermanager.postman_environment.json ├── frontend ├── src │ ├── assets │ │ ├── .gitkeep │ │ └── graphics │ │ │ ├── acc-logo-header.png │ │ │ └── login-background.jpg │ ├── app │ │ ├── app.component.scss │ │ ├── components │ │ │ ├── event-list │ │ │ │ ├── event-list.component.scss │ │ │ │ ├── event-list.component.spec.ts │ │ │ │ ├── event-list.component.html │ │ │ │ └── event-list.component.ts │ │ │ ├── logout-page │ │ │ │ ├── logout-page.component.scss │ │ │ │ ├── logout-page.component.html │ │ │ │ ├── logout-page.component.ts │ │ │ │ └── logout-page.component.spec.ts │ │ │ ├── server-table │ │ │ │ ├── server-table.component.css │ │ │ │ ├── server-table.component.spec.ts │ │ │ │ └── server-table-datasource.ts │ │ │ ├── system-info │ │ │ │ ├── system-info.component.scss │ │ │ │ ├── system-info.component.ts │ │ │ │ ├── system-info.component.spec.ts │ │ │ │ └── system-info.component.html │ │ │ ├── page-not-found │ │ │ │ ├── page-not-found.component.scss │ │ │ │ ├── page-not-found.component.html │ │ │ │ ├── page-not-found.component.ts │ │ │ │ └── page-not-found.component.spec.ts │ │ │ ├── event-edit-dialog │ │ │ │ ├── event-edit-dialog.component.scss │ │ │ │ └── event-edit-dialog.component.spec.ts │ │ │ ├── not-implemented │ │ │ │ ├── not-implemented.component.scss │ │ │ │ ├── not-implemented.component.html │ │ │ │ ├── not-implemented.component.ts │ │ │ │ └── not-implemented.component.spec.ts │ │ │ ├── event-create-dialog │ │ │ │ ├── event-create-dialog.component.scss │ │ │ │ ├── event-create-dialog.component.spec.ts │ │ │ │ └── event-create-dialog.component.ts │ │ │ ├── server-edit-dialog │ │ │ │ ├── server-edit-dialog.component.scss │ │ │ │ └── server-edit-dialog.component.spec.ts │ │ │ ├── event-table │ │ │ │ ├── event-table.component.css │ │ │ │ ├── event-table.component.spec.ts │ │ │ │ ├── event-table.component.html │ │ │ │ ├── event-table.component.ts │ │ │ │ └── event-table-datasource.ts │ │ │ ├── server-list │ │ │ │ ├── server-list.component.scss │ │ │ │ ├── server-list.component.spec.ts │ │ │ │ └── server-list.component.html │ │ │ ├── server-create-dialog │ │ │ │ ├── server-create-dialog.component.scss │ │ │ │ └── server-create-dialog.component.spec.ts │ │ │ ├── login-page │ │ │ │ ├── login-page.component.scss │ │ │ │ ├── login-page.component.spec.ts │ │ │ │ ├── login-page.component.html │ │ │ │ └── login-page.component.ts │ │ │ ├── header │ │ │ │ ├── header.component.scss │ │ │ │ ├── header.component.spec.ts │ │ │ │ ├── header.component.html │ │ │ │ └── header.component.ts │ │ │ └── error-dialog │ │ │ │ ├── error-dialog.component.spec.ts │ │ │ │ └── error-dialog.component.ts │ │ ├── config │ │ │ ├── Menu.ts │ │ │ └── Config.ts │ │ ├── models │ │ │ ├── enums │ │ │ │ ├── SessionType.ts │ │ │ │ ├── InstanceState.ts │ │ │ │ ├── Icon.ts │ │ │ │ └── Track.ts │ │ │ ├── dtos │ │ │ │ ├── InfoDto.ts │ │ │ │ ├── ConfigurationDto.ts │ │ │ │ ├── SessionDto.ts │ │ │ │ ├── ApiErrorResponse.ts │ │ │ │ ├── ServerDto.ts │ │ │ │ ├── SettingsDto.ts │ │ │ │ └── EventDto.ts │ │ │ └── objects │ │ │ │ ├── AuthBody.ts │ │ │ │ ├── NavMenuItem.ts │ │ │ │ └── DropDownMenuItem.ts │ │ ├── app.component.html │ │ ├── api │ │ │ ├── AuthApi.ts │ │ │ ├── EventApi.ts │ │ │ └── ServerApi.ts │ │ ├── app.component.ts │ │ ├── services │ │ │ ├── auth.service.spec.ts │ │ │ ├── info.service.spec.ts │ │ │ ├── user.service.spec.ts │ │ │ ├── dialog.service.spec.ts │ │ │ ├── utility.service.spec.ts │ │ │ ├── event-api.service.spec.ts │ │ │ ├── server-api.service.spec.ts │ │ │ ├── info.service.ts │ │ │ ├── user.service.ts │ │ │ ├── auth.service.ts │ │ │ ├── event-api.service.ts │ │ │ ├── server-api.service.ts │ │ │ ├── dialog.service.ts │ │ │ └── utility.service.ts │ │ ├── guards │ │ │ ├── auth.guard.spec.ts │ │ │ └── auth.guard.ts │ │ ├── layouts │ │ │ ├── fullpage-layout │ │ │ │ ├── fullpage-layout.component.ts │ │ │ │ └── fullpage-layout.component.spec.ts │ │ │ └── navbar-layout │ │ │ │ ├── navbar-layout.component.ts │ │ │ │ └── navbar-layout.component.spec.ts │ │ ├── interceptor │ │ │ ├── api-url.interceptor.ts │ │ │ ├── auth.interceptor.ts │ │ │ └── error.interceptor.ts │ │ ├── theme │ │ │ └── accMaterialTheme.scss │ │ ├── app.component.spec.ts │ │ └── app-routing.module.ts │ ├── favicon.ico │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── tsconfig.app.json │ ├── styles.scss │ ├── tsconfig.spec.json │ ├── tslint.json │ ├── browserslist │ ├── main.ts │ ├── index.html │ ├── test.ts │ ├── karma.conf.js │ └── polyfills.ts ├── assets │ ├── Login.png │ ├── ServerList.png │ └── ServerListTable.png ├── proxy.config.json ├── e2e │ ├── tsconfig.e2e.json │ ├── src │ │ ├── app.po.ts │ │ └── app.e2e-spec.ts │ └── protractor.conf.js ├── .editorconfig ├── README.md ├── tsconfig.json ├── .gitignore ├── package.json └── tslint.json ├── api-spec ├── .gitignore ├── assets │ └── api-overview.png └── README.md ├── docker ├── application.properties ├── .gitignore ├── README.md ├── supervisord.conf └── Dockerfile ├── backend ├── src │ ├── test │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── grimsi │ │ │ └── accservermanager │ │ │ └── backend │ │ │ ├── interfaces │ │ │ └── MappingUnitTest.java │ │ │ └── mapping │ │ │ ├── UserDtoMappingTest.java │ │ │ ├── ConfigurationDtoMappingTest.java │ │ │ ├── SessionDtoMappingTest.java │ │ │ ├── InstanceDtoMappingTest.java │ │ │ ├── EventDtoMappingTest.java │ │ │ └── SettingsDtoMappingTest.java │ └── main │ │ ├── resources │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── assets │ │ │ │ └── graphics │ │ │ │ │ ├── acc-logo-header.png │ │ │ │ │ └── login-background.jpg │ │ │ ├── login-background.2d26063e671c0d7daecd.jpg │ │ │ ├── index.html │ │ │ └── runtime.80ab492fe3d778817936.js │ │ └── application.properties │ │ └── java │ │ └── grimsi │ │ └── accservermanager │ │ └── backend │ │ ├── enums │ │ ├── SessionType.java │ │ ├── OperatingSystem.java │ │ ├── InstanceState.java │ │ ├── Car.java │ │ └── Track.java │ │ ├── dto │ │ ├── ErrorDto.java │ │ ├── PlayerDto.java │ │ ├── LeaderboardRankDto.java │ │ ├── SystemInfoDto.java │ │ ├── UserDto.java │ │ ├── InstanceStatsDto.java │ │ ├── InstanceDto.java │ │ ├── SessionDto.java │ │ ├── ConfigurationDto.java │ │ ├── EventDto.java │ │ └── SettingsDto.java │ │ ├── exception │ │ ├── NotFoundException.java │ │ ├── ConflictException.java │ │ ├── ContainerException.java │ │ ├── ApiException.java │ │ ├── UnknownHostOsException.java │ │ ├── CouldNotCreateFolderException.java │ │ ├── CouldNotDeleteFolderException.java │ │ ├── EventInUseException.java │ │ ├── InstanceNotStoppedException.java │ │ ├── CouldNotUpdateFolderException.java │ │ └── IllegalInstanceStateException.java │ │ ├── repository │ │ ├── UserRepository.java │ │ ├── EventRepository.java │ │ └── InstanceRepository.java │ │ ├── controller │ │ ├── FrontendController.java │ │ ├── LoginApiController.java │ │ ├── InfoApiController.java │ │ ├── EventsApiController.java │ │ └── InstancesApiController.java │ │ ├── entity │ │ ├── Configuration.java │ │ ├── Session.java │ │ ├── User.java │ │ ├── Settings.java │ │ ├── Instance.java │ │ └── Event.java │ │ ├── api │ │ ├── InfoApi.java │ │ ├── LoginApi.java │ │ ├── EventsApi.java │ │ └── InstancesApi.java │ │ ├── annotation │ │ ├── ValidEventId.java │ │ └── ValidAccVersion.java │ │ ├── configuration │ │ ├── RFC3339DateFormat.java │ │ ├── ApplicationConfiguration.java │ │ ├── FrontendConfiguration.java │ │ └── ErrorResponseConfiguration.java │ │ ├── error │ │ └── ApiError.java │ │ ├── service │ │ ├── UtilityService.java │ │ ├── JsonSchemaService.java │ │ ├── InfoService.java │ │ └── UserService.java │ │ ├── validator │ │ ├── AccVersionValidator.java │ │ └── EventIdValidator.java │ │ ├── filter │ │ └── ApiOriginFilter.java │ │ ├── security │ │ ├── UserPrincipal.java │ │ ├── JWTAuthenticationFilter.java │ │ └── JWTAuthorizationFilter.java │ │ └── ACCServerManager.java ├── README.md └── assets │ └── application.properties ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── LICENSE └── README.md /postman/.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api-spec/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/components/event-list/event-list.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/components/logout-page/logout-page.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-table/server-table.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/components/system-info/system-info.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/application.properties: -------------------------------------------------------------------------------- 1 | accservermanager.containerized=true 2 | -------------------------------------------------------------------------------- /frontend/src/app/components/page-not-found/page-not-found.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/config/Menu.ts: -------------------------------------------------------------------------------- 1 | export class Menu { 2 | 3 | 4 | } 5 | -------------------------------------------------------------------------------- /backend/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.data.mongodb.port=0 -------------------------------------------------------------------------------- /docker/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea 3 | -------------------------------------------------------------------------------- /frontend/src/app/components/event-edit-dialog/event-edit-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/components/not-implemented/not-implemented.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/components/event-create-dialog/event-create-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-edit-dialog/server-edit-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/frontend/src/favicon.ico -------------------------------------------------------------------------------- /frontend/assets/Login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/frontend/assets/Login.png -------------------------------------------------------------------------------- /frontend/src/app/components/event-table/event-table.component.css: -------------------------------------------------------------------------------- 1 | .full-width-table { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /api-spec/assets/api-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/api-spec/assets/api-overview.png -------------------------------------------------------------------------------- /frontend/assets/ServerList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/frontend/assets/ServerList.png -------------------------------------------------------------------------------- /frontend/src/app/models/enums/SessionType.ts: -------------------------------------------------------------------------------- 1 | export enum SessionType { 2 | P = 'P', 3 | Q = 'Q', 4 | R = 'R' 5 | } 6 | -------------------------------------------------------------------------------- /frontend/assets/ServerListTable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/frontend/assets/ServerListTable.png -------------------------------------------------------------------------------- /frontend/proxy.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "/v1/*": { 3 | "target": "http://localhost:8000", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-list/server-list.component.scss: -------------------------------------------------------------------------------- 1 | app-server-table.full-width-table { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-create-dialog/server-create-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .card-subtitle { 2 | margin-bottom: 10px; 3 | } 4 | -------------------------------------------------------------------------------- /backend/src/main/resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/backend/src/main/resources/public/favicon.ico -------------------------------------------------------------------------------- /frontend/src/assets/graphics/acc-logo-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/frontend/src/assets/graphics/acc-logo-header.png -------------------------------------------------------------------------------- /frontend/src/assets/graphics/login-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/frontend/src/assets/graphics/login-background.jpg -------------------------------------------------------------------------------- /frontend/src/app/components/not-implemented/not-implemented.component.html: -------------------------------------------------------------------------------- 1 |

This component is currently not implemented.

2 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | -------------------------------------------------------------------------------- /postman/README.md: -------------------------------------------------------------------------------- 1 | # ACC Server Manager - Postman 2 | 3 | This repository contains a Postman collection and a Postman Environment which can both be used to interact with my API. 4 | -------------------------------------------------------------------------------- /backend/src/main/resources/public/assets/graphics/acc-logo-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/backend/src/main/resources/public/assets/graphics/acc-logo-header.png -------------------------------------------------------------------------------- /frontend/src/app/models/dtos/InfoDto.ts: -------------------------------------------------------------------------------- 1 | export class InfoDto { 2 | version: string; 3 | metricsEnabled: boolean; 4 | supportedAccVersions: string[]; 5 | activeInstances: number; 6 | } 7 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/enums/SessionType.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.enums; 2 | 3 | public enum SessionType { 4 | P, 5 | Q, 6 | R 7 | } 8 | -------------------------------------------------------------------------------- /backend/src/main/resources/public/assets/graphics/login-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/backend/src/main/resources/public/assets/graphics/login-background.jpg -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/enums/OperatingSystem.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.enums; 2 | 3 | public enum OperatingSystem { 4 | WINDOWS, UNIX, MAC, UNKNOWN 5 | } 6 | -------------------------------------------------------------------------------- /backend/src/main/resources/public/login-background.2d26063e671c0d7daecd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimsi/accservermanager/HEAD/backend/src/main/resources/public/login-background.2d26063e671c0d7daecd.jpg -------------------------------------------------------------------------------- /frontend/src/app/config/Config.ts: -------------------------------------------------------------------------------- 1 | export class Config { 2 | public static baseTitle = 'ACC Server Manager'; 3 | public static apiBasePath = '/v1'; 4 | public static logoutRedirectTimeout = 2000; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/app/components/page-not-found/page-not-found.component.html: -------------------------------------------------------------------------------- 1 |
2 |

404

3 |

The page you are looking for does not exist.

4 |
5 | -------------------------------------------------------------------------------- /frontend/src/app/models/enums/InstanceState.ts: -------------------------------------------------------------------------------- 1 | export enum InstanceState { 2 | STOPPED = 'STOPPED', 3 | RUNNING = 'RUNNING', 4 | CRASHED = 'CRASHED', 5 | PAUSED = 'PAUSED', 6 | UNKNOWN = 'UNKNOWN' 7 | } 8 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # ACC Server Manager - Backend 2 | 3 | The backend of the ACC Server Manager is based on Spring Boot. 4 | It also uses the Spotify Docker client to interface with docker (needed to manage the containers). -------------------------------------------------------------------------------- /frontend/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | version: require('../../package.json').version, 4 | api_url: `/v${require('../../package.json').apiVersion}` 5 | }; 6 | -------------------------------------------------------------------------------- /frontend/src/app/components/logout-page/logout-page.component.html: -------------------------------------------------------------------------------- 1 |
2 |

You have been successfully logged out.

3 |

Redirecting you to the login page...

4 |
5 | 6 | -------------------------------------------------------------------------------- /frontend/src/app/api/AuthApi.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { HttpResponse } from '@angular/common/http'; 3 | 4 | export interface AuthApi { 5 | login(username: string, password: string): Observable>; 6 | } 7 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/enums/InstanceState.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.enums; 2 | 3 | public enum InstanceState { 4 | STOPPED, 5 | RUNNING, 6 | CRASHED, 7 | PAUSED, 8 | UNKNOWN 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent { 9 | } 10 | -------------------------------------------------------------------------------- /api-spec/README.md: -------------------------------------------------------------------------------- 1 | # AC Competizione Server Manager API specification 2 | 3 | Here you can find the most up to date version of the API specification used for my AC Competizione Server Manager Project 4 | 5 | ## Overview 6 | ![Overview](assets/api-overview.png "API Overview") 7 | -------------------------------------------------------------------------------- /frontend/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [ 6 | "node" 7 | ] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/models/dtos/ConfigurationDto.ts: -------------------------------------------------------------------------------- 1 | export class ConfigurationDto { 2 | readonly configVersion = 1; 3 | udpPort: number; 4 | tcpPort: number; 5 | maxConnections: number; 6 | registerToLobby: number; 7 | lanDiscovery: number; 8 | publicIP: number; 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/app/models/objects/AuthBody.ts: -------------------------------------------------------------------------------- 1 | export class AuthBody { 2 | readonly username: string; 3 | readonly password: string; 4 | 5 | constructor(username: string, password: string) { 6 | this.username = username; 7 | this.password = password; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/app/models/dtos/SessionDto.ts: -------------------------------------------------------------------------------- 1 | import { SessionType } from '../enums/SessionType'; 2 | 3 | export class SessionDto { 4 | hourOfDay: number; 5 | dayOfWeekend: number; 6 | timeMultiplier: number; 7 | sessionType: SessionType; 8 | sessionDurationMinutes: number; 9 | } 10 | -------------------------------------------------------------------------------- /frontend/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /frontend/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /backend/src/test/java/grimsi/accservermanager/backend/interfaces/MappingUnitTest.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.interfaces; 2 | 3 | import org.junit.Test; 4 | 5 | public interface MappingUnitTest { 6 | 7 | @Test 8 | void convertEntityToDto(); 9 | 10 | @Test 11 | void convertDtoToEntity(); 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "app/theme/accMaterialTheme"; 3 | 4 | html, body { 5 | height: 100%; 6 | } 7 | 8 | body { 9 | margin: 0; 10 | font-family: Roboto, "Helvetica Neue", sans-serif; 11 | } 12 | 13 | .full-width-table { 14 | width: 100%; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/models/dtos/ApiErrorResponse.ts: -------------------------------------------------------------------------------- 1 | import { HttpErrorResponse } from '@angular/common/http'; 2 | 3 | export interface ApiErrorResponse extends HttpErrorResponse { 4 | error: { 5 | timestamp: Date; 6 | error: string; 7 | status: number; 8 | errors: object[]; 9 | message: string; 10 | path: string; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/dto/ErrorDto.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | @AllArgsConstructor 10 | public class ErrorDto { 11 | private int code; 12 | private String message; 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.NOT_FOUND) 7 | public class NotFoundException extends RuntimeException { 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/app/components/login-page/login-page.component.scss: -------------------------------------------------------------------------------- 1 | #login-card { 2 | min-width: 300px; 3 | width: 20%; 4 | } 5 | 6 | .full-page { 7 | height: 100vh; 8 | width: 100vw; 9 | } 10 | 11 | .login-background { 12 | background: url('~src/assets/graphics/login-background.jpg') no-repeat center center fixed; 13 | background-size: cover; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/app/services/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AuthService } from './auth.service'; 4 | 5 | describe('AuthService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: AuthService = TestBed.get(AuthService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/services/info.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { InfoService } from './info.service'; 4 | 5 | describe('InfoService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: InfoService = TestBed.get(InfoService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/services/user.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { UserService } from './user.service'; 4 | 5 | describe('UserService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: UserService = TestBed.get(UserService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/dto/PlayerDto.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class PlayerDto { 13 | private String name; 14 | private String steamID; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/services/dialog.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { DialogService } from './dialog.service'; 4 | 5 | describe('DialogService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: DialogService = TestBed.get(DialogService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/components/page-not-found/page-not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-page-not-found', 5 | templateUrl: './page-not-found.component.html', 6 | styleUrls: ['./page-not-found.component.scss'] 7 | }) 8 | export class PageNotFoundComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/models/objects/NavMenuItem.ts: -------------------------------------------------------------------------------- 1 | import { Icon } from '../enums/Icon'; 2 | 3 | export class NavMenuItem { 4 | title: string; 5 | icon: Icon; 6 | route: string; 7 | enabled: boolean; 8 | 9 | public constructor(title: string, icon: Icon, route: string, enabled: boolean) { 10 | this.title = title; 11 | this.icon = icon; 12 | this.route = route; 13 | this.enabled = enabled; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/services/utility.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { UtilityService } from './utility.service'; 4 | 5 | describe('UtilityService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: UtilityService = TestBed.get(UtilityService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/components/not-implemented/not-implemented.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-not-implemented', 5 | templateUrl: './not-implemented.component.html', 6 | styleUrls: ['./not-implemented.component.scss'] 7 | }) 8 | export class NotImplementedComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ], 16 | "variable-name": [ 17 | true, 18 | "allow-leading-underscore" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/app/services/event-api.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { EventApiService } from './event-api.service'; 4 | 5 | describe('EventApiService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: EventApiService = TestBed.get(EventApiService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.repository; 2 | 3 | import grimsi.accservermanager.backend.entity.User; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface UserRepository extends MongoRepository { 9 | Optional findByUsername(String username); 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app/services/server-api.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ServerApiService } from './server-api.service'; 4 | 5 | describe('ServerApiService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ServerApiService = TestBed.get(ServerApiService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/guards/auth.guard.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async, inject } from '@angular/core/testing'; 2 | 3 | import { AuthGuard } from './auth.guard'; 4 | 5 | describe('AuthGuard', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthGuard] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([AuthGuard], (guard: AuthGuard) => { 13 | expect(guard).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /frontend/src/app/models/objects/DropDownMenuItem.ts: -------------------------------------------------------------------------------- 1 | import { Icon } from '../enums/Icon'; 2 | 3 | export class DropDownMenuItem { 4 | title: string; 5 | icon: Icon; 6 | action: () => void; 7 | enabled: boolean; 8 | 9 | public constructor(title: string, icon: Icon, action: () => void, enabled: boolean) { 10 | this.title = title; 11 | this.icon = icon; 12 | this.action = action; 13 | this.enabled = enabled; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # accservermanager-container 2 | Docker container for my ACC Server Manager 3 | 4 | [![](https://images.microbadger.com/badges/image/grimsi/accservermanager.svg)](https://microbadger.com/images/grimsi/accservermanager) [![](https://images.microbadger.com/badges/version/grimsi/accservermanager.svg)](https://microbadger.com/images/grimsi/accservermanager) 5 | 6 | Read here how to use this container: https://github.com/grimsi/accservermanager/wiki 7 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # ACC Server Manager - Frontend 2 | 3 | This is the frontend component of my ACC Server Manager. 4 | 5 | It consists of a web application made with Angular and Angular Material. 6 | 7 | Here are some screenshots: 8 | 9 | ![Login Screen](assets/Login.png "Login Screen") 10 | 11 | ![Server List Loading](assets/ServerList.png "Server List") 12 | 13 | ![Server List with Servers](assets/ServerListTable.png "Server List With Servers") 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/ConflictException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.CONFLICT) 7 | public class ConflictException extends RuntimeException { 8 | public ConflictException(String message){ 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import 'hammerjs'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 4 | 5 | import { AppModule } from './app/app.module'; 6 | import { environment } from './environments/environment'; 7 | 8 | if (environment.production) { 9 | enableProdMode(); 10 | } 11 | 12 | platformBrowserDynamic().bootstrapModule(AppModule) 13 | .catch(err => console.error(err)); 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/controller/FrontendController.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | 6 | @Controller 7 | public class FrontendController { 8 | 9 | @GetMapping(value = "/**/{path:[^\\.]*}") 10 | public String gui() { 11 | return "forward:/index.html"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/ContainerException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 8 | public class ContainerException extends RuntimeException{ 9 | 10 | public ContainerException(String message){ 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/entity/Configuration.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.entity; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class Configuration { 9 | private int udpPort; 10 | private int tcpPort; 11 | private int maxConnections; 12 | private int lanDiscovery; 13 | private int registerToLobby; 14 | private int publicIP; 15 | private int configVersion = 1; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/app/models/enums/Icon.ts: -------------------------------------------------------------------------------- 1 | export enum Icon { 2 | dns = 'dns', 3 | directions_car = 'directions_car', 4 | list_alt = 'list_alt', 5 | info = 'info', 6 | lock_open = 'lock_open', 7 | description = 'description', 8 | help = 'help', 9 | play_arrow = 'play_arrow', 10 | stop = 'stop', 11 | pause = 'pause', 12 | error = 'error', 13 | sync_problem = 'sync_problem', 14 | add = 'add', 15 | edit = 'edit', 16 | delete = 'delete', 17 | remove = 'remove' 18 | } 19 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/ApiException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class ApiException extends Exception{ 6 | private HttpStatus status; 7 | public ApiException (HttpStatus status, String msg) { 8 | super(msg); 9 | this.status = status; 10 | } 11 | 12 | public HttpStatus getStatus() { 13 | return status; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/entity/Session.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.entity; 2 | 3 | import grimsi.accservermanager.backend.enums.SessionType; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | 8 | @Getter 9 | @Setter 10 | public class Session { 11 | private int hourOfDay; 12 | private int dayOfWeekend; 13 | private int timeMultiplier; 14 | private SessionType sessionType; 15 | private int sessionDurationMinutes; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ACC Server Manager 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/UnknownHostOsException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 7 | public class UnknownHostOsException extends RuntimeException{ 8 | 9 | public UnknownHostOsException(){ 10 | super("Could not detect host operating system"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/repository/EventRepository.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.repository; 2 | 3 | import grimsi.accservermanager.backend.entity.Event; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface EventRepository extends MongoRepository { 9 | /*@Override 10 | S save(S s);*/ 11 | 12 | Optional findByName(String name); 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/components/header/header.component.scss: -------------------------------------------------------------------------------- 1 | $header-height: 64px; 2 | 3 | .menu-item-icon { 4 | margin-right: 10px; 5 | } 6 | 7 | .logo { 8 | padding-right: 30px; 9 | padding-left: 30px; 10 | } 11 | 12 | .drop-down { 13 | float: right; 14 | } 15 | 16 | .mat-tab-nav-bar, .mat-tab-links, .mat-tab-link { 17 | height: $header-height; 18 | user-select: none; 19 | } 20 | 21 | .spacer { 22 | flex: 1 1 auto; 23 | } 24 | 25 | #username { 26 | margin-right: 10px; 27 | user-select: none; 28 | } 29 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/dto/LeaderboardRankDto.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.dto; 2 | 3 | import grimsi.accservermanager.backend.dto.PlayerDto; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Getter 10 | @Setter 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class LeaderboardRankDto { 14 | private PlayerDto player; 15 | private int rank; 16 | private float bestTime; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/app/layouts/fullpage-layout/fullpage-layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-fullpage-layout', 5 | template: ` 6 |
7 |
8 | 9 |
10 |
11 | `, 12 | styles: [] 13 | }) 14 | export class FullpageLayoutComponent implements OnInit { 15 | 16 | constructor() { 17 | } 18 | 19 | ngOnInit() { 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/app/models/dtos/ServerDto.ts: -------------------------------------------------------------------------------- 1 | import { EventDto } from './EventDto'; 2 | import { InstanceState } from '../enums/InstanceState'; 3 | import { ConfigurationDto } from './ConfigurationDto'; 4 | import { SettingsDto } from './SettingsDto'; 5 | 6 | export class ServerDto { 7 | readonly id: string; 8 | name: string; 9 | restartRequired: boolean; 10 | version: string; 11 | state: InstanceState; 12 | readonly container: string; 13 | configuration: ConfigurationDto; 14 | settings: SettingsDto; 15 | event: EventDto; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/api/InfoApi.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.api; 2 | 3 | import grimsi.accservermanager.backend.dto.SystemInfoDto; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | 8 | @RequestMapping("/v1/info") 9 | public interface InfoApi { 10 | 11 | @GetMapping(produces = {"application/json"}) 12 | ResponseEntity getInfo(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/app/models/dtos/SettingsDto.ts: -------------------------------------------------------------------------------- 1 | export class SettingsDto { 2 | readonly configVersion = 1; 3 | serverName: string; 4 | password: string; 5 | adminPassword: string; 6 | spectatorPassword: string; 7 | carGroup: string; 8 | trackMedalsRequirement: number; 9 | safetyRatingRequirement: number; 10 | racecraftRatingRequirement: number; 11 | maxCarSlots: number; 12 | isRaceLocked: number; 13 | allowAutoDQ: number; 14 | shortFormationLap: number; 15 | formationLapType: number; 16 | ignorePrematureDisconnects: number; 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/dto/SystemInfoDto.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import java.util.List; 9 | 10 | @Getter 11 | @Setter 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class SystemInfoDto { 15 | private String version; 16 | private Boolean metricsEnabled; 17 | private List supportedAccVersions; 18 | private int activeInstances; 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/enums/Car.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | @Getter 10 | @AllArgsConstructor 11 | public enum Car { 12 | CAR1("Car 1", Arrays.asList("Skin 1", "Skin 2")), 13 | CAR2("Car 2", Arrays.asList("Skin 3", "Skin 4")); 14 | 15 | private int name; 16 | private List skins; 17 | 18 | private Car(String name, List skins) { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/enums/Track.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.enums; 2 | 3 | public enum Track { 4 | barcelona, 5 | brands_hatch, 6 | cota, 7 | donington, 8 | hungaroring, 9 | imola, 10 | indianapolis, 11 | kyalami, 12 | laguna_seca, 13 | misano, 14 | mount_panorama, 15 | monza, 16 | nurburgring, 17 | oulton_park, 18 | paul_ricard, 19 | silverstone, 20 | suzuka, 21 | watkins_glen, 22 | snetterton, 23 | spa, 24 | zandvoort, 25 | zolder 26 | } 27 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/CouldNotCreateFolderException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | import java.nio.file.Path; 7 | 8 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 9 | public class CouldNotCreateFolderException extends RuntimeException { 10 | public CouldNotCreateFolderException(Path folderPath) { 11 | super("Could not create folder '" + folderPath + "'."); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/CouldNotDeleteFolderException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | import java.nio.file.Path; 7 | 8 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 9 | public class CouldNotDeleteFolderException extends RuntimeException { 10 | public CouldNotDeleteFolderException(Path folderPath) { 11 | super("Could not delete folder '" + folderPath + "'."); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docker/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user=root 3 | stderr_logfile=/var/log/supervisord.err.log 4 | stdout_logfile=/var/log/supervisord.out.log 5 | loglevel=debug 6 | 7 | [program:mongodb] 8 | command=mongod --smallfiles 9 | autostart=true 10 | autorestart=true 11 | stderr_logfile=/var/log/mongo.err.log 12 | stdout_logfile=/var/log/mongo.out.log 13 | 14 | [program:accservermanager] 15 | directory=/opt/accservermanager/ 16 | command=java -jar accservermanager.jar 17 | autostart=true 18 | autorestart=true 19 | stderr_logfile=/var/log/accservermanager.err.log 20 | stdout_logfile=/var/log/accservermanager.out.log 21 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/EventInUseException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.CONFLICT) 7 | public class EventInUseException extends RuntimeException { 8 | private static final long serialVersionUID = -3073532927650758292L; 9 | 10 | public EventInUseException(String configId) { 11 | super("configuration with id '" + configId + "' is still used by at least one instance."); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/entity/User.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.entity; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.mongodb.core.mapping.Document; 7 | 8 | import java.io.Serializable; 9 | 10 | @Document 11 | @Getter 12 | @Setter 13 | public class User implements Serializable { 14 | 15 | private static final long serialVersionUID = 7650367368584885329L; 16 | 17 | @Id 18 | private String id; 19 | 20 | private String username; 21 | private String password; 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # IntelliJ files 11 | /.idea/ 12 | **/*.iml 13 | 14 | # maven target folder 15 | target/ 16 | 17 | # folder for packaged plugins 18 | plugins/packaged-plugins/ 19 | plugins/packaged-plugins/* 20 | 21 | # Mobile Tools for Java (J2ME) 22 | .mtj.tmp/ 23 | 24 | # Package Files # 25 | *.jar 26 | *.war 27 | *.nar 28 | *.ear 29 | *.zip 30 | *.tar.gz 31 | *.rar 32 | 33 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 34 | hs_err_pid* 35 | .vscode/launch.json 36 | .vscode/settings.json 37 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | import javax.validation.constraints.NotNull; 10 | 11 | @Getter 12 | @Setter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class UserDto { 16 | 17 | @NotBlank(message = "username is required.") 18 | private String username; 19 | 20 | @NotNull(message = "password is required.") 21 | private String password; 22 | } -------------------------------------------------------------------------------- /frontend/src/app/interceptor/api-url.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { Config } from '../config/Config'; 5 | 6 | @Injectable() 7 | export class ApiUrlInterceptor implements HttpInterceptor { 8 | 9 | constructor() { 10 | } 11 | 12 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 13 | request = request.clone({ 14 | url: Config.apiBasePath + request.url 15 | }); 16 | return next.handle(request); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/app/layouts/navbar-layout/navbar-layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-navbar-layout', 5 | template: ` 6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | `, 15 | styles: [] 16 | }) 17 | export class NavbarLayoutComponent implements OnInit { 18 | 19 | constructor() { 20 | } 21 | 22 | ngOnInit() { 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/logout-page/logout-page.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Config } from 'src/app/config/Config'; 4 | 5 | @Component({ 6 | templateUrl: './logout-page.component.html', 7 | styleUrls: ['./logout-page.component.scss'] 8 | }) 9 | export class LogoutPageComponent implements OnInit { 10 | 11 | constructor(private router: Router) { 12 | } 13 | 14 | ngOnInit() { 15 | setTimeout( 16 | () => { 17 | this.router.navigate(['/login']); 18 | }, 19 | Config.logoutRedirectTimeout 20 | ); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/InstanceNotStoppedException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import grimsi.accservermanager.backend.enums.InstanceState; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(HttpStatus.CONFLICT) 8 | public class InstanceNotStoppedException extends RuntimeException { 9 | 10 | public InstanceNotStoppedException(String instanceId, InstanceState state) { 11 | super("Cant delete instance '" + instanceId + "' because its current state is '" + state + "'"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/models/dtos/EventDto.ts: -------------------------------------------------------------------------------- 1 | import { Track } from '../enums/Track'; 2 | import { SessionDto } from './SessionDto'; 3 | 4 | export class EventDto { 5 | readonly configVersion = 1; 6 | readonly id: string; 7 | name: string; 8 | track: Track; 9 | preRaceWaitingTimeSeconds: number; 10 | sessionOverTimeSeconds: number; 11 | postQualySeconds: number; 12 | postRaceSeconds: number; 13 | ambientTemp: number; 14 | trackTemp: number; 15 | cloudLevel: number; 16 | rain: number; 17 | weatherRandomness: number; 18 | simracerWeatherConditions: number; 19 | isFixedConditionQualification: number; 20 | sessions: SessionDto[]; 21 | } 22 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/CouldNotUpdateFolderException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | import java.nio.file.Path; 7 | 8 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 9 | public class CouldNotUpdateFolderException extends RuntimeException { 10 | private static final long serialVersionUID = 5396484203660186517L; 11 | 12 | public CouldNotUpdateFolderException(Path folderPath) { 13 | super("Could not update folder '" + folderPath + "'."); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/annotation/ValidEventId.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.annotation; 2 | 3 | import grimsi.accservermanager.backend.validator.EventIdValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | @Documented 10 | @Constraint(validatedBy = EventIdValidator.class) 11 | @Target(ElementType.FIELD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ValidEventId { 14 | String message() default "event id is invalid."; 15 | 16 | Class[] groups() default {}; 17 | 18 | Class[] payload() default {}; 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/exception/IllegalInstanceStateException.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.exception; 2 | 3 | import grimsi.accservermanager.backend.enums.InstanceState; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(HttpStatus.CONFLICT) 8 | public class IllegalInstanceStateException extends RuntimeException { 9 | 10 | public IllegalInstanceStateException(String action, String instanceId, InstanceState state) { 11 | super("Cant " + action + " instance '" + instanceId + "' because its current state is '" + state + "'"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/annotation/ValidAccVersion.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.annotation; 2 | 3 | import grimsi.accservermanager.backend.validator.AccVersionValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | @Documented 10 | @Constraint(validatedBy = AccVersionValidator.class) 11 | @Target(ElementType.FIELD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ValidAccVersion { 14 | String message() default "unsupported ACC server version."; 15 | Class[] groups() default {}; 16 | Class[] payload() default {}; 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/api/LoginApi.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.api; 2 | 3 | import grimsi.accservermanager.backend.dto.UserDto; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestBody; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | 9 | import javax.validation.Valid; 10 | 11 | @RequestMapping("/v1/login") 12 | public interface LoginApi { 13 | 14 | @PostMapping(produces = {"application/json"}, consumes = {"application/json"}) 15 | ResponseEntity auth(@Valid @RequestBody UserDto body); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /frontend/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /frontend/src/app/components/system-info/system-info.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { InfoService } from '../../services/info.service'; 3 | import { InfoDto } from '../../models/dtos/InfoDto'; 4 | 5 | @Component({ 6 | selector: 'app-system-info', 7 | templateUrl: './system-info.component.html', 8 | styleUrls: ['./system-info.component.scss'] 9 | }) 10 | export class SystemInfoComponent implements OnInit { 11 | 12 | loading = true; 13 | info: InfoDto; 14 | 15 | constructor(private sysInfoService: InfoService) { 16 | } 17 | 18 | ngOnInit() { 19 | this.sysInfoService.getInfo().subscribe((info: InfoDto) => { 20 | this.info = info; 21 | this.loading = false; 22 | }); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /frontend/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to accservermanager-frontend!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /frontend/src/app/services/info.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { InfoDto } from '../models/dtos/InfoDto'; 5 | import { map } from 'rxjs/operators'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class InfoService { 11 | 12 | private readonly apiPath = '/info'; 13 | 14 | constructor(private http: HttpClient) { 15 | } 16 | 17 | public getInfo(): Observable { 18 | return this.http.get(this.apiPath); 19 | } 20 | 21 | public getSupportedVersions(): Observable { 22 | return this.getInfo().pipe(map((info: InfoDto) => { 23 | return info.supportedAccVersions; 24 | })); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/app/components/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HeaderComponent } from './header.component'; 4 | 5 | describe('HeaderComponent', () => { 6 | let component: HeaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HeaderComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HeaderComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/models/enums/Track.ts: -------------------------------------------------------------------------------- 1 | export enum Track { 2 | barcelona = 'barcelona', 3 | brands_hatch = 'brands_hatch', 4 | cota = 'cota', 5 | donington = 'donington', 6 | hungaroring = 'hungaroring', 7 | imola = 'imola', 8 | indianapolis = 'indianapolis', 9 | kyalami = 'kyalami', 10 | laguna_seca = 'laguna_seca', 11 | misano = 'misano', 12 | mount_panorama = 'mount_panorama', 13 | monza = 'monza', 14 | nurburgring = 'nurburgring', 15 | oulton_park = 'oulton_park', 16 | paul_ricard = 'paul_ricard', 17 | silverstone = 'silverstone', 18 | snetterton = 'snetterton', 19 | spa = 'spa', 20 | suzuka = 'suzuka', 21 | watkins_glen = 'watkins_glen', 22 | zandvoort = 'zandvoort', 23 | zolder = 'zolder' 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/app/interceptor/auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { AuthService } from '../services/auth.service'; 5 | 6 | @Injectable() 7 | export class AuthInterceptor implements HttpInterceptor { 8 | 9 | constructor(private authService: AuthService) { 10 | } 11 | 12 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 13 | if (this.authService.isAuthenticated()) { 14 | request = request.clone({ 15 | headers: request.headers.set('Authorization', `Bearer ${this.authService.getToken()}`) 16 | }); 17 | } 18 | return next.handle(request); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/app/guards/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; 3 | import { AuthService } from '../services/auth.service'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class AuthGuard implements CanActivate { 9 | 10 | constructor(private authService: AuthService, 11 | private router: Router) { 12 | } 13 | 14 | canActivate(next: ActivatedRouteSnapshot, 15 | state: RouterStateSnapshot): boolean { 16 | if (this.authService.isAuthenticated()) { 17 | return true; 18 | } else { 19 | this.router.navigate(['/login'], {queryParams: {returnUrl: window.location.pathname}}); 20 | return false; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/configuration/RFC3339DateFormat.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.configuration; 2 | 3 | import com.fasterxml.jackson.databind.util.ISO8601DateFormat; 4 | import com.fasterxml.jackson.databind.util.ISO8601Utils; 5 | 6 | import java.text.FieldPosition; 7 | import java.util.Date; 8 | 9 | 10 | public class RFC3339DateFormat extends ISO8601DateFormat { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | // Same as ISO8601DateFormat but serializing milliseconds. 15 | @Override 16 | public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { 17 | String value = ISO8601Utils.format(date, true); 18 | toAppendTo.append(value); 19 | return toAppendTo; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /frontend/src/app/components/event-list/event-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EventListComponent } from './event-list.component'; 4 | 5 | describe('EventListComponent', () => { 6 | let component: EventListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EventListComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EventListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/login-page/login-page.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoginPageComponent } from './login-page.component'; 4 | 5 | describe('LoginPageComponent', () => { 6 | let component: LoginPageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LoginPageComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LoginPageComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/entity/Settings.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.entity; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class Settings { 9 | private String serverName; 10 | private String password; 11 | private String adminPassword; 12 | private String spectatorPassword; 13 | private String carGroup; 14 | private int trackMedalsRequirement; 15 | private int safetyRatingRequirement; 16 | private int configVersion = 1; 17 | private int racecraftRatingRequirement; 18 | private int maxCarSlots; 19 | private int isRaceLocked; 20 | private int allowAutoDQ; 21 | private int shortFormationLap; 22 | private int formationLapType; 23 | private int ignorePrematureDisconnects; 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/app/components/event-list/event-list.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Loading events...

3 | 4 |
5 |
6 |
7 |
8 |

No events found.

9 | 10 |
11 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /frontend/src/app/components/logout-page/logout-page.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LogoutPageComponent } from './logout-page.component'; 4 | 5 | describe('LogoutPageComponent', () => { 6 | let component: LogoutPageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LogoutPageComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LogoutPageComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-list/server-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ServerListComponent } from './server-list.component'; 4 | 5 | describe('ServerListComponent', () => { 6 | let component: ServerListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ServerListComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ServerListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/system-info/system-info.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SystemInfoComponent } from './system-info.component'; 4 | 5 | describe('SystemInfoComponent', () => { 6 | let component: SystemInfoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SystemInfoComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SystemInfoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/dto/InstanceStatsDto.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.dto; 2 | 3 | import grimsi.accservermanager.backend.enums.Car; 4 | import grimsi.accservermanager.backend.enums.Track; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | 10 | import java.util.List; 11 | 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class InstanceStatsDto { 17 | private int maxPlayerCount; 18 | private int currentPlayerCount; 19 | private List currentPlayers; 20 | private List leaderboard; 21 | private Track currentTrack; 22 | private List tracks; 23 | private Track nextTrack; 24 | private List cars; 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/error-dialog/error-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ErrorDialogComponent } from './error-dialog.component'; 4 | 5 | describe('ErrorDialogComponent', () => { 6 | let component: ErrorDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ErrorDialogComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ErrorDialogComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-list/server-list.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Loading servers...

3 | 4 |
5 |
6 |
7 |
8 |

No servers found.

9 | 10 |
11 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /frontend/src/app/layouts/navbar-layout/navbar-layout.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavbarLayoutComponent } from './navbar-layout.component'; 4 | 5 | describe('NavbarLayoutComponent', () => { 6 | let component: NavbarLayoutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NavbarLayoutComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavbarLayoutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/services/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnInit } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class UserService implements OnInit { 7 | 8 | private readonly userNameKey = 'username'; 9 | 10 | private _userName: string; 11 | 12 | constructor() { 13 | } 14 | 15 | ngOnInit(): void { 16 | const username: string = localStorage.getItem(this.userNameKey); 17 | if (username !== null) { 18 | this.userName = username; 19 | } 20 | } 21 | 22 | get userName(): string { 23 | return this._userName; 24 | } 25 | 26 | set userName(value: string) { 27 | localStorage.setItem(this.userNameKey, value); 28 | this._userName = value; 29 | } 30 | 31 | public removeKey(): void { 32 | localStorage.removeItem(this.userNameKey); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/app/components/page-not-found/page-not-found.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PageNotFoundComponent } from './page-not-found.component'; 4 | 5 | describe('PageNotFoundComponent', () => { 6 | let component: PageNotFoundComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PageNotFoundComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PageNotFoundComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /frontend/src/app/components/not-implemented/not-implemented.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NotImplementedComponent } from './not-implemented.component'; 4 | 5 | describe('NotImplementedComponent', () => { 6 | let component: NotImplementedComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NotImplementedComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NotImplementedComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/layouts/fullpage-layout/fullpage-layout.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FullpageLayoutComponent } from './fullpage-layout.component'; 4 | 5 | describe('FullpageLayoutComponent', () => { 6 | let component: FullpageLayoutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FullpageLayoutComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FullpageLayoutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/event-edit-dialog/event-edit-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EventEditDialogComponent } from './event-edit-dialog.component'; 4 | 5 | describe('EventEditDialogComponent', () => { 6 | let component: EventEditDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EventEditDialogComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EventEditDialogComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | version: require('../../package.json').version, 8 | api_url: `/v${require('../../package.json').apiVersion}` 9 | }; 10 | 11 | /* 12 | * For easier debugging in development mode, you can import the following file 13 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 14 | * 15 | * This import should be commented out in production mode because it will have a negative impact 16 | * on performance if an error is thrown. 17 | */ 18 | import 'zone.js/dist/zone-error'; // Included with Angular CLI. 19 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/repository/InstanceRepository.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.repository; 2 | 3 | import grimsi.accservermanager.backend.entity.Instance; 4 | import grimsi.accservermanager.backend.enums.InstanceState; 5 | import org.springframework.data.mongodb.repository.MongoRepository; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public interface InstanceRepository extends MongoRepository { 11 | Optional findByName(String name); 12 | 13 | Optional> findAllByEvent_Id(String eventId); 14 | 15 | Optional> findAllByConfiguration_TcpPortOrConfiguration_UdpPort(int tcpPort, int udpPort); 16 | 17 | Optional> findAllByState(InstanceState state); 18 | 19 | Optional findByContainer(String containerId); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-edit-dialog/server-edit-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ServerEditDialogComponent } from './server-edit-dialog.component'; 4 | 5 | describe('ServerEditDialogComponent', () => { 6 | let component: ServerEditDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ServerEditDialogComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ServerEditDialogComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/event-create-dialog/event-create-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EventCreateDialogComponent } from './event-create-dialog.component'; 4 | 5 | describe('EventCreateDialogComponent', () => { 6 | let component: EventCreateDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EventCreateDialogComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EventCreateDialogComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /postman/accservermanager.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "c290d073-3a3b-4230-b258-bb84b53dd2a2", 3 | "name": "accservermanager", 4 | "values": [ 5 | { 6 | "key": "jwt", 7 | "value": "", 8 | "description": "", 9 | "enabled": true 10 | }, 11 | { 12 | "key": "username", 13 | "value": "admin", 14 | "description": "", 15 | "enabled": true 16 | }, 17 | { 18 | "key": "password", 19 | "value": "admin", 20 | "description": "", 21 | "enabled": true 22 | }, 23 | { 24 | "key": "eventId", 25 | "value": "", 26 | "description": "", 27 | "enabled": true 28 | }, 29 | { 30 | "key": "instanceId", 31 | "value": "", 32 | "description": "", 33 | "enabled": true 34 | } 35 | ], 36 | "_postman_variable_scope": "environment", 37 | "_postman_exported_at": "2019-03-17T18:54:52.679Z", 38 | "_postman_exported_using": "Postman/7.0.6" 39 | } -------------------------------------------------------------------------------- /frontend/src/app/components/server-create-dialog/server-create-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ServerCreateDialogComponent } from './server-create-dialog.component'; 4 | 5 | describe('ServerCreateDialogComponent', () => { 6 | let component: ServerCreateDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ServerCreateDialogComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ServerCreateDialogComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/components/system-info/system-info.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Loading system info...

3 | 4 |
5 |
6 |
7 |

System Information

8 |

Backend version: {{info.version}}

9 |

Metrics: {{info.metricsEnabled ? 'enabled' : 'disabled'}}

10 |

Supported ACC server versions: 11 | 12 | {{version}} 13 | , 14 | 15 |

16 |

Servers online: {{info.activeInstances}}

17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /backend/src/main/resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ACC Server Manager 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/error/ApiError.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.error; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | import java.time.ZonedDateTime; 8 | 9 | @Getter 10 | @Setter 11 | public class ApiError { 12 | 13 | private final ZonedDateTime timestamp = ZonedDateTime.now(); 14 | 15 | private final String error; 16 | 17 | private final int status; 18 | 19 | private final Object errors; 20 | 21 | private final String message; 22 | 23 | private final String path; 24 | 25 | public ApiError(HttpStatus status, Object errors, String message, String path) { 26 | this.status = status.value(); 27 | error = status.getReasonPhrase(); 28 | this.errors = errors; 29 | this.message = message; 30 | this.path = path; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /frontend/src/app/api/EventApi.ts: -------------------------------------------------------------------------------- 1 | import { EventDto } from '../models/dtos/EventDto'; 2 | import { InstanceState } from '../models/enums/InstanceState'; 3 | import { Observable } from 'rxjs'; 4 | import { ApiErrorResponse } from '../models/dtos/ApiErrorResponse'; 5 | import { EventSourcePolyfill } from 'ng-event-source'; 6 | 7 | export interface EventApi { 8 | createEvent(event: EventDto): Observable; 9 | 10 | getEvents(name?: string, state?: InstanceState): Observable; 11 | 12 | getEventById(eventId: string): Observable; 13 | 14 | deleteEventById(eventId: string): Observable; 15 | 16 | updateEventById(event: EventDto): Observable; 17 | 18 | getEventStream(): EventSourcePolyfill; 19 | 20 | getEventSchema(): Observable; 21 | } 22 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/controller/LoginApiController.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.controller; 2 | 3 | import grimsi.accservermanager.backend.api.LoginApi; 4 | import grimsi.accservermanager.backend.dto.UserDto; 5 | import grimsi.accservermanager.backend.service.UserService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.stereotype.Controller; 10 | 11 | @Controller 12 | public class LoginApiController implements LoginApi { 13 | 14 | @Autowired 15 | private UserService userService; 16 | 17 | @Override 18 | public ResponseEntity auth(UserDto body) { 19 | return userService.authenticate(body) ? new ResponseEntity<>(HttpStatus.OK) : new ResponseEntity<>(HttpStatus.UNAUTHORIZED); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/service/UtilityService.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.service; 2 | 3 | import grimsi.accservermanager.backend.enums.OperatingSystem; 4 | import grimsi.accservermanager.backend.exception.UnknownHostOsException; 5 | import org.springframework.stereotype.Service; 6 | 7 | @Service 8 | public class UtilityService { 9 | 10 | public OperatingSystem getHostOS(){ 11 | String os = System.getProperty("os.name").toLowerCase(); 12 | 13 | if(os.contains("win")){ 14 | return OperatingSystem.WINDOWS; 15 | } 16 | else if(os.contains("mac")){ 17 | return OperatingSystem.MAC; 18 | } 19 | else if(os.contains("nix") || os.contains("nux") || os.contains("aix")){ 20 | return OperatingSystem.UNIX; 21 | } 22 | 23 | throw new UnknownHostOsException(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-alpine 2 | 3 | ENV APPVERSION 1.0.0 4 | ENV PACKAGES mongodb supervisor 5 | 6 | VOLUME /opt/server 7 | VOLUME /data/db 8 | WORKDIR /opt/accservermanager 9 | 10 | ADD supervisord.conf /etc/supervisor.conf 11 | 12 | RUN wget https://github.com/grimsi/accservermanager/releases/download/${APPVERSION}/release.${APPVERSION}.zip && \ 13 | unzip release.${APPVERSION}.zip && \ 14 | rm release.${APPVERSION}.zip application.properties start.bat start.sh && \ 15 | mv accservermanager-${APPVERSION}.jar accservermanager.jar 16 | 17 | ADD application.properties /opt/accservermanager/application.properties 18 | 19 | RUN apk update && \ 20 | apk add --update $PACKAGES --no-cache && \ 21 | rm -rf /var/cache/apk/* 22 | 23 | EXPOSE 8000 24 | EXPOSE 9554 25 | 26 | CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor.conf"] 27 | -------------------------------------------------------------------------------- /frontend/src/app/components/error-dialog/error-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, OnInit } from '@angular/core'; 2 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; 3 | 4 | @Component({ 5 | selector: 'app-error-dialog', 6 | template: ` 7 |

Error

8 | 9 | {{message}} 10 | 11 | 12 | 13 | 14 | ` 15 | }) 16 | export class ErrorDialogComponent implements OnInit { 17 | 18 | message: string; 19 | 20 | constructor(public dialogRef: MatDialogRef, 21 | @Inject(MAT_DIALOG_DATA) data) { 22 | this.message = data.message; 23 | } 24 | 25 | ngOnInit() { 26 | } 27 | 28 | onClick(): void { 29 | this.dialogRef.close(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/entity/Instance.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.entity; 2 | 3 | import grimsi.accservermanager.backend.enums.InstanceState; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.mongodb.core.index.Indexed; 8 | import org.springframework.data.mongodb.core.mapping.DBRef; 9 | import org.springframework.data.mongodb.core.mapping.Document; 10 | 11 | @Document 12 | @Getter 13 | @Setter 14 | public class Instance { 15 | @Id 16 | private String id; 17 | 18 | private boolean restartRequired; 19 | 20 | @Indexed(unique = true) 21 | private String name; 22 | 23 | private InstanceState state; 24 | 25 | private String container; 26 | 27 | private Configuration configuration; 28 | 29 | private Settings settings; 30 | 31 | @DBRef 32 | private Event event; 33 | 34 | private String version; 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/validator/AccVersionValidator.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.validator; 2 | 3 | import grimsi.accservermanager.backend.annotation.ValidAccVersion; 4 | import grimsi.accservermanager.backend.service.FileSystemService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | 7 | import javax.validation.ConstraintValidator; 8 | import javax.validation.ConstraintValidatorContext; 9 | import java.util.HashSet; 10 | 11 | public class AccVersionValidator implements ConstraintValidator { 12 | 13 | private final FileSystemService fileSystemService; 14 | 15 | @Autowired 16 | public AccVersionValidator(FileSystemService fileSystemService){ 17 | this.fileSystemService = fileSystemService; 18 | } 19 | 20 | @Override 21 | public boolean isValid(String version, ConstraintValidatorContext cxt) { 22 | return new HashSet<>(fileSystemService.getInstalledServerVersions()).contains(version); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/configuration/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.configuration; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Primary; 8 | 9 | import java.util.UUID; 10 | 11 | @Getter 12 | @Setter 13 | @Primary 14 | @Configuration 15 | @ConfigurationProperties(prefix = "accservermanager") 16 | public class ApplicationConfiguration { 17 | private UUID secret; 18 | private long expirationTime; 19 | private String username; 20 | private String password; 21 | private String folderPath; 22 | private String serverExecutableName; 23 | private boolean containerNamePostfixEnabled; 24 | private boolean containerized; 25 | private String containerImage; 26 | private String folderPathContainerized; 27 | private boolean deleteUnassignedContainersEnabled; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/app/components/header/header.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | 18 |

{{userService.userName}}

19 | 20 | 23 | 24 | 29 | 30 |
31 | -------------------------------------------------------------------------------- /frontend/src/app/components/event-table/event-table.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 3 | import { MatPaginatorModule, MatSortModule, MatTableModule } from '@angular/material'; 4 | 5 | import { EventTableComponent } from './event-table.component'; 6 | 7 | describe('EventTableComponent', () => { 8 | let component: EventTableComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | declarations: [ EventTableComponent ], 14 | imports: [ 15 | NoopAnimationsModule, 16 | MatPaginatorModule, 17 | MatSortModule, 18 | MatTableModule, 19 | ] 20 | }).compileComponents(); 21 | })); 22 | 23 | beforeEach(() => { 24 | fixture = TestBed.createComponent(EventTableComponent); 25 | component = fixture.componentInstance; 26 | fixture.detectChanges(); 27 | }); 28 | 29 | it('should compile', () => { 30 | expect(component).toBeTruthy(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /frontend/src/app/theme/accMaterialTheme.scss: -------------------------------------------------------------------------------- 1 | @import "~@angular/material/theming"; 2 | 3 | @include mat-core(); 4 | 5 | $md-assetto: ( 6 | 50 : #ffe3e3, 7 | 100 : #ffb9b9, 8 | 200 : #ff8b8b, 9 | 300 : #ff5c5c, 10 | 400 : #ff3939, 11 | 500 : #ff1616, 12 | 600 : #ff1313, 13 | 700 : #ff1010, 14 | 800 : #ff0c0c, 15 | 900 : #ff0606, 16 | A100 : #ffffff, 17 | A200 : #fff3f3, 18 | A400 : #ffc0c0, 19 | A700 : #ffa7a7, 20 | contrast: ( 21 | 50 : #000000, 22 | 100 : #000000, 23 | 200 : #000000, 24 | 300 : #000000, 25 | 400 : #ffffff, 26 | 500 : #ffffff, 27 | 600 : #ffffff, 28 | 700 : #ffffff, 29 | 800 : #ffffff, 30 | 900 : #ffffff, 31 | A100 : #000000, 32 | A200 : #000000, 33 | A400 : #000000, 34 | A700 : #000000, 35 | ) 36 | ); 37 | 38 | $custom-theme-primary: mat-palette($md-assetto); 39 | $custom-theme-accent: mat-palette($mat-grey, A200, A100, A400); 40 | $custom-theme-warn: mat-palette($mat-red); 41 | 42 | $custom-theme: mat-dark-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn); 43 | 44 | @include angular-material-theme($custom-theme); 45 | -------------------------------------------------------------------------------- /frontend/src/app/components/server-table/server-table.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 3 | import { MatPaginatorModule, MatSortModule, MatTableModule } from '@angular/material'; 4 | 5 | import { ServerTableComponent } from './server-table.component'; 6 | 7 | describe('ServerTableComponent', () => { 8 | let component: ServerTableComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | declarations: [ ServerTableComponent ], 14 | imports: [ 15 | NoopAnimationsModule, 16 | MatPaginatorModule, 17 | MatSortModule, 18 | MatTableModule, 19 | ] 20 | }).compileComponents(); 21 | })); 22 | 23 | beforeEach(() => { 24 | fixture = TestBed.createComponent(ServerTableComponent); 25 | component = fixture.componentInstance; 26 | fixture.detectChanges(); 27 | }); 28 | 29 | it('should compile', () => { 30 | expect(component).toBeTruthy(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/filter/ApiOriginFilter.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.filter; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.*; 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | @javax.annotation.Generated(value = "grimsi.accservermanager.backend.codegen.v3.generators.java.SpringCodegen", date = "2019-03-10T17:37:16.729Z[GMT]") 9 | public class ApiOriginFilter implements javax.servlet.Filter { 10 | @Override 11 | public void doFilter(ServletRequest request, ServletResponse response, 12 | FilterChain chain) throws IOException, ServletException { 13 | HttpServletResponse res = (HttpServletResponse) response; 14 | res.addHeader("Access-Control-Allow-Origin", "*"); 15 | res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); 16 | res.addHeader("Access-Control-Allow-Headers", "Content-Type"); 17 | chain.doFilter(request, response); 18 | } 19 | 20 | @Override 21 | public void destroy() { 22 | } 23 | 24 | @Override 25 | public void init(FilterConfig filterConfig) throws ServletException { 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/validator/EventIdValidator.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.validator; 2 | 3 | import grimsi.accservermanager.backend.annotation.ValidEventId; 4 | import grimsi.accservermanager.backend.dto.EventDto; 5 | import grimsi.accservermanager.backend.exception.NotFoundException; 6 | import grimsi.accservermanager.backend.service.EventService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | import javax.validation.ConstraintValidator; 10 | import javax.validation.ConstraintValidatorContext; 11 | 12 | public class EventIdValidator implements ConstraintValidator { 13 | 14 | private final EventService eventService; 15 | 16 | @Autowired 17 | public EventIdValidator(EventService eventService) { 18 | this.eventService = eventService; 19 | } 20 | 21 | @Override 22 | public boolean isValid(EventDto event, ConstraintValidatorContext cxt) { 23 | try { 24 | eventService.findById(event.getId()); 25 | return true; 26 | } catch (NotFoundException e) { 27 | return false; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/service/JsonSchemaService.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.service; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.JsonNode; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | public class JsonSchemaService { 13 | 14 | private final Logger log = LoggerFactory.getLogger(JsonSchemaService.class); 15 | 16 | public String getJsonSchema(Class targetClass) { 17 | try { 18 | ObjectMapper objectMapper = new ObjectMapper(); 19 | JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(new ObjectMapper()); 20 | JsonNode jsonSchema = jsonSchemaGenerator.generateJsonSchema(targetClass); 21 | return objectMapper.writeValueAsString(jsonSchema); 22 | } catch (JsonProcessingException e) { 23 | log.error(e.getMessage()); 24 | return null; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage/accservermanager-frontend'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'accservermanager-frontend'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('accservermanager-frontend'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to accservermanager-frontend!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/entity/Event.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.entity; 2 | 3 | import grimsi.accservermanager.backend.enums.Track; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.mongodb.core.index.Indexed; 8 | import org.springframework.data.mongodb.core.mapping.Document; 9 | 10 | import java.math.BigDecimal; 11 | import java.util.List; 12 | 13 | @Document("event") 14 | @Getter 15 | @Setter 16 | public class Event { 17 | @Id 18 | private String id; 19 | 20 | @Indexed(unique = true) 21 | private String name; 22 | 23 | private Track track; 24 | private int preRaceWaitingTimeSeconds; 25 | private int sessionOverTimeSeconds; 26 | private int postQualySeconds; 27 | private int postRaceSeconds; 28 | private int ambientTemp; 29 | private int trackTemp; 30 | private BigDecimal cloudLevel; 31 | private BigDecimal rain; 32 | private BigDecimal weatherRandomness; 33 | private int simracerWeatherConditions; 34 | private int isFixedConditionQualification; 35 | private List sessions; 36 | private int configVersion = 1; 37 | } 38 | -------------------------------------------------------------------------------- /backend/assets/application.properties: -------------------------------------------------------------------------------- 1 | # Secret value used to sign the JSON Web Tokens. Random value by default that gets generated every restart of the backend. 2 | # Only change it if you want to persist sessions even after rebooting the backend. 3 | # Dont ever tell anyone the value of this secret! 4 | # accservermanager.secret= 5 | 6 | # Expiration time for a session in milliseconds. Default is one day 7 | # accservermanager.expiration-time=86400000 8 | 9 | # Defines if the backend should include the version of the accServer.exe in the container name. Default is true 10 | # accservermanager.container-name-postfix-enabled=true 11 | 12 | # Defines on which port to run the backend. Default is 8000 13 | # server.port=8000 14 | 15 | # Defines on which port to run the metrics endpoint. Default is 9554, please only change if really necessary! 16 | # management.server.port=9554 17 | 18 | # Login credentials for the backend. Keep private! 19 | accservermanager.username=admin 20 | accservermanager.password=admin 21 | 22 | # Path to the ACC Server folder 23 | # Please read https://github.com/grimsi/accservermanager-backend/wiki/Set-up-the-server-executable-folder for further information 24 | # 25 | accservermanager.folder-path= 26 | 27 | -------------------------------------------------------------------------------- /frontend/src/app/api/ServerApi.ts: -------------------------------------------------------------------------------- 1 | import { ServerDto } from '../models/dtos/ServerDto'; 2 | import { InstanceState } from '../models/enums/InstanceState'; 3 | import { Observable } from 'rxjs'; 4 | import { ApiErrorResponse } from '../models/dtos/ApiErrorResponse'; 5 | import { EventSourcePolyfill } from 'ng-event-source'; 6 | 7 | export interface ServerApi { 8 | createServer(server: ServerDto): Observable; 9 | 10 | getServers(name?: string, state?: InstanceState): Observable; 11 | 12 | getServerById(serverId: string): Observable; 13 | 14 | deleteServerById(serverId: string): Observable; 15 | 16 | updateServerById(server: ServerDto): Observable; 17 | 18 | startServerById(serverId: string): Observable; 19 | 20 | stopServerById(serverId: string): Observable; 21 | 22 | pauseServerById(serverId: string): Observable; 23 | 24 | resumeServerById(serverId: string): Observable; 25 | 26 | getServerStream(): EventSourcePolyfill; 27 | 28 | getServerSchema(): Observable; 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/controller/InfoApiController.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.controller; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import grimsi.accservermanager.backend.api.InfoApi; 5 | import grimsi.accservermanager.backend.dto.SystemInfoDto; 6 | import grimsi.accservermanager.backend.service.InfoService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.stereotype.Controller; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | 14 | @Controller 15 | public class InfoApiController implements InfoApi { 16 | 17 | private final HttpServletRequest request; 18 | @Autowired 19 | InfoService infoService; 20 | 21 | @org.springframework.beans.factory.annotation.Autowired 22 | public InfoApiController(ObjectMapper objectMapper, HttpServletRequest request) { 23 | this.request = request; 24 | } 25 | 26 | @Override 27 | public ResponseEntity getInfo() { 28 | 29 | SystemInfoDto systemInfo = infoService.getSystemInfo(); 30 | return new ResponseEntity<>(systemInfo, HttpStatus.OK); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /backend/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # System Info 2 | application.name=@project.artifactId@ 3 | build.version=@project.version@ 4 | # General 5 | logging.level.root=info 6 | # API 7 | server.servlet.context-path=/ 8 | # Server 9 | server.port=8000 10 | management.server.port=9554 11 | management.endpoints.web.exposure.include=* 12 | management.endpoints.web.base-path= 13 | spring.mvc.static-path-pattern=/resources/public/** 14 | # Database 15 | spring.data.mongodb.database=accservermanager 16 | # Spring 17 | spring.jackson.date-format=grimsi.accservermanager.backend.configuration.RFC3339DateFormat 18 | spring.jackson.serialization.write-dates-as-timestamps=false 19 | spring.jackson.mapper.accept-case-insensitive-enums=true 20 | spring.cache.type=none 21 | # ACC Server Manager 22 | accservermanager.secret=${random.uuid} 23 | accservermanager.expiration-time=86400000 24 | accservermanager.server-executable-name=accServer.exe 25 | accservermanager.container-name-postfix-enabled=true 26 | accservermanager.containerized=false 27 | accservermanager.container-image=janonorati92/accserver:latest 28 | accservermanager.folder-path-containerized=/opt/server 29 | accservermanager.delete-unassigned-containers-enabled=false 30 | accservermanager.username= 31 | accservermanager.password= 32 | accservermanager.folder-path= 33 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/configuration/FrontendConfiguration.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.configuration; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.core.Ordered; 5 | import org.springframework.core.annotation.Order; 6 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 7 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 9 | 10 | @Configuration 11 | @Order(Ordered.HIGHEST_PRECEDENCE) 12 | public class FrontendConfiguration implements WebMvcConfigurer { 13 | 14 | private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { 15 | "classpath:/META-INF/resources/", "classpath:/resources/", 16 | "classpath:/public/", "classpath:/public/"}; 17 | 18 | @Override 19 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 20 | registry.addResourceHandler("/**") 21 | .addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS); 22 | } 23 | 24 | @Override 25 | public void addViewControllers(ViewControllerRegistry registry) { 26 | registry.addViewController("/").setViewName("forward:index.html"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # DON'T BE A DICK PUBLIC LICENSE 2 | 3 | > Version 1.1, December 2016 4 | 5 | > Copyright (C) 2019 Simon Grimme 6 | 7 | Everyone is permitted to copy and distribute verbatim or modified 8 | copies of this license document. 9 | 10 | > DON'T BE A DICK PUBLIC LICENSE 11 | > TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 1. Do whatever you like with the original work, just don't be a dick. 14 | 15 | Being a dick includes - but is not limited to - the following instances: 16 | 17 | 1a. Outright copyright infringement - Don't just copy the original work/works and change the name. 18 | 1b. Selling the unmodified original with no work done what-so-ever, that's REALLY being a dick. 19 | 1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick. 20 | 21 | 2. If you become rich through modifications, related works/services, or supporting the original work, 22 | share the love. Only a dick would make loads off this work and not buy the original work's 23 | creator(s) a pint. 24 | 25 | 3. Code is provided with no warranty. Using somebody else's code and bitching when it goes wrong makes 26 | you a DONKEY dick. Fix the problem yourself. A non-dick would submit the fix back or submit a [bug report](https://github.com/grimsi/accservermanager/issues/new) 27 | -------------------------------------------------------------------------------- /backend/src/main/java/grimsi/accservermanager/backend/service/InfoService.java: -------------------------------------------------------------------------------- 1 | package grimsi.accservermanager.backend.service; 2 | 3 | import grimsi.accservermanager.backend.dto.SystemInfoDto; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.core.env.Environment; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class InfoService { 11 | 12 | @Autowired 13 | Environment env; 14 | 15 | @Autowired 16 | FileSystemService fileSystemService; 17 | 18 | @Autowired 19 | InstanceService instanceService; 20 | 21 | @Value("${build.version}") 22 | private String buildVersion; 23 | 24 | public SystemInfoDto getSystemInfo() { 25 | SystemInfoDto systemInfo = new SystemInfoDto(); 26 | 27 | systemInfo.setVersion(buildVersion); 28 | systemInfo.setSupportedAccVersions(fileSystemService.getInstalledServerVersions()); 29 | systemInfo.setMetricsEnabled(areMetricsEnabled()); 30 | systemInfo.setActiveInstances(instanceService.getActiveInstanceCount()); 31 | 32 | return systemInfo; 33 | } 34 | 35 | private boolean areMetricsEnabled() { 36 | return !env.getProperty("management.server.port", String.class).isEmpty(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/src/main/resources/public/runtime.80ab492fe3d778817936.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c