Browse Source

Added registration

master
Shyim 7 years ago
parent
commit
83d7ea4e7f
  1. 3
      .env.dist
  2. 5
      README.md
  3. 4
      config/packages/security.yaml
  4. 7
      config/services.yaml
  5. 2
      public/static/js/app.js
  6. 116
      public/theme/src/components/ReCast/Login.vue
  7. 2
      src/Command/CronRunnerCommand.php
  8. 6
      src/Command/SetupCommand.php
  9. 22
      src/Controller/Index.php
  10. 92
      src/Controller/Register.php
  11. 24
      webpack.config.js

3
.env.dist

@ -33,4 +33,5 @@ JWT_PASSPHRASE=a758fddfbc878122f8b37259b8ea14c3
## ReCast Enviroments ## ReCast Enviroments
NGINX_CONFIG_DIR=/opt/nginx-rtmp/conf/ NGINX_CONFIG_DIR=/opt/nginx-rtmp/conf/
APP_HOST="http://recast.in" APP_HOST="http://recast.in"
NGINX_RESTART_COMMAND="systemctl reload nginx-rtmp" NGINX_RELOAD_COMMAND="systemctl reload nginx-rtmp"
APP_REGISTRATION_ENABLED=false

5
README.md

@ -18,11 +18,12 @@ ReCast is a multi platform streaming tool written in PHP and uses nginx RTMP. Yo
### Environment variable overview ### Environment variable overview
| Name | Description | Example | | Name | Description | Example |
|-----------------------|-------------------------------------------------------------------|--------------------------------------------------| |-----------------------|-------------------------------------------------------------------|--------------------------------------------------|
| APP_HOST | URL which is used in nginx rtmp conf, This address must be http | http://try.recast.in |
| APP_ENV | Which environment it runs | prod | | APP_ENV | Which environment it runs | prod |
| DATABASE_URL | Database credentials as URL | DATABASE_URL=mysql://USER:PASS@HOST:3306/DB_NAME | | DATABASE_URL | Database credentials as URL | DATABASE_URL=mysql://USER:PASS@HOST:3306/DB_NAME |
| NGINX_CONFIG_DIR | Folder where nginx.conf is located | /opt/nginx-rtmp/conf/ | | NGINX_CONFIG_DIR | Folder where nginx.conf is located | /opt/nginx-rtmp/conf/ |
| APP_HOST | URL which is used in nginx rtmp conf, This address must be http | http://try.recast.in | | NGINX_RELOAD_COMMAND | Reload command for nginx rtmp | systemctl reload nginx-rtmp |
| NGINX_RESTART_COMMAND | Reload command for nginx rtmp | systemctl reload nginx-rtmp | | APP_REGISTRATION_ENABLED | Toggles registration form | true |
**Docker Setup will be following** **Docker Setup will be following**

4
config/packages/security.yaml

@ -3,6 +3,10 @@ security:
user_db: user_db:
entity: { class: App\Entity\User, property: username } entity: { class: App\Entity\User, property: username }
firewalls: firewalls:
settings:
pattern: ^/api/(settings|register)
security: false
login: login:
pattern: ^/api/auth/login pattern: ^/api/auth/login
stateless: true stateless: true

7
config/services.yaml

@ -3,8 +3,9 @@
parameters: parameters:
locale: 'en' locale: 'en'
appHost: '%env(APP_HOST)%' appHost: '%env(APP_HOST)%'
nginxFolder: '%env(NGINX_CONFIG_DIR)%' nginxConfigFolder: '%env(NGINX_CONFIG_DIR)%'
nginxRestartCommand: '%env(NGINX_RESTART_COMMAND)%' nginxReloadCommand: '%env(NGINX_RELOAD_COMMAND)%'
registrationEnabled: '%env(APP_REGISTRATION_ENABLED)%'
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file
@ -39,7 +40,7 @@ services:
class: App\Component\NginxConfigGenerator class: App\Component\NginxConfigGenerator
autowire: true autowire: true
bind: bind:
$nginxFolder: '%nginxFolder%' $nginxFolder: '%nginxConfigFolder%'
$appHost: '%appHost%' $appHost: '%appHost%'
# add more service definitions when explicit configuration is needed # add more service definitions when explicit configuration is needed

