Inital commit
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Simon Giesel 2020-04-20 18:40:28 +02:00
commit 067996357a
27 changed files with 13845 additions and 0 deletions

47
.drone.yml Normal file
View file

@ -0,0 +1,47 @@
kind: pipeline
name: default
steps:
- name: install
image: node:13.13
commands:
- npm --prefix ./server install ./server
- name: test
image: node:13.13
commands:
- npm --prefix ./server run test
- name: lint
image: node:13.13
commands:
- npm --prefix ./server run lint
- name: build
image: node:13.13
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/landingpage
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event:
- push
- tag
- deployment
trigger:
branch:
- master

113
.gitignore vendored Normal file
View file

@ -0,0 +1,113 @@
# ---> 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/
mongo/
webapp/assets/

9
.vscode/extensions.json vendored Normal file
View 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",
]
}

27
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,27 @@
{
"editor.formatOnSave": true,
"eslint.workingDirectories": [
"./server",
"./webapp"
],
"eslint.format.enable": true,
"cSpell.language": "de,en,de-DE,en-US",
"cSpell.words": [
"Cliffbreak",
"Fastify",
"Giesel",
"Roboto",
"browserconfig",
"camelcase",
"cliffbreakmeet",
"dbpath",
"favicon",
"ijmap",
"mkdir",
"mongod",
"msapplication",
"nestjs",
"playsinline",
"uglifycss"
],
}

78
COMMIT_CONVENTION.md Normal file
View 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.

121
README.md Normal file
View file

@ -0,0 +1,121 @@
# CliffbreakMeet 💬
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.
Also a working instance of MongoDB `mongod` is required to use all features.
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/CliffbreakMeet.git
```
Change to the cloned repository and inside change your active directory to `server`
```
cd CliffbreakMeet && 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 get the database running create a directory "mongo"
```
mkdir mongo
```
To start the mongo run
```
cd mongo && mongod --dbpath=.
```
To fill the database with dummy data
```
npm run seed
```
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
* [MongoDB](https://docs.mongodb.com/) - The database used to store and retrieve data
* [HandlebarsJS](https://handlebarsjs.com/) - Dynamic HTML renderer using templates
## Authors
* **Simon Giesel** - *Initial work* - [Simon Giesel](https://git.cliffbreak.de/SimGie)

6
server/ .eslintignore Normal file
View 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

3
server/.env.example Normal file
View file

@ -0,0 +1,3 @@
PRODUCTION=false
PORT=8080

89
server/.eslintrc.js Normal file
View 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
View 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;

12909
server/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

62
server/package.json Normal file
View file

@ -0,0 +1,62 @@
{
"name": "cliffbreakmeet",
"version": "0.0.0",
"description": "A self-built video-chat application",
"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/CliffbreakMeet.git"
},
"author": "Simon Giesel",
"license": "ISC",
"dependencies": {
"@nestjs/common": "^6.11.11",
"@nestjs/core": "^6.11.11",
"@nestjs/platform-fastify": "^6.11.11",
"@nestjs/swagger": "^4.5.2",
"class-transformer": "^0.2.3",
"class-validator": "^0.11.1",
"dotenv": "^8.2.0",
"fastify": "^2.13.1",
"fastify-swagger": "^2.5.1",
"handlebars": "^4.7.6",
"mongoose": "^5.9.9",
"point-of-view": "^3.8.0",
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@types/mongoose": "^5.7.12",
"@types/node": "^13.13.0",
"@typescript-eslint/eslint-plugin": "^2.28.0",
"@typescript-eslint/parser": "^2.28.0",
"babel-loader": "^8.1.0",
"concurrently": "^5.1.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.0.2",
"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.2",
"typeface-roboto": "0.0.75",
"typescript": "^3.8.3",
"webpack-stream": "^5.2.1"
}
}

2
server/server.js Normal file
View file

@ -0,0 +1,2 @@
require('ts-node/register');
require('./src/server');

View file

@ -0,0 +1,9 @@
import { AppController } from './controllers/app.controller';
import { Module } from '@nestjs/common';
@Module({
providers: [],
controllers: [AppController],
exports: [],
})
export class ApplicationModule { }

View file

@ -0,0 +1,26 @@
import { Controller, Get, HttpCode, Logger, Param, Render } from '@nestjs/common';
@Controller()
export class AppController {
// Constructor(){}
@Get()
@Render('index.hbs')
getIndex(): object {
return { message: 'Hello world!' };
}
@Get(':room')
@Render('room.hbs')
getRoom(@Param('room') roomId: string): object {
Logger.log('Client joined room: ' + roomId, 'AppController');
return { roomId };
}
@Get('favicon.ico')
@HttpCode(404)
getFavicon(): void {
// for now do nothing
}
}

View file

@ -0,0 +1,5 @@
import { Document } from 'mongoose';
export interface IUser extends Document {
email: string;
}

45
server/src/server.ts Normal file
View 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('CliffbreakMeet API')
.setDescription('This is the official CliffbreakMeet 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();

23
server/tsconfig.json Normal file
View file

@ -0,0 +1,23 @@
{
"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/**/*",
// "./scripts/setup/init.ts",
"./migrations/*",
],
"exclude": [
"test",
]
}

64
webapp/src/js/app.js Normal file
View file

@ -0,0 +1,64 @@
(async () => {
// Send permission dialog to client
const openMediaDevices = async (constraints) => {
return await navigator.mediaDevices.getUserMedia(constraints);
}
try {
const stream = await openMediaDevices({ 'video': true, 'audio': true });
console.log('Got MediaStream:', stream);
function updateList(elements, type) {
const listElement = document.querySelector(type == 'video' ? 'select#availableCameras' : 'select#availableMicrophones');
listElement.innerHTML = '';
elements.forEach(camera => {
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
listElement.add(cameraOption);
});
}
// Fetch an array of devices of a certain type
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Listen for changes to media devices and update the list accordingly
navigator.mediaDevices.addEventListener('devicechange', async _ => {
updateList(await getConnectedDevices('videoinput'), 'video');
updateList(await getConnectedDevices('audioinput'), 'audio');
});
updateList(await getConnectedDevices('videoinput'), 'video');
updateList(await getConnectedDevices('audioinput'), 'audio');
async function playVideoFromCamera() {
try {
const videoSelect = document.querySelector('select#availableCameras');
const stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: videoSelect.options[videoSelect.selectedIndex].value,
// width: 1920,
// height: 1080,
frameRate: 30,
}
});
const videoElement = document.querySelector('video#localVideo');
videoElement.srcObject = stream;
} catch (error) {
console.error('Error opening video camera.', error);
}
}
document.querySelector('button#startVideo').addEventListener('click', _ => {
playVideoFromCamera();
});
} catch (error) {
console.error('Error accessing media devices.', error);
}
})();

View file

@ -0,0 +1 @@
// Add animation here

View 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";

View file

@ -0,0 +1 @@
// TODO

View file

@ -0,0 +1,2 @@
<h1>This is the Index!</h1>
Message: {{message}}

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
{{>head}}
<body>
<content>
{{{body}}}
</content>
{{>footer}}
<script src="/assets/js/app.js" async></script>
</body>
</html>

View 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>

View 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>

View file

@ -0,0 +1,7 @@
This is a room
<p>RoomId: {{roomId}}</p>
<select name="availableCameras" id="availableCameras"></select>
<select name="availableMicrophones" id="availableMicrophones"></select>
<button name="startVideo" id="startVideo">Start Video</button>
<video id="localVideo" autoplay playsinline />