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