2
public/static/js/app.js

File diff suppressed because one or more lines are too long

116
public/theme/src/components/ReCast/Login.vue

@ -1,12 +1,11 @@
<template> <template>
<div class="loginWrapper"> <div class="loginWrapper">
<div class="sidebarBanner"></div> <div class="sidebarBanner"></div>
<div class="loginForm"> <div class="loginForm" v-if="!showRegistration">
<h4>Login</h4> <h4>Login</h4>
<div class="alert alert-danger" v-if="authFailed">Bad credentials</div> <div class="alert alert-danger" v-if="authFailed">Bad credentials</div>
<form @submit="onLogin"> <form @submit="onLogin">
<fg-input type="text" <fg-input type="text"
label="Username" label="Username"
@ -24,6 +23,48 @@
</div> </div>
<button class="btn btn-primary">Login</button> <button class="btn btn-primary">Login</button>
<div v-if="settings.registrationEnabled">
<hr>
<div class="text-center">
<a @click="toggleRegistration" href="#">Create a new Account</a>
</div>
</div>
</form>
</div>
<div class="loginForm" v-if="showRegistration">
<h4>Registration</h4>
<form @submit="onRegister">
<fg-input type="text"
label="Username"
placeholder="Username"
pattern=".{3,}"
v-model="register.username">
</fg-input>
<div class="form-group">
<label class="control-label">Password</label>
<input type="password" class="form-control" pattern=".{6,}" placeholder="Password" v-model="register.password">
</div>
<div class="form-group">
<label class="control-label">Password Confirmation</label>
<input type="password" class="form-control" pattern=".{6,}" placeholder="Password Confirmation" v-model="register.passwordConfirm">
</div>
<fg-input type="email"
label="Email"
placeholder="Email"
v-model="register.email">
</fg-input>
<button class="btn btn-primary">Register</button>
<hr>
<div class="text-center">
<a @click="toggleRegistration" href="#">I have already a account</a>
</div>
</form> </form>
</div> </div>
</div> </div>
@ -33,6 +74,11 @@
import Card from 'src/components/UIComponents/Cards/Card.vue' import Card from 'src/components/UIComponents/Cards/Card.vue'
export default { export default {
mounted() {
this.axios.get('/settings').then(response => {
this.settings = response.data;
})
},
components: { components: {
Card, Card,
PCheckbox PCheckbox
@ -43,8 +89,18 @@
_username: '', _username: '',
_password: '', _password: '',
}, },
register: {
username: '',
password: '',
passwordConfirm: '',
email: ''
},
rememberMe: false, rememberMe: false,
authFailed: false authFailed: false,
settings: {
registrationEnabled: false
},
showRegistration: false
} }
}, },
methods: { methods: {
@ -64,6 +120,25 @@
}; };
this.$notify( this.$notify(
{
component: notification,
icon: 'fa fa-exclamation-triangle',
horizontalAlign: 'right',
verticalAlign: 'top',
type: 'danger'
});
}
})
},
onRegister: function(e) {
e.preventDefault();
if (this.register.password !== this.register.passwordConfirm) {
const notification = {
template: `<span>Passwords are not equal</span>`
};
this.$notify(
{ {
component: notification, component: notification,
icon: 'fa fa-exclamation-triangle', icon: 'fa fa-exclamation-triangle',
@ -71,8 +146,39 @@
verticalAlign: 'top', verticalAlign: 'top',
type: 'danger' type: 'danger'
}); });
} } else {
}) this.axios.post('/register', this.register).then(response => {
const notification = {
template: `<span>Registration was successfully. You can login now</span>`
};
this.$notify(
{
component: notification,
icon: 'fa fa-exclamation-triangle',
horizontalAlign: 'right',
verticalAlign: 'top',
type: 'success'
});
this.toggleRegistration();
}).catch(error => {
const notification = {
template: `<span>${error.response.data.message}</span>`
};
this.$notify(
{
component: notification,
icon: 'fa fa-exclamation-triangle',
horizontalAlign: 'right',
verticalAlign: 'top',
type: 'danger'
});
})
}
},
toggleRegistration: function () {
this.showRegistration = !this.showRegistration;
} }
} }
} }

