This commit is contained in:
commit
470ed4a47d
29 changed files with 13389 additions and 0 deletions
47
.drone.yml
Normal file
47
.drone.yml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
kind: pipeline
|
||||||
|
name: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: install
|
||||||
|
image: node:14.0
|
||||||
|
commands:
|
||||||
|
- npm --prefix ./server install ./server
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
image: node:14.0
|
||||||
|
commands:
|
||||||
|
- npm --prefix ./server run test
|
||||||
|
|
||||||
|
- name: lint
|
||||||
|
image: node:14.0
|
||||||
|
commands:
|
||||||
|
- npm --prefix ./server run lint
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: node:14.0
|
||||||
|
commands:
|
||||||
|
- npm --prefix ./server run build
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
- deployment
|
||||||
|
|
||||||
|
- name: deploy
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
registry: registry.cliffbreak.de
|
||||||
|
repo: registry.cliffbreak.de/tsviewer
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
- deployment
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- master
|
112
.gitignore
vendored
Normal file
112
.gitignore
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
# ---> Node
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
## Custom direcotries
|
||||||
|
dist/
|
||||||
|
webapp/assets/
|
9
.vscode/extensions.json
vendored
Normal file
9
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"sibiraj-s.vscode-scss-formatter",
|
||||||
|
"mikestead.dotenv",
|
||||||
|
"streetsidesoftware.code-spell-checker",
|
||||||
|
"streetsidesoftware.code-spell-checker-german",
|
||||||
|
]
|
||||||
|
}
|
23
.vscode/settings.json
vendored
Normal file
23
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"html.format.indentHandlebars": true,
|
||||||
|
"eslint.workingDirectories": [
|
||||||
|
"./server",
|
||||||
|
],
|
||||||
|
"eslint.format.enable": true,
|
||||||
|
"cSpell.language": "de,en,de-DE,en-US",
|
||||||
|
"cSpell.words": [
|
||||||
|
"Giesel",
|
||||||
|
"Gitea",
|
||||||
|
"Roboto",
|
||||||
|
"camelcase",
|
||||||
|
"channellist",
|
||||||
|
"clid",
|
||||||
|
"cliffbreak",
|
||||||
|
"fastify",
|
||||||
|
"ijmap",
|
||||||
|
"nestjs",
|
||||||
|
"tsviewer",
|
||||||
|
"uglifycss"
|
||||||
|
],
|
||||||
|
}
|
78
COMMIT_CONVENTION.md
Normal file
78
COMMIT_CONVENTION.md
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
### Commit Message Format
|
||||||
|
|
||||||
|
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||||
|
format that includes a **type**, a **scope** and a **subject**:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
<BLANK LINE>
|
||||||
|
<body>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The **header** is mandatory and the **scope** of the header is optional.
|
||||||
|
|
||||||
|
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier to read on Gitea as well as in various git tools.
|
||||||
|
|
||||||
|
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-using-keywords/) if any.
|
||||||
|
|
||||||
|
Samples:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs(readme): update change log to beta.5
|
||||||
|
```
|
||||||
|
```
|
||||||
|
style(server): reorder imports
|
||||||
|
```
|
||||||
|
```
|
||||||
|
fix(webapp): ui animations should now play on the right time
|
||||||
|
|
||||||
|
Closes #123
|
||||||
|
```
|
||||||
|
|
||||||
|
### Revert
|
||||||
|
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||||
|
|
||||||
|
### Type
|
||||||
|
Must be one of the following:
|
||||||
|
|
||||||
|
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, npm)
|
||||||
|
* **ci**: Changes to our CI configuration files and scripts (example scopes: drone)
|
||||||
|
* **docs**: Documentation only changes
|
||||||
|
* **feat**: A new feature
|
||||||
|
* **fix**: A bug fix
|
||||||
|
* **perf**: A code change that improves performance
|
||||||
|
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||||
|
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||||
|
* **test**: Adding missing tests or correcting existing tests
|
||||||
|
* **bump**: Bump a version to X
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
The following is the list of supported scopes (more to come):
|
||||||
|
|
||||||
|
* **core**: used for changes made in server and webapp
|
||||||
|
* **server** used for changes made in the server
|
||||||
|
* **webapp** used for changes made in the webapp
|
||||||
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||||
|
|
||||||
|
|
||||||
|
### Subject
|
||||||
|
The subject contains succinct description of the change:
|
||||||
|
|
||||||
|
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||||
|
* don't capitalize first letter
|
||||||
|
* no dot (.) at the end
|
||||||
|
|
||||||
|
### Body
|
||||||
|
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
|
||||||
|
The body should include the motivation for the change and contrast this with previous behavior.
|
||||||
|
|
||||||
|
### Footer
|
||||||
|
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||||
|
reference GitHub issues that this commit **Closes**.
|
||||||
|
|
||||||
|
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||||
|
|
||||||
|
### More Infos
|
||||||
|
To generate a changelog **[conventional-changelog](https://github.com/conventional-changelog/conventional-changelog)** is used.
|
9
Dockerfile
Normal file
9
Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
FROM node:14.0
|
||||||
|
ENV NODE_ENV dev
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
COPY server/package*.json ./server/
|
||||||
|
COPY server/.env.example ./server/.env
|
||||||
|
RUN npm --prefix ./server install --production ./server --silent
|
||||||
|
COPY . .
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD npm --prefix ./server start
|
101
README.md
Normal file
101
README.md
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
# Cliffbreak TS-Viewer 🔎
|
||||||
|
|
||||||
|
This is the main Repository containing all required resources to run the server and a corresponding website.
|
||||||
|
`/server` contains the complete backend. Offering a REST API for all actions.
|
||||||
|
`/webapp` contains the Web App, communicating with the REST API from `/server`.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
The following instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
|
||||||
|
|
||||||
|
After setting up you local environment, you can access the following services with your web browser:
|
||||||
|
* Web frontend via [http://localhost:8080/](http://localhost:8080/)
|
||||||
|
* API documentation via [http://localhost:8080/api-docs/](http://localhost:8080/api-docs/)
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
`NodeJS (including NPM)` is required to run this project.
|
||||||
|
You'll also need `git` if you want to contribute to the project.
|
||||||
|
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
|
||||||
|
First clone this repository to your local machine by using
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://git.cliffbreak.de/Cliffbreak/CliffbreakTS-Viewer.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Change to the cloned repository and inside change your active directory to `server`
|
||||||
|
|
||||||
|
```
|
||||||
|
cd CliffbreakTS-Viewer && cd server
|
||||||
|
```
|
||||||
|
|
||||||
|
To install all required packages run
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the .env.example and paste it as .env
|
||||||
|
|
||||||
|
```
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
To test the application run
|
||||||
|
```
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
To start the server in development mode run the following npm script
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configure Visual Studio Code
|
||||||
|
If you haven't a instance of Visual Studio Code up and running download the latest version [here](https://code.visualstudio.com/download).
|
||||||
|
Install the recommended extensions by opening the Extensions-Tab (Ctrl+Shift+X).
|
||||||
|
Enter `@recommended` and install all extensions.
|
||||||
|
After that restart Visual Studio Code to apply all changes.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
First create an issue and assign it to yourself. Or assign an existing issue.
|
||||||
|
Make sure all issue details (description, labels, milestones) are correct.
|
||||||
|
Check out in a new branch. Notice our convention: use either feature or fix followed by a slash (`/`) and then a **short** description using camel case. (`fix/addMissingImports` or `feature/addBasicAuth`).
|
||||||
|
After that you may create a Pull Request to merge your changes into master (If it's work in progress add `WIP:` to the title!).
|
||||||
|
Also refer to our **[COMMIT_CONVENTION](COMMIT_CONVENTION.md)**
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
In this section you can read details on the deployment cycle.
|
||||||
|
|
||||||
|
### Changelog
|
||||||
|
After bumping the version in the `package.json` please run:
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
to update all packages and bump the application version.
|
||||||
|
|
||||||
|
After that generate a changelog via the `changelog` gulp script.
|
||||||
|
```
|
||||||
|
npm run changelog
|
||||||
|
```
|
||||||
|
Then commit and tag your changes using: (use this format for versioning: `v[major][minor][patch]` i.e. v1.2.3)
|
||||||
|
```
|
||||||
|
git commit -m 'bump: to {version}
|
||||||
|
git tag {version}
|
||||||
|
git push origin
|
||||||
|
git push origin --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
## Built With
|
||||||
|
|
||||||
|
* [Node.js](https://nodejs.org/) - The JavaScript runtime used as the project's base
|
||||||
|
* [NestJS](https://docs.nestjs.com/) - The framework used to create the REST API
|
||||||
|
* [HandlebarsJS](https://handlebarsjs.com/) - Dynamic HTML renderer using templates
|
||||||
|
|
||||||
|
## Authors
|
||||||
|
|
||||||
|
* **Simon Giesel** - *Initial work* - [Simon Giesel](https://git.cliffbreak.de/SimGie)
|
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
version: '2.1'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external:
|
||||||
|
name: proxy
|
||||||
|
|
||||||
|
services:
|
||||||
|
tsviewer:
|
||||||
|
container_name: tsviewer
|
||||||
|
restart: always
|
||||||
|
image: registry.cliffbreak.de/tsviewer
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
PRODUCTION: true
|
||||||
|
TEAMSPEAK_HOST: example.com
|
||||||
|
TEAMSPEAK_QUERY_NAME: Query-User
|
||||||
|
TEAMSPEAK_QUERY_PASS: secret
|
||||||
|
PORT: 3000
|
||||||
|
expose:
|
||||||
|
- 3000
|
||||||
|
networks:
|
||||||
|
- proxy
|
6
server/ .eslintignore
Normal file
6
server/ .eslintignore
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# don't ever lint node_modules
|
||||||
|
node_modules
|
||||||
|
# don't lint build output (make sure it's set to your correct build folder name)
|
||||||
|
dist
|
||||||
|
# don't lint nyc coverage output
|
||||||
|
coverage
|
7
server/.env.example
Normal file
7
server/.env.example
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
PRODUCTION=false
|
||||||
|
|
||||||
|
TEAMSPEAK_HOST=example.com
|
||||||
|
TEAMSPEAK_QUERY_NAME=Query-User
|
||||||
|
TEAMSPEAK_QUERY_PASS=secret
|
||||||
|
|
||||||
|
PORT=8080
|
89
server/.eslintrc.js
Normal file
89
server/.eslintrc.js
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
'ecmaVersion': 2017,
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
'node': true,
|
||||||
|
'es6': true,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'camelcase': ['error', {
|
||||||
|
'properties': 'always',
|
||||||
|
'ignoreDestructuring': false,
|
||||||
|
'ignoreImports': false,
|
||||||
|
}],
|
||||||
|
// 'capitalized-comments': ['error', 'always'],
|
||||||
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'comma-spacing': ['error', { 'before': false, 'after': true }],
|
||||||
|
'curly': ['error', 'all'],
|
||||||
|
'eol-last': ['error', 'never'],
|
||||||
|
'indent': ['error', 4],
|
||||||
|
'key-spacing': ['error', {
|
||||||
|
'beforeColon': false,
|
||||||
|
'afterColon': true,
|
||||||
|
'mode': 'strict',
|
||||||
|
}],
|
||||||
|
'keyword-spacing': ['error', {
|
||||||
|
'after': true,
|
||||||
|
'overrides': {
|
||||||
|
'if': { 'after': false },
|
||||||
|
'for': { 'after': false },
|
||||||
|
'while': { 'after': false },
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
'max-classes-per-file': ['error', 1],
|
||||||
|
'max-len': ['error', 150],
|
||||||
|
'no-console': ['warn'],
|
||||||
|
'no-empty': ['error', { 'allowEmptyCatch': false }],
|
||||||
|
'no-multi-spaces': ['error', { 'ignoreEOLComments': false }],
|
||||||
|
'no-trailing-spaces': ['error', { 'ignoreComments': false }],
|
||||||
|
'no-warning-comments': ['warn', { 'location': 'start' }],
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'quotes': ['error', 'single'],
|
||||||
|
'semi': ['error', 'always'],
|
||||||
|
'sort-imports': ['error', {
|
||||||
|
'ignoreCase': false,
|
||||||
|
'ignoreDeclarationSort': false,
|
||||||
|
'ignoreMemberSort': false,
|
||||||
|
'memberSyntaxSortOrder': ['none', 'all', 'multiple', 'single']
|
||||||
|
}],
|
||||||
|
'sort-vars': ['error', { 'ignoreCase': true }],
|
||||||
|
'space-before-blocks': ['error', 'always'],
|
||||||
|
'space-before-function-paren': ['error', {
|
||||||
|
'anonymous': 'always',
|
||||||
|
'named': 'never',
|
||||||
|
'asyncArrow': 'always'
|
||||||
|
}],
|
||||||
|
'space-in-parens': ['error', 'never'],
|
||||||
|
'spaced-comment': ['error', 'always'],
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'@typescript-eslint',
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/eslint-recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
// '@typescript-eslint/array-type': ['error', {
|
||||||
|
// 'default': 'array',
|
||||||
|
// 'readonly': 'array-simple',
|
||||||
|
// }],
|
||||||
|
'@typescript-eslint/interface-name-prefix': ['error', 'always'],
|
||||||
|
'@typescript-eslint/no-empty-interface': ['error', { 'allowSingleExtends': false }],
|
||||||
|
'@typescript-eslint/type-annotation-spacing': ['error', { 'before': false, 'after': true }],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
144
server/gulpfile.js
Normal file
144
server/gulpfile.js
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
const { dest, parallel, series, src, watch } = require('gulp'),
|
||||||
|
changed = require('gulp-changed'),
|
||||||
|
conventionalChangelog = require('gulp-conventional-changelog'),
|
||||||
|
dateFormat = require('dateformat'),
|
||||||
|
del = require('del'),
|
||||||
|
path = require('path'),
|
||||||
|
rename = require('gulp-rename'),
|
||||||
|
sass = require('gulp-sass'),
|
||||||
|
ts = require('gulp-typescript'),
|
||||||
|
uglifycss = require('gulp-uglifycss'),
|
||||||
|
webpack = require('webpack-stream');
|
||||||
|
|
||||||
|
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
function clean() {
|
||||||
|
return del(
|
||||||
|
[
|
||||||
|
'./dist/',
|
||||||
|
'./../webapp/assets/css/',
|
||||||
|
'./../webapp/assets/fonts/',
|
||||||
|
'./../webapp/assets/js/',
|
||||||
|
], { force: true }); // Force option needed to delete files outside of project dir
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileJavaScript() {
|
||||||
|
return src('./../webapp/src/js/app.js')
|
||||||
|
.pipe(webpack({
|
||||||
|
entry: {
|
||||||
|
app: './../webapp/src/js/app.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mode: process.env.PRODUCTION ? 'production' : 'development',
|
||||||
|
resolve: {
|
||||||
|
modules: [
|
||||||
|
path.resolve('./../server/node_modules'),
|
||||||
|
],
|
||||||
|
extensions: ['.js', '.json'],
|
||||||
|
// alias: {
|
||||||
|
// handlebars: 'handlebars/dist/handlebars.min.js',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
query: {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
targets: {
|
||||||
|
chrome: 78,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}).on('error', function () { this.emit('end'); }))
|
||||||
|
.pipe(dest('./../webapp/assets/js/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileScss() {
|
||||||
|
return src('./../webapp/src/scss/*.scss')
|
||||||
|
.pipe(sass({ outputStyle: 'compressed', includePaths: ['./../server/node_modules/'] })).on('error', sass.logError)
|
||||||
|
.pipe(dest('./../webapp/assets/css/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileTypeScript() {
|
||||||
|
const tsProject = ts.createProject('tsconfig.json');
|
||||||
|
// do not compile tests in production
|
||||||
|
tsProject.config.exclude = !tsProject.config.exclude ? ['test'] : tsProject.config.exclude.push('test');
|
||||||
|
return tsProject.src()
|
||||||
|
.pipe(tsProject())
|
||||||
|
.js.pipe(dest('./dist/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function installMaterialIcons() {
|
||||||
|
src(
|
||||||
|
'./node_modules/material-design-icons/iconfont/*.css')
|
||||||
|
.pipe(uglifycss())
|
||||||
|
.pipe(changed('./../webapp/assets/fonts/material-icons/'))
|
||||||
|
.pipe(dest('./../webapp/assets/fonts/material-icons/'));
|
||||||
|
return src(
|
||||||
|
'./node_modules/material-design-icons/iconfont/*',
|
||||||
|
{ ignore: ['**/README.md', '**/codepoints', '**/*.ijmap'] })
|
||||||
|
.pipe(changed('./../webapp/assets/fonts/material-icons/'))
|
||||||
|
.pipe(dest('./../webapp/assets/fonts/material-icons/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function installRoboto() {
|
||||||
|
src('./node_modules/typeface-roboto/files/*')
|
||||||
|
.pipe(changed('./../webapp/assets/fonts/roboto/files/'))
|
||||||
|
.pipe(dest('./../webapp/assets/fonts/roboto/files/'));
|
||||||
|
return src('./node_modules/typeface-roboto/index.css')
|
||||||
|
.pipe(rename('roboto.css'))
|
||||||
|
.pipe(uglifycss())
|
||||||
|
.pipe(changed('./../webapp/assets/fonts/roboto/'))
|
||||||
|
.pipe(dest('./../webapp/assets/fonts/roboto/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function watchCompile() {
|
||||||
|
watch('./../webapp/src/scss/*.scss', { ignoreInitial: false }, compileScss);
|
||||||
|
watch('./../webapp/src/js/*.js', { ignoreInitial: false }, compileJavaScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changelog() {
|
||||||
|
return src('../CHANGELOG.md')
|
||||||
|
.pipe(conventionalChangelog({
|
||||||
|
// conventional-changelog options go here
|
||||||
|
preset: 'angular',
|
||||||
|
// releaseCount: 0, // Uncomment to regenerate whole changelog
|
||||||
|
}, {
|
||||||
|
// context goes here
|
||||||
|
commit: 'commit',
|
||||||
|
date: dateFormat(new Date(), 'dd.mm.yyyy', true),
|
||||||
|
}, {
|
||||||
|
// git-raw-commits options go here
|
||||||
|
}, {
|
||||||
|
// conventional-commits-parser options go here
|
||||||
|
}, {
|
||||||
|
// conventional-changelog-writer options go here
|
||||||
|
}))
|
||||||
|
.pipe(dest('../'));
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.clean = clean;
|
||||||
|
exports.compileFonts = parallel(installMaterialIcons, installRoboto);
|
||||||
|
exports.compile = parallel(compileScss, compileJavaScript, compileTypeScript, exports.compileFonts);
|
||||||
|
exports.watchCompile = series(clean, exports.compileFonts, watchCompile);
|
||||||
|
exports.build = series(clean, exports.compile);
|
||||||
|
exports.changelog = changelog;
|
||||||
|
exports.default = exports.build;
|
12425
server/package-lock.json
generated
Normal file
12425
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
61
server/package.json
Normal file
61
server/package.json
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"name": "ts-viewer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp",
|
||||||
|
"clean": "gulp clean",
|
||||||
|
"dev": "concurrently --kill-others \"gulp watchCompile\" \"npm run dev:node\"",
|
||||||
|
"dev:node": "node -r dotenv/config node_modules/nodemon/bin/nodemon --watch . --ext ts server",
|
||||||
|
"lint": "eslint -c .eslintrc.js --ext .ts ./src ",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.cliffbreak.de/Cliffbreak/CliffbreakTS-Viewer.git"
|
||||||
|
},
|
||||||
|
"author": "Simon Giesel",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^7.0.9",
|
||||||
|
"@nestjs/core": "^7.0.9",
|
||||||
|
"@nestjs/platform-fastify": "^7.0.9",
|
||||||
|
"class-transformer": "^0.2.3",
|
||||||
|
"class-validator": "^0.12.2",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"fastify": "^2.13.1",
|
||||||
|
"fastify-swagger": "^2.5.1",
|
||||||
|
"handlebars": "^4.7.6",
|
||||||
|
"node-ts": "^5.0.4",
|
||||||
|
"point-of-view": "^3.8.0",
|
||||||
|
"reflect-metadata": "^0.1.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.9.0",
|
||||||
|
"@babel/preset-env": "^7.9.5",
|
||||||
|
"@nestjs/swagger": "^4.5.3",
|
||||||
|
"@types/node": "^13.13.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^2.29.0",
|
||||||
|
"@typescript-eslint/parser": "^2.29.0",
|
||||||
|
"babel-loader": "^8.1.0",
|
||||||
|
"concurrently": "^5.2.0",
|
||||||
|
"dateformat": "^3.0.3",
|
||||||
|
"del": "^5.1.0",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"gulp": "^4.0.2",
|
||||||
|
"gulp-changed": "^4.0.2",
|
||||||
|
"gulp-conventional-changelog": "^2.0.29",
|
||||||
|
"gulp-rename": "^2.0.0",
|
||||||
|
"gulp-sass": "^4.1.0",
|
||||||
|
"gulp-typescript": "^6.0.0-alpha.1",
|
||||||
|
"gulp-uglifycss": "^1.1.0",
|
||||||
|
"material-design-icons": "^3.0.1",
|
||||||
|
"nodemon": "^2.0.3",
|
||||||
|
"normalize-scss": "^7.0.1",
|
||||||
|
"ts-node": "^8.8.1",
|
||||||
|
"typeface-roboto": "0.0.75",
|
||||||
|
"typescript": "^3.8.3",
|
||||||
|
"webpack-stream": "^5.2.1"
|
||||||
|
}
|
||||||
|
}
|
2
server/server.js
Normal file
2
server/server.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
require('ts-node/register');
|
||||||
|
require('./src/server');
|
10
server/src/app/app.module.ts
Normal file
10
server/src/app/app.module.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { AppController } from './controllers/app.controller';
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TeamspeakService } from './services/teamspeak.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [TeamspeakService],
|
||||||
|
controllers: [AppController],
|
||||||
|
exports: [],
|
||||||
|
})
|
||||||
|
export class ApplicationModule { }
|
14
server/src/app/controllers/app.controller.ts
Normal file
14
server/src/app/controllers/app.controller.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { Controller, Get, Render } from '@nestjs/common';
|
||||||
|
import { TeamspeakService } from '../services/teamspeak.service';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private teamspeakService: TeamspeakService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@Render('index.hbs')
|
||||||
|
async getIndex(): Promise<object> {
|
||||||
|
const channels = await this.teamspeakService.getPopulatedChannelList();
|
||||||
|
return { title: 'Cliffbreak.de TS-Viewer', channels };
|
||||||
|
}
|
||||||
|
}
|
6
server/src/app/interfaces/channel.interface.ts
Normal file
6
server/src/app/interfaces/channel.interface.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export interface IChannel {
|
||||||
|
id: number;
|
||||||
|
// parent: number;
|
||||||
|
children: IChannel[];
|
||||||
|
name: string;
|
||||||
|
}
|
5
server/src/app/interfaces/client.interface.ts
Normal file
5
server/src/app/interfaces/client.interface.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export interface IClient {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
channel: number;
|
||||||
|
}
|
5
server/src/app/interfaces/populatedchannel.interface.ts
Normal file
5
server/src/app/interfaces/populatedchannel.interface.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { IChannel } from './channel.interface';
|
||||||
|
|
||||||
|
export interface IPopulatedChannel extends IChannel{
|
||||||
|
clients: string[];
|
||||||
|
}
|
80
server/src/app/services/teamspeak.service.ts
Normal file
80
server/src/app/services/teamspeak.service.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { IChannel } from '../interfaces/channel.interface';
|
||||||
|
import { IClient } from '../interfaces/client.interface';
|
||||||
|
import { IPopulatedChannel } from '../interfaces/populatedchannel.interface';
|
||||||
|
import { TeamSpeakClient } from 'node-ts';
|
||||||
|
|
||||||
|
const env = process.env;
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable @typescript-eslint/camelcase */
|
||||||
|
// Disable specific ESLint rules because node-ts has no typings
|
||||||
|
@Injectable()
|
||||||
|
export class TeamspeakService {
|
||||||
|
private client: TeamSpeakClient;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(): Promise<void> {
|
||||||
|
this.client = new TeamSpeakClient(env.TEAMSPEAK_HOST);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.client.connect();
|
||||||
|
await this.client.send('use', { sid: 1 });
|
||||||
|
await this.client.send('login', {
|
||||||
|
client_login_name: env.TEAMSPEAK_QUERY_NAME,
|
||||||
|
client_login_password: env.TEAMSPEAK_QUERY_PASS,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error('Error initializing TeamSpeak Server connection', err, 'TeamspeakService');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getClients(): Promise<IClient[]> {
|
||||||
|
const clients: IClient[] = [];
|
||||||
|
const query = await this.client.send('clientlist', { });
|
||||||
|
const rawList: any = query.response;
|
||||||
|
const clientList = rawList.filter(el => el.client_type == 0);
|
||||||
|
for(const client of clientList)
|
||||||
|
{
|
||||||
|
clients.push({ id: client.clid, name: client.client_nickname, channel: client.cid });
|
||||||
|
}
|
||||||
|
return clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChannels(): Promise<IChannel[]> {
|
||||||
|
const channels: IChannel[] = [];
|
||||||
|
const query = await this.client.send('channellist', {}, []);
|
||||||
|
const channelList: any = query.response;
|
||||||
|
for(const channel of channelList) {
|
||||||
|
if(channel.pid == 0) {
|
||||||
|
channels.push({ id: channel.cid, children: [], name: channel.channel_name });
|
||||||
|
} else {
|
||||||
|
channels.find(el => el.id == channel.pid).children.push({ id: channel.cid, children: [], name: channel.channel_name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPopulatedChannelList(): Promise<IPopulatedChannel[]> {
|
||||||
|
const channels = await this.getChannels();
|
||||||
|
const clients = await this.getClients();
|
||||||
|
return this.addClientToChannel(channels, clients);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addClientToChannel(channels, clients): IPopulatedChannel[] {
|
||||||
|
const channelList: IPopulatedChannel[] = [];
|
||||||
|
for(const channel of channels) {
|
||||||
|
const populatedChannel: IPopulatedChannel = channel;
|
||||||
|
populatedChannel.clients = clients.filter(el=> el.channel == channel.id);
|
||||||
|
if(channel.children.length > 0) {
|
||||||
|
this.addClientToChannel(channel.children, clients);
|
||||||
|
}
|
||||||
|
channelList.push(populatedChannel);
|
||||||
|
}
|
||||||
|
return channelList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
server/src/server.ts
Normal file
45
server/src/server.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||||
|
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||||
|
import { ApplicationModule } from './app/app.module';
|
||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
const env = process.env;
|
||||||
|
const __webapp = join(__dirname, '..', '..', 'webapp');
|
||||||
|
|
||||||
|
|
||||||
|
async function bootstrap(): Promise<void> {
|
||||||
|
const app = await NestFactory.create<NestFastifyApplication>(
|
||||||
|
ApplicationModule,
|
||||||
|
new FastifyAdapter({ logger: false }), // Set to true to activate the built in logger
|
||||||
|
);
|
||||||
|
|
||||||
|
app.useStaticAssets({
|
||||||
|
root: join(__webapp, 'assets'),
|
||||||
|
prefix: '/assets/',
|
||||||
|
});
|
||||||
|
app.setViewEngine({
|
||||||
|
engine: {
|
||||||
|
handlebars: require('handlebars'),
|
||||||
|
},
|
||||||
|
templates: join(__webapp, 'src', 'views'),
|
||||||
|
layout: join('layouts', 'main.hbs'),
|
||||||
|
options: {
|
||||||
|
partials: {
|
||||||
|
'head': join('partials', 'head.hbs'),
|
||||||
|
'footer': join('partials', 'footer.hbs'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const swaggerOptions = new DocumentBuilder()
|
||||||
|
.setTitle('Cliffbreak TeamSpeak API')
|
||||||
|
.setDescription('This is the official Cliffbreak TeamSpeak API!')
|
||||||
|
.addBearerAuth()
|
||||||
|
.setVersion(require('./../package.json').version)
|
||||||
|
.build();
|
||||||
|
const swaggerDocument = SwaggerModule.createDocument(app, swaggerOptions);
|
||||||
|
SwaggerModule.setup('/api-docs', app, swaggerDocument);
|
||||||
|
await app.listen(parseInt(env.PORT));
|
||||||
|
}
|
||||||
|
bootstrap();
|
21
server/tsconfig.json
Normal file
21
server/tsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2017",
|
||||||
|
"module": "commonjs",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"removeComments": true,
|
||||||
|
"preserveConstEnums": false,
|
||||||
|
"declaration": false,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"noLib": false,
|
||||||
|
"allowJs": false,
|
||||||
|
"sourceMap": false,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/**/*",
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"test",
|
||||||
|
]
|
||||||
|
}
|
3
webapp/src/js/app.js
Normal file
3
webapp/src/js/app.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
(async () => {
|
||||||
|
console.log('app.js loaded successfully')
|
||||||
|
})();
|
14
webapp/src/scss/index.scss
Normal file
14
webapp/src/scss/index.scss
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
@import "normalize-scss/sass/normalize/import-now";
|
||||||
|
|
||||||
|
// @import "normalize-scss/sass/_normalize";
|
||||||
|
// @include normalize();
|
||||||
|
|
||||||
|
/** COLORS **/
|
||||||
|
$backgroundColor: #fcfcfc;
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Roboto";
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @import "animations";
|
12
webapp/src/views/index.hbs
Normal file
12
webapp/src/views/index.hbs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{{#each channels}}
|
||||||
|
{{this.name}}<br />
|
||||||
|
{{#each this.clients}}
|
||||||
|
{{this.name}}<br />
|
||||||
|
{{/each}}
|
||||||
|
{{#each this.children}}
|
||||||
|
{{this.name}}<br />
|
||||||
|
{{#each this.clients}}
|
||||||
|
{{this.name}}<br />
|
||||||
|
{{/each}}
|
||||||
|
{{/each}}
|
||||||
|
{{/each}}
|
13
webapp/src/views/layouts/main.hbs
Normal file
13
webapp/src/views/layouts/main.hbs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
{{>head}}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<content>
|
||||||
|
{{{body}}}
|
||||||
|
</content>
|
||||||
|
{{!-- {{>footer}} --}}
|
||||||
|
<script src="/assets/js/app.js" async></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
10
webapp/src/views/partials/footer.hbs
Normal file
10
webapp/src/views/partials/footer.hbs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<copyright>© 2020 Cliffbreak.de</copyright>
|
||||||
|
{{!-- <div>
|
||||||
|
<a href="/imprint/">Impressum</a>
|
||||||
|
|
|
||||||
|
<a href="/disclaimer/">Haftungsausschluss</a>
|
||||||
|
</div> --}}
|
||||||
|
</div>
|
||||||
|
</footer>
|
15
webapp/src/views/partials/head.hbs
Normal file
15
webapp/src/views/partials/head.hbs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{{!-- NOTE: Don't import page specific scripts here (They will NOT be recalled on page change - see router.js --}}
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>{{title}}</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="/assets/fonts/roboto/roboto.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/assets/fonts/material-icons/material-icons.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/assets/css/index.css">
|
||||||
|
<noscript>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/assets/css/noscript.css">
|
||||||
|
</noscript>
|
||||||
|
</head>
|
Loading…
Reference in a new issue