2
src/Command/CronRunnerCommand.php

@ -63,7 +63,7 @@ class CronRunnerCommand extends Command implements ContainerAwareInterface
{ {
if ($this->connection->fetchColumn('SELECT 1 FROM queue')) { if ($this->connection->fetchColumn('SELECT 1 FROM queue')) {
$this->configGenerator->generate(); $this->configGenerator->generate();
system($this->container->getParameter('nginxRestartCommand')); system($this->container->getParameter('nginxReloadCommand'));
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$io->success('Configs generated, rtmp has been reloaded'); $io->success('Configs generated, rtmp has been reloaded');

6
src/Command/SetupCommand.php

@ -48,22 +48,24 @@ class SetupCommand extends Command
$appHost = $io->ask('Please specify a http url where recast will be available', 'http://app.recast.in'); $appHost = $io->ask('Please specify a http url where recast will be available', 'http://app.recast.in');
$nginxFolder = $io->ask('Please specify the nginx folder where nginx rtmp is installed', '/opt/nginx-rtmp/conf/'); $nginxFolder = $io->ask('Please specify the nginx folder where nginx rtmp is installed', '/opt/nginx-rtmp/conf/');
$nginxReloadCommand = $io->ask('Please specify the command that should be executed to reload nginx rtmp', 'systemctl reload nginx-rtmp'); $nginxReloadCommand = $io->ask('Please specify the command that should be executed to reload nginx rtmp', 'systemctl reload nginx-rtmp');
$registerEnabled = $io->ask('Should the registration enabled? (true / false)', 'true');
if (parse_url($appHost, PHP_URL_SCHEME) !== 'http') { if (parse_url($appHost, PHP_URL_SCHEME) !== 'http') {
throw new \RuntimeException('URL must be http due nginx-rtmp limitations'); throw new \RuntimeException('URL must be http due nginx-rtmp limitations');
} }
$envs = []; $envs = [];
$envs['APP_HOST'] = $appHost;
$envs['APP_ENV'] = 'prod'; $envs['APP_ENV'] = 'prod';
$envs['APP_SECRET'] = $this->generateRandomString(); $envs['APP_SECRET'] = $this->generateRandomString();
$envs['REGISTRATION_ENABLED'] = $registerEnabled;
$envs['MAILER_URL'] = 'null://localhost'; $envs['MAILER_URL'] = 'null://localhost';
$envs['JWT_PRIVATE_KEY_PATH'] = 'config/jwt/private.pem'; $envs['JWT_PRIVATE_KEY_PATH'] = 'config/jwt/private.pem';
$envs['JWT_PUBLIC_KEY_PATH'] = 'config/jwt/public.pem'; $envs['JWT_PUBLIC_KEY_PATH'] = 'config/jwt/public.pem';
$envs['JWT_PASSPHRASE'] = 'a758fddfbc878122f8b37259b8ea14c3'; $envs['JWT_PASSPHRASE'] = 'a758fddfbc878122f8b37259b8ea14c3';
$envs['DATABASE_URL'] = sprintf('mysql://%s:%s@%s:%s/%s', $user, $password, $host, $hostPort, $dbName); $envs['DATABASE_URL'] = sprintf('mysql://%s:%s@%s:%s/%s', $user, $password, $host, $hostPort, $dbName);
$envs['NGINX_CONFIG_DIR'] = $nginxFolder; $envs['NGINX_CONFIG_DIR'] = $nginxFolder;
$envs['APP_HOST'] = $appHost; $envs['NGINX_RELOAD_COMMAND'] = $nginxReloadCommand;
$envs['NGINX_RESTART_COMMAND'] = $nginxReloadCommand;
$stringEnv = []; $stringEnv = [];
foreach ($envs as $env => $value) { foreach ($envs as $env => $value) {

22
src/Controller/Index.php

@ -1,20 +1,36 @@
<?php <?php
namespace App\Controller; namespace App\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
/**
* Class Index
* @author Soner Sayakci <shyim@posteo.de>
*/
class Index extends Controller class Index extends Controller
{ {
/** /**
* @Route(path="/", name="index") * @Route(path="/", name="index")
* @author Soner Sayakci <shyim@posteo.de> * @author Soner Sayakci <shyim@posteo.de>
*/ */
public function index() public function index(): Response
{ {
return $this->render('index.twig'); return $this->render('index.twig');
} }
/**
* @Route(path="/api/settings")
* @author Soner Sayakci <shyim@posteo.de>
*/
public function settings(): JsonResponse
{
return new JsonResponse([
'registrationEnabled' => XmlUtils::phpize($this->container->getParameter('registrationEnabled'))
]);
}
} }

92
src/Controller/Register.php

@ -0,0 +1,92 @@
<?php
namespace App\Controller;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\ORMException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* @Route("/api")
* Class Register
* @author Soner Sayakci <shyim@posteo.de>
*/
class Register extends Controller
{
/**
* @var UserRepository
*/
private $repository;
/**
* @var UserPasswordEncoderInterface
*/
private $userPasswordEncoder;
/**
* Register constructor.
* @param UserRepository $repository
* @param UserPasswordEncoderInterface $userPasswordEncoder
* @author Soner Sayakci <shyim@posteo.de>
*/
public function __construct(UserRepository $repository, UserPasswordEncoderInterface $userPasswordEncoder)
{
$this->repository = $repository;
$this->userPasswordEncoder = $userPasswordEncoder;
}
/**
* @Route(path="/register")
* @author Soner Sayakci <shyim@posteo.de>
* @param Request $request
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
if (!$this->isRegistrationEnabled()) {
return new JsonResponse(['message' => 'Registration is disabled'], 500);
}
$data = $request->request->all();
if ($this->repository->findOneBy(['username' => $data['username']])) {
return new JsonResponse(['message' => 'Username is already taken'], 500);
}
if ($this->repository->findOneBy(['email' => $data['email']])) {
return new JsonResponse(['message' => 'Email is already taken'], 500);
}
$user = new User();
$user->setUsername($data['username']);
$user->setPassword($this->userPasswordEncoder->encodePassword($user, $data['password']));
$user->setEmail($data['email']);
$manager = $this->get('doctrine.orm.default_entity_manager');
try {
$manager->persist($user);
$manager->flush();
} catch (ORMException $e) {
return new JsonResponse(['message' => $e->getMessage()], 500);
}
return new JsonResponse();
}
/**
* @return bool
* @author Soner Sayakci <shyim@posteo.de>
*/
private function isRegistrationEnabled(): bool
{
return XmlUtils::phpize($this->container->getParameter('registrationEnabled'));
}
}

24
webpack.config.js

@ -1,24 +0,0 @@
var Encore = require('@symfony/webpack-encore');
Encore
// the project directory where compiled assets will be stored
.setOutputPath('public/build/')
// the public path used by the web server to access the previous directory
.setPublicPath('/build')
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
// uncomment to create hashed filenames (e.g. app.abc123.css)
// .enableVersioning(Encore.isProduction())
// uncomment to define the assets of the project
// .addEntry('js/app', './assets/js/app.js')
// .addStyleEntry('css/app', './assets/css/app.scss')
// uncomment if you use Sass/SCSS files
// .enableSassLoader()
// uncomment for legacy applications that require $/jQuery as a global variable
// .autoProvidejQuery()
;
module.exports = Encore.getWebpackConfig();
Loading…
Cancel
Save