mirror of
https://github.com/fergalmoran/podnoms.git
synced 2025-12-26 19:28:01 +00:00
Basic re-auth framework in place
This commit is contained in:
@@ -1,78 +1,79 @@
|
|||||||
{
|
{
|
||||||
"name": "pod-noms.web",
|
"name": "pod-noms.web",
|
||||||
"version": "0.21.0",
|
"version": "0.21.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve --aot",
|
"start": "ng serve --aot",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint"
|
"lint": "ng lint"
|
||||||
},
|
},
|
||||||
"ngrxGen": {
|
"ngrxGen": {
|
||||||
"basePath": "./src/app",
|
"basePath": "./src/app",
|
||||||
"seperateDirectory": true
|
"seperateDirectory": true
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^5.2.10",
|
"@angular/animations": "^5.2.10",
|
||||||
"@angular/common": "^5.2.10",
|
"@angular/common": "^5.2.10",
|
||||||
"@angular/compiler": "^5.2.10",
|
"@angular/compiler": "^5.2.10",
|
||||||
"@angular/core": "^5.2.10",
|
"@angular/core": "^5.2.10",
|
||||||
"@angular/forms": "^5.2.10",
|
"@angular/forms": "^5.2.10",
|
||||||
"@angular/http": "^5.2.10",
|
"@angular/http": "^5.2.10",
|
||||||
"@angular/platform-browser": "^5.2.10",
|
"@angular/platform-browser": "^5.2.10",
|
||||||
"@angular/platform-browser-dynamic": "^5.2.10",
|
"@angular/platform-browser-dynamic": "^5.2.10",
|
||||||
"@angular/router": "^5.2.10",
|
"@angular/router": "^5.2.10",
|
||||||
"@aspnet/signalr": "1.0.0-rc1-30631",
|
"@aspnet/signalr": "1.0.0-rc1-30631",
|
||||||
"@ngrx/effects": "^5.1.0",
|
"@ngrx/effects": "^5.1.0",
|
||||||
"@ngrx/store": "^5.1.0",
|
"@ngrx/store": "^5.1.0",
|
||||||
"@ngrx/store-devtools": "^5.1.0",
|
"@ngrx/store-devtools": "^5.1.0",
|
||||||
"@qontu/ngx-inline-editor": "^0.2.0-alpha.12",
|
"@qontu/ngx-inline-editor": "^0.2.0-alpha.12",
|
||||||
"angular2-jwt": "^0.2.3",
|
"angular2-jwt": "^0.2.3",
|
||||||
"angular2-moment": "^1.8.0",
|
"angular2-moment": "^1.8.0",
|
||||||
"angularfire2": "^5.0.0-rc.6",
|
"angularfire2": "^5.0.0-rc.6",
|
||||||
"applicationinsights-js": "^1.0.15",
|
"angularx-social-login": "^1.1.8",
|
||||||
"auth0": "^2.9.1",
|
"applicationinsights-js": "^1.0.15",
|
||||||
"auth0-lock": "^11.4.0",
|
"auth0": "^2.9.1",
|
||||||
"bootstrap": "4.1.0",
|
"auth0-lock": "^11.4.0",
|
||||||
"core-js": "^2.5.3",
|
"bootstrap": "4.1.0",
|
||||||
"dropzone": "^5.3.0",
|
"core-js": "^2.5.3",
|
||||||
"firebase": "^4.13.1",
|
"dropzone": "^5.3.0",
|
||||||
"font-awesome": "^4.7.0",
|
"firebase": "^4.13.1",
|
||||||
"howler": "^2.0.9",
|
"font-awesome": "^4.7.0",
|
||||||
"jquery": "^3.3.1",
|
"howler": "^2.0.9",
|
||||||
"lodash": "^4.17.5",
|
"jquery": "^3.3.1",
|
||||||
"ng2-toasty": "^4.0.3",
|
"lodash": "^4.17.5",
|
||||||
"ngx-bootstrap": "^2.0.4",
|
"ng2-toasty": "^4.0.3",
|
||||||
"ngx-clipboard": "^10.0.0",
|
"ngx-bootstrap": "^2.0.4",
|
||||||
"ngx-moment": "^2.0.0-rc.0",
|
"ngx-clipboard": "^10.0.0",
|
||||||
"popper.js": "^1.13.0",
|
"ngx-moment": "^2.0.0-rc.0",
|
||||||
"rxjs": "5.5.6",
|
"popper.js": "^1.13.0",
|
||||||
"simple-line-icons": "^2.4.1",
|
"rxjs": "5.5.6",
|
||||||
"tether": "^1.4.3",
|
"simple-line-icons": "^2.4.1",
|
||||||
"uglify-es": "^3.3.10",
|
"tether": "^1.4.3",
|
||||||
"zone.js": "^0.8.20"
|
"uglify-es": "^3.3.10",
|
||||||
},
|
"zone.js": "^0.8.20"
|
||||||
"devDependencies": {
|
},
|
||||||
"@angular/cli": "1.7.4",
|
"devDependencies": {
|
||||||
"@angular/compiler-cli": "^5.2.6",
|
"@angular/cli": "1.7.4",
|
||||||
"@angular/language-service": "^5.2.6",
|
"@angular/compiler-cli": "^5.2.6",
|
||||||
"@types/applicationinsights-js": "^1.0.5",
|
"@angular/language-service": "^5.2.6",
|
||||||
"@types/jasmine": "^2.8.6",
|
"@types/applicationinsights-js": "^1.0.5",
|
||||||
"@types/node": "~9.4.6",
|
"@types/jasmine": "^2.8.6",
|
||||||
"codelyzer": "^4.2.1",
|
"@types/node": "~9.4.6",
|
||||||
"jasmine-core": "~2.99.1",
|
"codelyzer": "^4.2.1",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-core": "~2.99.1",
|
||||||
"karma": "~2.0.0",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
"karma-chrome-launcher": "~2.2.0",
|
"karma": "~2.0.0",
|
||||||
"karma-cli": "~1.0.1",
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
"karma-coverage-istanbul-reporter": "^1.4.1",
|
"karma-cli": "~1.0.1",
|
||||||
"karma-jasmine": "^1.1.1",
|
"karma-coverage-istanbul-reporter": "^1.4.1",
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
"karma-jasmine": "^1.1.1",
|
||||||
"protractor": "~5.3.0",
|
"karma-jasmine-html-reporter": "^0.2.2",
|
||||||
"ts-node": "^5.0.1",
|
"protractor": "~5.3.0",
|
||||||
"tslint": "~5.9.1",
|
"ts-node": "^5.0.1",
|
||||||
"typescript": "~2.5.3"
|
"tslint": "~5.9.1",
|
||||||
}
|
"typescript": "~2.5.3"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { GlobalsService } from './services/globals.service';
|
|||||||
import { Component, HostBinding, OnInit } from '@angular/core';
|
import { Component, HostBinding, OnInit } from '@angular/core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { ToastyService } from 'ng2-toasty';
|
import { ToastyService } from 'ng2-toasty';
|
||||||
import { AuthService } from 'app/services/auth.service';
|
import { PodnomsAuthService } from 'app/services/podnoms-auth.service';
|
||||||
import { AppInsightsService } from 'app/services/app-insights.service';
|
import { AppInsightsService } from 'app/services/app-insights.service';
|
||||||
import { SignalRService } from 'app/services/signalr.service';
|
import { SignalRService } from 'app/services/signalr.service';
|
||||||
import { ProfileService } from './services/profile.service';
|
import { ProfileService } from './services/profile.service';
|
||||||
@@ -15,15 +15,13 @@ import { MessagingService } from 'app/services/messaging.service';
|
|||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private _authService: AuthService,
|
private _authService: PodnomsAuthService,
|
||||||
private _toastyService: ToastyService,
|
private _toastyService: ToastyService,
|
||||||
private _signalrService: SignalRService,
|
private _signalrService: SignalRService,
|
||||||
private _profileService: ProfileService,
|
private _profileService: ProfileService,
|
||||||
private _messagingService: MessagingService,
|
private _messagingService: MessagingService,
|
||||||
_appInsights: AppInsightsService
|
_appInsights: AppInsightsService
|
||||||
) {
|
) {
|
||||||
_authService.handleAuthentication();
|
|
||||||
_authService.scheduleRenewal();
|
|
||||||
}
|
}
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
return this._authService.isAuthenticated();
|
return this._authService.isAuthenticated();
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
|
|||||||
import { AngularFireDatabaseModule } from 'angularfire2/database';
|
import { AngularFireDatabaseModule } from 'angularfire2/database';
|
||||||
import { AngularFireAuthModule } from 'angularfire2/auth';
|
import { AngularFireAuthModule } from 'angularfire2/auth';
|
||||||
import { AngularFireModule } from 'angularfire2';
|
import { AngularFireModule } from 'angularfire2';
|
||||||
|
import { SocialLoginModule, AuthServiceConfig } from 'angularx-social-login';
|
||||||
|
import {
|
||||||
|
GoogleLoginProvider,
|
||||||
|
FacebookLoginProvider
|
||||||
|
} from 'angularx-social-login';
|
||||||
|
|
||||||
import { ModalModule } from 'ngx-bootstrap/modal';
|
import { ModalModule } from 'ngx-bootstrap/modal';
|
||||||
import { AuthGuard } from './services/auth.guard';
|
import { AuthGuard } from './services/auth.guard';
|
||||||
import { AuthConfig, AuthHttp } from 'angular2-jwt';
|
|
||||||
import { ImageService } from './services/image.service';
|
import { ImageService } from './services/image.service';
|
||||||
import { DebugService } from './services/debug.service';
|
import { DebugService } from './services/debug.service';
|
||||||
import { ChatterService } from './services/chatter.service';
|
import { ChatterService } from './services/chatter.service';
|
||||||
@@ -31,7 +35,7 @@ import { AppComponent } from './app.component';
|
|||||||
import { HomeComponent } from './components/home/home.component';
|
import { HomeComponent } from './components/home/home.component';
|
||||||
import { LoginComponent } from './components/login/login.component';
|
import { LoginComponent } from './components/login/login.component';
|
||||||
import { NavbarComponent } from './components/navbar/navbar.component';
|
import { NavbarComponent } from './components/navbar/navbar.component';
|
||||||
import { AuthService } from './services/auth.service';
|
import { PodnomsAuthService } from './services/podnoms-auth.service';
|
||||||
import { ProfileService } from './services/profile.service';
|
import { ProfileService } from './services/profile.service';
|
||||||
import { MomentModule } from 'angular2-moment';
|
import { MomentModule } from 'angular2-moment';
|
||||||
import { FilterEntryPipe } from './pipes/filter-entry.pipe';
|
import { FilterEntryPipe } from './pipes/filter-entry.pipe';
|
||||||
@@ -61,17 +65,21 @@ import { environment } from 'environments/environment';
|
|||||||
import { FooterPlayerComponent } from 'app/components/footer-player/footer-player.component';
|
import { FooterPlayerComponent } from 'app/components/footer-player/footer-player.component';
|
||||||
import { AudioService } from 'app/services/audio.service';
|
import { AudioService } from 'app/services/audio.service';
|
||||||
import { HumaniseTimePipe } from './pipes/humanise-time.pipe';
|
import { HumaniseTimePipe } from './pipes/humanise-time.pipe';
|
||||||
|
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
||||||
|
import { PodNomsApiInterceptor } from './interceptors/podnoms-api.interceptor';
|
||||||
|
|
||||||
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
|
let config = new AuthServiceConfig([
|
||||||
return new AuthHttp(
|
{
|
||||||
new AuthConfig({
|
id: GoogleLoginProvider.PROVIDER_ID,
|
||||||
noClientCheck: true,
|
provider: new GoogleLoginProvider('Google-OAuth-Client-Id')
|
||||||
globalHeaders: [{ 'Content-Type': 'application/json' }],
|
},
|
||||||
tokenGetter: () => localStorage.getItem('id_token')
|
{
|
||||||
}),
|
id: FacebookLoginProvider.PROVIDER_ID,
|
||||||
http,
|
provider: new FacebookLoginProvider('117715354940616')
|
||||||
options
|
}
|
||||||
);
|
]);
|
||||||
|
export function provideConfig() {
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -112,7 +120,7 @@ export function authHttpServiceFactory(http: Http, options: RequestOptions) {
|
|||||||
}),
|
}),
|
||||||
AngularFireDatabaseModule,
|
AngularFireDatabaseModule,
|
||||||
AngularFireAuthModule,
|
AngularFireAuthModule,
|
||||||
|
HttpClientModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
HttpModule,
|
HttpModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
@@ -123,6 +131,7 @@ export function authHttpServiceFactory(http: Http, options: RequestOptions) {
|
|||||||
ToastyModule.forRoot(),
|
ToastyModule.forRoot(),
|
||||||
DropzoneModule,
|
DropzoneModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
|
SocialLoginModule,
|
||||||
|
|
||||||
StoreModule.forRoot(reducers),
|
StoreModule.forRoot(reducers),
|
||||||
|
|
||||||
@@ -136,12 +145,16 @@ export function authHttpServiceFactory(http: Http, options: RequestOptions) {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AuthService,
|
PodnomsAuthService,
|
||||||
AuthGuard,
|
AuthGuard,
|
||||||
{
|
{
|
||||||
provide: AuthHttp,
|
provide: HTTP_INTERCEPTORS,
|
||||||
useFactory: authHttpServiceFactory,
|
useClass: PodNomsApiInterceptor,
|
||||||
deps: [Http, RequestOptions]
|
multi: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AuthServiceConfig,
|
||||||
|
useFactory: provideConfig
|
||||||
},
|
},
|
||||||
SignalRService,
|
SignalRService,
|
||||||
ProfileService,
|
ProfileService,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { AuthService } from 'app/services/auth.service';
|
import { PodnomsAuthService } from 'app/services/podnoms-auth.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -8,7 +8,7 @@ import { Router } from '@angular/router';
|
|||||||
styleUrls: ['./callback.component.css']
|
styleUrls: ['./callback.component.css']
|
||||||
})
|
})
|
||||||
export class CallbackComponent implements OnInit {
|
export class CallbackComponent implements OnInit {
|
||||||
constructor(private _authService: AuthService, private _router: Router) {}
|
constructor(private _authService: PodnomsAuthService, private _router: Router) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._router.navigate(['/podcasts']);
|
this._router.navigate(['/podcasts']);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AuthService } from 'app/services/auth.service';
|
import { PodnomsAuthService } from 'app/services/podnoms-auth.service';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import { Router } from '@angular/router';
|
|||||||
styleUrls: ['./home.component.css']
|
styleUrls: ['./home.component.css']
|
||||||
})
|
})
|
||||||
export class HomeComponent implements OnInit {
|
export class HomeComponent implements OnInit {
|
||||||
constructor(private _router: Router, private _authService: AuthService) {}
|
constructor(private _router: Router, private _authService: PodnomsAuthService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this._authService.isAuthenticated) {
|
if (this._authService.isAuthenticated) {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.new-user-alert {
|
||||||
|
padding-top: 2.5rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<div id="page-container"
|
<div id="page-container" class="main-content-boxed">
|
||||||
class="main-content-boxed">
|
|
||||||
<main id="main-container">
|
<main id="main-container">
|
||||||
<div class="bg-image"
|
<div class="bg-image" style="background-image: url('/assets/img/robothand.jpg'); background-size: 100% 100%;">
|
||||||
style="background-image: url('/assets/img/robothand.jpg'); background-size: 100% 100%;">
|
|
||||||
<div class="hero-static content content-full bg-white">
|
<div class="hero-static content content-full bg-white">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a class="link-effect font-w700">
|
<a class="link-effect font-w700">
|
||||||
@@ -15,37 +13,26 @@
|
|||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-4 mb-5">
|
<div class="col-sm-4 mb-5">
|
||||||
<button class="btn btn-facebook"
|
<button class="btn btn-facebook" (click)="login('facebook')" style="width: 100%">
|
||||||
(click)="login('facebook')"
|
<i class="fa fa-facebook"></i> Facebook</button>
|
||||||
style="width: 100%"><i class="fa fa-facebook"></i> Facebook</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 mb-5">
|
<div class="col-sm-4 mb-5">
|
||||||
<button class="btn btn-google-plus"
|
<button class="btn btn-google-plus" (click)="login('google-oauth2')" style="width: 100%">
|
||||||
(click)="login('google-oauth2')"
|
<i class="fa fa-google"></i> Google</button>
|
||||||
style="width: 100%"><i class="fa fa-google"></i> Google</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 mb-5">
|
<div class="col-sm-4 mb-5">
|
||||||
<button class="btn btn-twitter"
|
<button class="btn btn-twitter" (click)="login('twitter')" style="width: 100%">
|
||||||
(click)="login('twitter')"
|
<i class="fa fa-twitter"></i> Twitter</button>
|
||||||
style="width: 100%"><i class="fa fa-twitter"></i> Twitter</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-sm-8 col-md-6 col-xl-4">
|
<div class="col-sm-8 col-md-6 col-xl-4">
|
||||||
<form class="js-validation-signin"
|
<form class="js-validation-signin" action="" (ngSubmit)="login()" method="post">
|
||||||
action=""
|
|
||||||
(ngSubmit)="login()"
|
|
||||||
method="post">
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="form-material floating">
|
<div class="form-material floating">
|
||||||
<input type="text"
|
<input type="text" autocomplete="username" class="form-control" id="login-username" name="login-username" [(ngModel)]="username">
|
||||||
autocomplete="username"
|
|
||||||
class="form-control"
|
|
||||||
id="login-username"
|
|
||||||
name="login-username"
|
|
||||||
[(ngModel)]="username">
|
|
||||||
<label for="login-username">Email Address</label>
|
<label for="login-username">Email Address</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -53,37 +40,32 @@
|
|||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="form-material floating">
|
<div class="form-material floating">
|
||||||
<input type="password"
|
<input type="password" autocomplete="current-password" class="form-control" id="login-password" name="login-password" [(ngModel)]="password">
|
||||||
autocomplete="current-password"
|
|
||||||
class="form-control"
|
|
||||||
id="login-password"
|
|
||||||
name="login-password"
|
|
||||||
[(ngModel)]="password">
|
|
||||||
<label for="login-password">Password</label>
|
<label for="login-password">Password</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-danger"
|
<div class="alert alert-danger" *ngIf="errorMessage" role="alert">
|
||||||
*ngIf="errorMessage"
|
|
||||||
role="alert">
|
|
||||||
{{errorMessage}}
|
{{errorMessage}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-12 new-user-alert" *ngIf="brandNew">
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
<strong>Account created!</strong> Please login with your new details
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row gutters-tiny">
|
<div class="form-group row gutters-tiny">
|
||||||
<div class="col-12 mb-10">
|
<div class="col-12 mb-10">
|
||||||
<button type="submit"
|
<button type="submit" class="btn btn-block btn-hero btn-noborder btn-rounded btn-alt-primary">
|
||||||
class="btn btn-block btn-hero btn-noborder btn-rounded btn-alt-primary">
|
|
||||||
<i class="icon-login mr-10"></i> Sign In
|
<i class="icon-login mr-10"></i> Sign In
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 mb-5">
|
<div class="col-sm-6 mb-5">
|
||||||
<a class="btn btn-block btn-noborder btn-rounded btn-alt-secondary"
|
<a class="btn btn-block btn-noborder btn-rounded btn-alt-secondary" [routerLink]="['/register']">
|
||||||
[routerLink]="['/register']">
|
|
||||||
<i class="fa fa-plus text-muted mr-5"></i> New Account
|
<i class="fa fa-plus text-muted mr-5"></i> New Account
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 mb-5">
|
<div class="col-sm-6 mb-5">
|
||||||
<a class="btn btn-block btn-noborder btn-rounded btn-alt-secondary"
|
<a class="btn btn-block btn-noborder btn-rounded btn-alt-secondary" [routerLink]="['/reset']">
|
||||||
[routerLink]="['/reset']">
|
|
||||||
<i class="fa fa-warning text-muted mr-5"></i> Forgot password
|
<i class="fa fa-warning text-muted mr-5"></i> Forgot password
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,4 +76,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,32 +1,65 @@
|
|||||||
import { AuthService } from './../../services/auth.service';
|
import { PodnomsAuthService } from './../../services/podnoms-auth.service';
|
||||||
|
|
||||||
|
import { AuthService } from 'angularx-social-login';
|
||||||
|
import {
|
||||||
|
FacebookLoginProvider,
|
||||||
|
GoogleLoginProvider,
|
||||||
|
LinkedInLoginProvider
|
||||||
|
} from 'angularx-social-login';
|
||||||
|
|
||||||
import { Component, NgZone, OnInit } from '@angular/core';
|
import { Component, NgZone, OnInit } from '@angular/core';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './login.component.html',
|
templateUrl: './login.component.html',
|
||||||
styleUrls: ['./login.component.css']
|
styleUrls: ['./login.component.css']
|
||||||
})
|
})
|
||||||
export class LoginComponent implements OnInit {
|
export class LoginComponent implements OnInit {
|
||||||
|
private _authWindow: Window;
|
||||||
|
private _subscription: Subscription;
|
||||||
|
|
||||||
|
brandNew: boolean = false;
|
||||||
user: any;
|
user: any;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
isRequesting: boolean = false;
|
||||||
signIn;
|
signIn;
|
||||||
widget;
|
widget;
|
||||||
errorMessage: string = '';
|
errorMessage: string = '';
|
||||||
constructor(private _authService: AuthService) {}
|
constructor(
|
||||||
|
private _authService: PodnomsAuthService,
|
||||||
|
private _socialAuthService: AuthService,
|
||||||
|
private _activatedRoute: ActivatedRoute,
|
||||||
|
private _router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {
|
||||||
|
this._subscription = this._activatedRoute.queryParams.subscribe(
|
||||||
|
(param: any) => {
|
||||||
|
this.brandNew = param['brandNew'];
|
||||||
|
this.username = param['email'];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
login(provider?: string) {
|
login(provider?: string) {
|
||||||
if (!provider) {
|
this.isRequesting = true;
|
||||||
this._authService.loginUsername(
|
if (provider === 'facebook') {
|
||||||
this.username,
|
this._socialAuthService.signIn(FacebookLoginProvider.PROVIDER_ID);
|
||||||
this.password,
|
|
||||||
success => this.loginSuccess(success),
|
|
||||||
error => this.loginError(error)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
this._authService.loginSocial(provider);
|
this._authService
|
||||||
|
.login(this.username, this.password)
|
||||||
|
.finally(() => (this.isRequesting = false))
|
||||||
|
.subscribe((result) => {
|
||||||
|
if (result) {
|
||||||
|
this._router.navigate(['/']);
|
||||||
|
}
|
||||||
|
}, (error) => (this.errorMessage = error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._socialAuthService.authState.subscribe((user) => {
|
||||||
|
this.user = user;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
logout() {}
|
logout() {}
|
||||||
loginSuccess(data) {
|
loginSuccess(data) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ProfileModel } from 'app/models/profile.model';
|
import { ProfileModel } from 'app/models/profile.model';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { AuthService } from '../../services/auth.service';
|
import { PodnomsAuthService } from '../../services/podnoms-auth.service';
|
||||||
import { ProfileService } from '../../services/profile.service';
|
import { ProfileService } from '../../services/profile.service';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ export class NavbarComponent implements OnInit {
|
|||||||
user$: Observable<ProfileModel>;
|
user$: Observable<ProfileModel>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _authService: AuthService,
|
private _authService: PodnomsAuthService,
|
||||||
private _profileService: ProfileService
|
private _profileService: ProfileService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|||||||
@@ -40,16 +40,16 @@ export class PodcastAddUrlFormComponent implements AfterViewInit {
|
|||||||
this.isPosting = true;
|
this.isPosting = true;
|
||||||
const entry = new PodcastEntryModel(this.podcast.id, urlToCheck);
|
const entry = new PodcastEntryModel(this.podcast.id, urlToCheck);
|
||||||
this._service.addEntry(entry).subscribe(
|
this._service.addEntry(entry).subscribe(
|
||||||
e => {
|
(e) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
if (e.processingStatus == 6) {
|
if (e.processingStatus == '6') {
|
||||||
this.onUploadDeferred.emit(e);
|
this.onUploadDeferred.emit(e);
|
||||||
} else {
|
} else {
|
||||||
this.onUrlAddComplete.emit(e);
|
this.onUrlAddComplete.emit(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
err => {
|
(err) => {
|
||||||
this.isPosting = false;
|
this.isPosting = false;
|
||||||
this.errorText = 'This does not look like a valid URL';
|
this.errorText = 'This does not look like a valid URL';
|
||||||
this.newEntrySourceUrl = urlToCheck;
|
this.newEntrySourceUrl = urlToCheck;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
ViewChild
|
ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { AuthService } from 'app/services/auth.service';
|
import { PodnomsAuthService } from 'app/services/podnoms-auth.service';
|
||||||
import { PodcastModel } from 'app/models/podcasts.models';
|
import { PodcastModel } from 'app/models/podcasts.models';
|
||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export class PodcastUploadFormComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _toastyService: ToastyService,
|
private _toastyService: ToastyService,
|
||||||
private _auth: AuthService
|
private _auth: PodnomsAuthService
|
||||||
) {}
|
) {}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const config = {
|
const config = {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class PodcastComponent {
|
|||||||
selectedPodcast$: Observable<PodcastModel>;
|
selectedPodcast$: Observable<PodcastModel>;
|
||||||
pendingEntry: PodcastEntryModel = null;
|
pendingEntry: PodcastEntryModel = null;
|
||||||
entries$: Observable<PodcastEntryModel[]>;
|
entries$: Observable<PodcastEntryModel[]>;
|
||||||
uploadMode = true;
|
uploadMode = false;
|
||||||
urlMode = false;
|
urlMode = false;
|
||||||
firstRun = true;
|
firstRun = true;
|
||||||
|
|
||||||
|
|||||||
@@ -73,4 +73,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { AuthService } from './../../services/auth.service';
|
import { PodnomsAuthService } from './../../services/podnoms-auth.service';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-register',
|
selector: 'app-register',
|
||||||
@@ -12,21 +13,23 @@ export class RegisterComponent implements OnInit {
|
|||||||
password: string;
|
password: string;
|
||||||
passwordRepeat: string;
|
passwordRepeat: string;
|
||||||
sending = false;
|
sending = false;
|
||||||
|
_isRequesting: boolean = false;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
constructor(private _authService: AuthService) {}
|
constructor(private _authService: PodnomsAuthService, private _router: Router) {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
|
||||||
doRegister() {
|
doRegister() {
|
||||||
|
this._isRequesting = true;
|
||||||
this._authService
|
this._authService
|
||||||
.signup(this.username, this.password)
|
.signup(this.username, this.password)
|
||||||
.catch(err => {
|
.finally(() => (this._isRequesting = false))
|
||||||
if ((err.code = 'user_exists')) this.errorMessage = 'A user with this email address already exists';
|
.subscribe((result) => {
|
||||||
else this.errorMessage = err.description;
|
if (result) {
|
||||||
|
this._router.navigate(['/login'], {
|
||||||
return Observable.of(`Error logging in: ${err.description}`);
|
queryParams: { brandNew: true, email: this.username }
|
||||||
})
|
});
|
||||||
.subscribe(r => console.log('Done'));
|
}
|
||||||
|
}, (errors) => (this.errorMessage = errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AuthService } from './../../services/auth.service';
|
import { PodnomsAuthService } from './../../services/podnoms-auth.service';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import 'rxjs/add/operator/catch';
|
import 'rxjs/add/operator/catch';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
@@ -12,22 +12,22 @@ export class ResetComponent implements OnInit {
|
|||||||
username: string;
|
username: string;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
successMessage: string;
|
successMessage: string;
|
||||||
constructor(private _authService: AuthService) {}
|
constructor(private _authService: PodnomsAuthService) {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
resetPassword() {
|
resetPassword() {
|
||||||
if (this.username) {
|
if (this.username) {
|
||||||
this._authService
|
this._authService
|
||||||
.resetPassword(this.username)
|
.resetPassword(this.username);
|
||||||
.catch(err => {
|
// .catch(err => {
|
||||||
this.errorMessage = err.description;
|
// this.errorMessage = err.description;
|
||||||
return Observable.of(`Error resetting password: ${err.description}`);
|
// return Observable.of(`Error resetting password: ${err.description}`);
|
||||||
})
|
// })
|
||||||
.subscribe(result => {
|
// .subscribe(result => {
|
||||||
console.log('reset.component.ts', 'method', result);
|
// console.log('reset.component.ts', 'method', result);
|
||||||
this.errorMessage = '';
|
// this.errorMessage = '';
|
||||||
this.successMessage = `A password reset link has been sent to ${this.username}`;
|
// this.successMessage = `A password reset link has been sent to ${this.username}`;
|
||||||
});
|
// });
|
||||||
} else {
|
} else {
|
||||||
this.errorMessage = 'Please enter your email address';
|
this.errorMessage = 'Please enter your email address';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { Injectable } from '@angular/core';
|
|||||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
||||||
import { tokenNotExpired } from 'angular2-jwt';
|
import { tokenNotExpired } from 'angular2-jwt';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { AuthService } from './auth.service';
|
import { PodnomsAuthService } from './podnoms-auth.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthGuard implements CanActivate {
|
||||||
constructor(private _auth: AuthService) {}
|
constructor(private _auth: PodnomsAuthService) {}
|
||||||
canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (this._auth.isAuthenticated()) {
|
if (this._auth.isAuthenticated()) {
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
import { environment } from 'environments/environment';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { AUTH_CONFIG } from './../constants/auth0';
|
|
||||||
import * as auth0 from 'auth0-js';
|
|
||||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
|
||||||
import { Observable } from 'rxjs/Rx';
|
|
||||||
|
|
||||||
import 'rxjs/add/observable/throw';
|
|
||||||
import 'rxjs/add/operator/filter';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AuthService {
|
|
||||||
errorMessage: string;
|
|
||||||
refreshSubscription: any;
|
|
||||||
|
|
||||||
auth0 = new auth0.WebAuth({
|
|
||||||
domain: AUTH_CONFIG.AUTH0_DOMAIN,
|
|
||||||
clientID: AUTH_CONFIG.AUTH0_CLIENT_ID,
|
|
||||||
redirectUri: AUTH_CONFIG.AUTH0_CALLBACKURL,
|
|
||||||
audience: `https://${AUTH_CONFIG.AUTH0_DOMAIN}/userinfo`,
|
|
||||||
responseType: 'token id_token',
|
|
||||||
prompt: 'select_account',
|
|
||||||
scope: 'openid profile email'
|
|
||||||
});
|
|
||||||
constructor(private _router: Router) {}
|
|
||||||
public loginUsername(username: string, password: string, success, error): void {
|
|
||||||
this.auth0.client.login(
|
|
||||||
{
|
|
||||||
realm: 'podnoms-db-connection',
|
|
||||||
username: username,
|
|
||||||
password: password
|
|
||||||
},
|
|
||||||
(err, authResult) => {
|
|
||||||
if (err) {
|
|
||||||
error(err);
|
|
||||||
console.log(err);
|
|
||||||
return;
|
|
||||||
} else if (authResult && authResult.accessToken && authResult.idToken) {
|
|
||||||
this.setSession(authResult);
|
|
||||||
success(authResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
public signup(email: string, password: string): Observable<any> {
|
|
||||||
return Observable.create(observer => {
|
|
||||||
this.auth0.redirect.signupAndLogin(
|
|
||||||
{
|
|
||||||
connection: 'podnoms-db-connection',
|
|
||||||
email,
|
|
||||||
password
|
|
||||||
},
|
|
||||||
err => {
|
|
||||||
if (err) {
|
|
||||||
observer.error(err);
|
|
||||||
} else observer.next();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public resetPassword(email: string): Observable<any> {
|
|
||||||
return Observable.create(observer => {
|
|
||||||
this.auth0.changePassword(
|
|
||||||
{
|
|
||||||
connection: 'podnoms-db-connection',
|
|
||||||
email
|
|
||||||
},
|
|
||||||
(err, resp) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
Observable.throw(err);
|
|
||||||
} else {
|
|
||||||
observer.next('success');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public loginSocial(provider: string): void {
|
|
||||||
this.auth0.authorize({
|
|
||||||
connection: provider
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public handleAuthentication(): void {
|
|
||||||
this.auth0.parseHash((err, authResult) => {
|
|
||||||
if (authResult && authResult.accessToken && authResult.idToken) {
|
|
||||||
this.setSession(authResult);
|
|
||||||
} else if (err) {
|
|
||||||
this.logout();
|
|
||||||
this._router.navigate(['/']).then(r => window.location.reload()); // TODO: Remove this for the love of baby Jesus!
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public getToken(): string {
|
|
||||||
if (this.isAuthenticated()) return localStorage.getItem('id_token');
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
public renewToken() {
|
|
||||||
this.auth0.renewAuth(
|
|
||||||
{
|
|
||||||
audience: 'https://podnoms/',
|
|
||||||
redirectUri: `${environment.API_HOST}/silent`,
|
|
||||||
usePostMessage: true,
|
|
||||||
postMessageOrigin: environment.BASE_URL
|
|
||||||
},
|
|
||||||
(err, result) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
} else {
|
|
||||||
this.setSession(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
public scheduleRenewal() {
|
|
||||||
if (!this.isAuthenticated()) return;
|
|
||||||
this.unscheduleRenewal();
|
|
||||||
|
|
||||||
const expiresAt = JSON.parse(window.localStorage.getItem('expires_at'));
|
|
||||||
const source = Observable.of(expiresAt).flatMap(e => {
|
|
||||||
const now = Date.now();
|
|
||||||
return Observable.timer(Math.max(1, e - now));
|
|
||||||
});
|
|
||||||
this.refreshSubscription = source.subscribe(() => {
|
|
||||||
console.log('auth.service.ts', 'scheduleRenewal', 'Starting renewal and schedule');
|
|
||||||
this.renewToken();
|
|
||||||
this.scheduleRenewal();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public unscheduleRenewal() {
|
|
||||||
if (!this.refreshSubscription) return;
|
|
||||||
this.refreshSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
private setSession(authResult): void {
|
|
||||||
const expiresAt = JSON.stringify(authResult.expiresIn * 1000 + new Date().getTime());
|
|
||||||
localStorage.setItem('access_token', authResult.accessToken);
|
|
||||||
localStorage.setItem('id_token', authResult.idToken);
|
|
||||||
localStorage.setItem('expires_at', expiresAt);
|
|
||||||
this.scheduleRenewal();
|
|
||||||
this._router.navigate(['/']);
|
|
||||||
}
|
|
||||||
public logout(): void {
|
|
||||||
localStorage.removeItem('access_token');
|
|
||||||
localStorage.removeItem('id_token');
|
|
||||||
localStorage.removeItem('expires_at');
|
|
||||||
|
|
||||||
this._router.navigate(['/']);
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
public isAuthenticated(): boolean {
|
|
||||||
const expiresAt = JSON.parse(localStorage.getItem('expires_at'));
|
|
||||||
return new Date().getTime() < expiresAt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
import { AuthHttp } from 'angular2-jwt';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ChatterService {
|
export class ChatterService {
|
||||||
constructor(private _http: AuthHttp) {}
|
constructor(private _http: HttpClient) {}
|
||||||
|
|
||||||
ping(message: string): any {
|
ping(message: string): any {
|
||||||
return this._http.post(
|
return this._http.post(
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { AuthHttp } from 'angular2-jwt';
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DebugService {
|
export class DebugService {
|
||||||
constructor(private _http: AuthHttp) {}
|
constructor(private _http: HttpClient) {}
|
||||||
|
|
||||||
sendRealtime(message: string): any {
|
sendRealtime(message: string): any {
|
||||||
return this._http.post(environment.API_HOST + '/debug/realtime', JSON.stringify(message));
|
return this._http.post(
|
||||||
|
environment.API_HOST + '/debug/realtime',
|
||||||
|
JSON.stringify(message)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDebugInfo(): Observable<string> {
|
getDebugInfo(): Observable<string> {
|
||||||
return this._http.get(environment.API_HOST + '/debug').map(r => r.json());
|
return this._http.get<string>(environment.API_HOST + '/debug');
|
||||||
}
|
}
|
||||||
|
|
||||||
ping(): Observable<string> {
|
ping(): Observable<string> {
|
||||||
return this._http.get(environment.API_HOST + '/ping').map(r => r.text());
|
return this._http.get<string>(environment.API_HOST + '/ping');
|
||||||
}
|
}
|
||||||
sendPush(): Observable<string>{
|
sendPush(): Observable<string> {
|
||||||
return this._http.get(environment.API_HOST + '/debug/serverpush').map(r => r.text());
|
return this._http.get<string>(
|
||||||
|
environment.API_HOST + '/debug/serverpush'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Http } from '@angular/http';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EntriesService {
|
export class EntriesService {
|
||||||
constructor(private http: Http) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
get(): Observable<any> {
|
get(): Observable<any> {
|
||||||
return this.http.get('https://api.com');
|
return this.http.get('https://api.com');
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import {Http, Headers} from '@angular/http';
|
import {Http, Headers} from '@angular/http';
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {AuthService} from './auth.service';
|
import {PodnomsAuthService} from './podnoms-auth.service';
|
||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImageService {
|
export class ImageService {
|
||||||
|
|
||||||
// TODO: Change this to use AuthHttp when I can figure out why formData is null
|
constructor(private _http: Http, private _auth: PodnomsAuthService) {
|
||||||
constructor(private _http: Http, private _auth: AuthService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upload(podcastSlug: string, image) {
|
upload(podcastSlug: string, image) {
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Response } from '@angular/http';
|
import { Response } from '@angular/http';
|
||||||
import { AuthHttp } from 'angular2-jwt';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JobsService {
|
export class JobsService {
|
||||||
constructor(private _http: AuthHttp) { }
|
constructor(private _http: HttpClient) {}
|
||||||
|
|
||||||
processOrphans(): Observable<Response> {
|
processOrphans(): Observable<Response> {
|
||||||
return this._http.get(environment.API_HOST + '/job/processorphans');
|
return this._http.get<Response>(
|
||||||
|
environment.API_HOST + '/job/processorphans'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPlaylists(): Observable<Response> {
|
processPlaylists(): Observable<Response> {
|
||||||
return this._http.get(environment.API_HOST + '/job/processplaylists');
|
return this._http.get<Response>(
|
||||||
|
environment.API_HOST + '/job/processplaylists'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
updateYouTubeDl(): Observable<Response> {
|
updateYouTubeDl(): Observable<Response> {
|
||||||
return this._http.get(environment.API_HOST + '/job/updateyoutubedl');
|
return this._http.get<Response>(
|
||||||
|
environment.API_HOST + '/job/updateyoutubedl'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
import { PodcastEntryModel } from 'app/models/podcasts.models';
|
import { PodcastEntryModel } from 'app/models/podcasts.models';
|
||||||
import { PodcastModel } from './../models/podcasts.models';
|
import { PodcastModel } from './../models/podcasts.models';
|
||||||
import { AuthHttp } from 'angular2-jwt';
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PodcastService {
|
export class PodcastService {
|
||||||
@@ -13,24 +13,25 @@ export class PodcastService {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
constructor(private _http: AuthHttp) { }
|
constructor(private _http: HttpClient) {}
|
||||||
//#region Podcasts
|
//#region Podcasts
|
||||||
get(): Observable<PodcastModel[]> {
|
get(): Observable<PodcastModel[]> {
|
||||||
return this._http
|
return this._http.get<PodcastModel[]>(
|
||||||
.get(environment.API_HOST + '/podcast/')
|
environment.API_HOST + '/podcast/'
|
||||||
.map(res => res.json());
|
);
|
||||||
}
|
}
|
||||||
getPodcast(slug: string): Observable<PodcastModel> {
|
getPodcast(slug: string): Observable<PodcastModel> {
|
||||||
return this._http
|
return this._http.get<PodcastModel>(
|
||||||
.get(environment.API_HOST + '/podcast/' + slug)
|
environment.API_HOST + '/podcast/' + slug
|
||||||
.map(res => res.json());
|
);
|
||||||
}
|
}
|
||||||
addPodcast(podcast: PodcastModel): Observable<PodcastModel> {
|
addPodcast(podcast: PodcastModel): Observable<PodcastModel> {
|
||||||
console.log('PodcastService', 'addPodcast', podcast);
|
console.log('PodcastService', 'addPodcast', podcast);
|
||||||
const data = JSON.stringify(podcast, PodcastService._replacer);
|
const data = JSON.stringify(podcast, PodcastService._replacer);
|
||||||
return this._http
|
return this._http.post<PodcastModel>(
|
||||||
.post(environment.API_HOST + '/podcast', data)
|
environment.API_HOST + '/podcast',
|
||||||
.map(res => res.json());
|
data
|
||||||
|
);
|
||||||
}
|
}
|
||||||
updatePodcast(podcast: PodcastModel) {
|
updatePodcast(podcast: PodcastModel) {
|
||||||
return this._http.put(environment.API_HOST + '/podcast/', podcast);
|
return this._http.put(environment.API_HOST + '/podcast/', podcast);
|
||||||
@@ -41,43 +42,48 @@ export class PodcastService {
|
|||||||
//#endregion
|
//#endregion
|
||||||
//#region Entries
|
//#region Entries
|
||||||
getEntries(slug: string): any {
|
getEntries(slug: string): any {
|
||||||
return this._http
|
return this._http.get(environment.API_HOST + '/entry/all/' + slug);
|
||||||
.get(environment.API_HOST + '/entry/all/' + slug)
|
|
||||||
.map(res => res.json());
|
|
||||||
}
|
}
|
||||||
addEntry(entry: PodcastEntryModel) {
|
addEntry(entry: PodcastEntryModel): Observable<PodcastEntryModel> {
|
||||||
return this._http
|
return this._http.post<PodcastEntryModel>(
|
||||||
.post(environment.API_HOST + '/entry', JSON.stringify(entry))
|
environment.API_HOST + '/entry',
|
||||||
.map(res => res.json());
|
JSON.stringify(entry)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
updateEntry(entry: PodcastEntryModel) {
|
updateEntry(entry: PodcastEntryModel) {
|
||||||
return this._http
|
return this._http.post<PodcastEntryModel>(
|
||||||
.post(environment.API_HOST + '/entry', JSON.stringify(entry))
|
environment.API_HOST + '/entry',
|
||||||
.map(res => res.json());
|
JSON.stringify(entry)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
deleteEntry(id: number) {
|
deleteEntry(id: number) {
|
||||||
return this._http.delete(environment.API_HOST + '/entry/' + id);
|
return this._http.delete(environment.API_HOST + '/entry/' + id);
|
||||||
}
|
}
|
||||||
checkEntry(url: string): Observable<boolean> {
|
checkEntry(url: string): Observable<boolean> {
|
||||||
return this._http
|
return this._http
|
||||||
.post(environment.API_HOST + '/entry/isvalid/', `"${url}"`)
|
.post<Response>(
|
||||||
.map(r => (r.status == 200 ? true : false))
|
environment.API_HOST + '/entry/isvalid/',
|
||||||
|
`"${url}"`
|
||||||
|
)
|
||||||
|
.map((r) => (r.status == 200 ? true : false))
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
return Observable.throw(new Error(error.status));
|
return Observable.throw(new Error(error.status));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
reSubmitEntry(entry: PodcastEntryModel): Observable<PodcastEntryModel> {
|
reSubmitEntry(entry: PodcastEntryModel): Observable<PodcastEntryModel> {
|
||||||
return this._http
|
return this._http.post<PodcastEntryModel>(
|
||||||
.post(environment.API_HOST + '/entry/resubmit', entry)
|
environment.API_HOST + '/entry/resubmit',
|
||||||
.map(res => res.json());
|
entry
|
||||||
|
);
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Playlists
|
//#region Playlists
|
||||||
addPlaylist(entry: PodcastEntryModel) {
|
addPlaylist(entry: PodcastEntryModel) {
|
||||||
return this._http
|
return this._http.post<PodcastEntryModel>(
|
||||||
.post(environment.API_HOST + '/playlist', JSON.stringify(entry))
|
environment.API_HOST + '/playlist',
|
||||||
.map(res => res.json());
|
JSON.stringify(entry)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,21 @@ import { environment } from 'environments/environment';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { ProfileModel } from 'app/models/profile.model';
|
import { ProfileModel } from 'app/models/profile.model';
|
||||||
import { AuthHttp } from 'angular2-jwt';
|
|
||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/map';
|
||||||
import { Profile } from 'selenium-webdriver/firefox';
|
import { Profile } from 'selenium-webdriver/firefox';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProfileService {
|
export class ProfileService {
|
||||||
constructor(private _http: AuthHttp) {}
|
|
||||||
profile: ProfileModel;
|
profile: ProfileModel;
|
||||||
|
constructor(private _http: HttpClient) {}
|
||||||
|
|
||||||
getProfile(): Observable<ProfileModel> {
|
getProfile(): Observable<ProfileModel> {
|
||||||
if (!this.profile) {
|
if (!this.profile) {
|
||||||
return this._http
|
return this._http
|
||||||
.get(environment.API_HOST + '/profile')
|
.get<ProfileModel>(environment.API_HOST + '/profile')
|
||||||
.map(res => {
|
.map((res) => {
|
||||||
this.profile = res.json();
|
this.profile = res;
|
||||||
return this.profile;
|
return this.profile;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -26,20 +26,22 @@ export class ProfileService {
|
|||||||
|
|
||||||
updateProfile(profile): Observable<ProfileModel> {
|
updateProfile(profile): Observable<ProfileModel> {
|
||||||
console.log('ProfileService', 'updateProfile', profile);
|
console.log('ProfileService', 'updateProfile', profile);
|
||||||
return this._http
|
return this._http.post<ProfileModel>(
|
||||||
.post(environment.API_HOST + '/profile', profile)
|
environment.API_HOST + '/profile',
|
||||||
.map(res => res.json());
|
profile
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSlug(slug): Observable<string> {
|
checkSlug(slug): Observable<string> {
|
||||||
console.log('profile.service.ts', 'checkSlug', slug);
|
console.log('profile.service.ts', 'checkSlug', slug);
|
||||||
return this._http
|
return this._http.get<string>(
|
||||||
.get(environment.API_HOST + '/profile/checkslug/' + slug)
|
environment.API_HOST + '/profile/checkslug/' + slug
|
||||||
.map(res => res.text());
|
);
|
||||||
}
|
}
|
||||||
regenerateApiKey(): Observable<string> {
|
regenerateApiKey(): Observable<string> {
|
||||||
return this._http
|
return this._http.post<string>(
|
||||||
.post(environment.API_HOST + '/profile/updateapikey', null)
|
environment.API_HOST + '/profile/updateapikey',
|
||||||
.map(res => res.text());
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AuthHttp } from 'angular2-jwt';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import 'rxjs/add/operator/catch';
|
import 'rxjs/add/operator/catch';
|
||||||
@@ -7,11 +6,12 @@ import 'rxjs/add/operator/map';
|
|||||||
import 'rxjs/add/observable/throw';
|
import 'rxjs/add/observable/throw';
|
||||||
|
|
||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PushRegistrationService {
|
export class PushRegistrationService {
|
||||||
private API_URL: string;
|
private API_URL: string;
|
||||||
constructor(private http: AuthHttp) {
|
constructor(private _http: HttpClient) {
|
||||||
this.API_URL = environment.API_HOST;
|
this.API_URL = environment.API_HOST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export class PushRegistrationService {
|
|||||||
|
|
||||||
addSubscriber(subscription) {
|
addSubscriber(subscription) {
|
||||||
const url = `${this.API_URL}/webpush/subscribe`;
|
const url = `${this.API_URL}/webpush/subscribe`;
|
||||||
return this.http.post(url, subscription).catch(this.handleError);
|
return this._http.post(url, subscription).catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSubscriber(subscription) {
|
deleteSubscriber(subscription) {
|
||||||
@@ -40,7 +40,9 @@ export class PushRegistrationService {
|
|||||||
subscription: subscription
|
subscription: subscription
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.http.post(url, body).catch(this.handleError);
|
return this._http
|
||||||
|
.post(url, JSON.stringify(body))
|
||||||
|
.catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleError(error: Response | any) {
|
private handleError(error: Response | any) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AuthService } from './auth.service';
|
import { PodnomsAuthService } from './podnoms-auth.service';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
HubConnection,
|
HubConnection,
|
||||||
@@ -12,7 +12,7 @@ import { environment } from 'environments/environment';
|
|||||||
export class SignalRService {
|
export class SignalRService {
|
||||||
public connection: HubConnection;
|
public connection: HubConnection;
|
||||||
|
|
||||||
constructor(private _auth: AuthService) {}
|
constructor(private _auth: PodnomsAuthService) {}
|
||||||
public init(hub: string): Promise<void> {
|
public init(hub: string): Promise<void> {
|
||||||
const url = `${environment.SIGNALR_HOST}/hubs/${hub}`;
|
const url = `${environment.SIGNALR_HOST}/hubs/${hub}`;
|
||||||
|
|
||||||
|
|||||||
36
server/Controllers/AccountsController.cs
Normal file
36
server/Controllers/AccountsController.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PodNoms.Api.Models.ViewModels;
|
||||||
|
using PodNoms.Api.Persistence;
|
||||||
|
using PodNoms.Api.Services.Auth;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Controllers {
|
||||||
|
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class AccountsController : Controller {
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
public AccountsController(IUserRepository userRepository, UserManager<ApplicationUser> userManager, IMapper mapper) {
|
||||||
|
this._userRepository = userRepository;
|
||||||
|
this._userManager = userManager;
|
||||||
|
this._mapper = mapper;
|
||||||
|
}
|
||||||
|
// POST api/accounts
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Post([FromBody]RegistrationViewModel model) {
|
||||||
|
if (!ModelState.IsValid) {
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
var userIdentity = _mapper.Map<RegistrationViewModel, ApplicationUser>(model);
|
||||||
|
var result = await _userManager.CreateAsync(userIdentity, model.Password);
|
||||||
|
// var result = await _userRepository.AddOrUpdate(userIdentity, model.Password);
|
||||||
|
|
||||||
|
if (!result.Succeeded) return new BadRequestObjectResult(result);
|
||||||
|
return new OkObjectResult(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +1,60 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using PodNoms.Api.Models;
|
using PodNoms.Api.Models;
|
||||||
using PodNoms.Api.Persistence;
|
using PodNoms.Api.Models.ViewModels;
|
||||||
|
using PodNoms.Api.Services.Auth;
|
||||||
|
using PodNoms.Api.Utils;
|
||||||
|
|
||||||
namespace PodNoms.Api.Controllers {
|
namespace PodNoms.Api.Controllers {
|
||||||
[Authorize]
|
[Route("[controller]")]
|
||||||
public class AuthController : Controller {
|
public class AuthController : Controller {
|
||||||
protected IUserRepository _userRepository { get; }
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly IJwtFactory _jwtFactory;
|
||||||
|
private readonly JwtIssuerOptions _jwtOptions;
|
||||||
|
|
||||||
public AuthController(IUserRepository repository) {
|
public AuthController(UserManager<ApplicationUser> userManager, IJwtFactory jwtFactory, IOptions<JwtIssuerOptions> jwtOptions) {
|
||||||
this._userRepository = repository;
|
_userManager = userManager;
|
||||||
|
_jwtFactory = jwtFactory;
|
||||||
|
_jwtOptions = jwtOptions.Value;
|
||||||
}
|
}
|
||||||
protected async Task<User> GetUserAsync() {
|
|
||||||
var identifier = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
|
// POST api/auth/login
|
||||||
var user = await this._userRepository.GetAsync(identifier);
|
[HttpPost("login")]
|
||||||
return user;
|
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials) {
|
||||||
|
if (!ModelState.IsValid) {
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
|
||||||
|
if (identity == null) {
|
||||||
|
return BadRequest(Errors.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwt = await Tokens.GenerateJwt(identity, _jwtFactory, credentials.UserName, _jwtOptions,
|
||||||
|
new JsonSerializerSettings { Formatting = Formatting.Indented });
|
||||||
|
return new OkObjectResult(jwt);
|
||||||
}
|
}
|
||||||
protected async Task<string> GetUserUidAsync() {
|
|
||||||
var user = await GetUserAsync();
|
private async Task<ClaimsIdentity> GetClaimsIdentity(string userName, string password) {
|
||||||
return user.Uid;
|
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
|
||||||
}
|
return await Task.FromResult<ClaimsIdentity>(null);
|
||||||
protected async Task<int> GetUserIdAsync() {
|
|
||||||
var user = await GetUserAsync();
|
// get the user to verifty
|
||||||
return user.Id;
|
var userToVerify = await _userManager.FindByNameAsync(userName);
|
||||||
|
|
||||||
|
if (userToVerify == null) return await Task.FromResult<ClaimsIdentity>(null);
|
||||||
|
|
||||||
|
// check the credentials
|
||||||
|
if (await _userManager.CheckPasswordAsync(userToVerify, password)) {
|
||||||
|
return await Task.FromResult(_jwtFactory.GenerateClaimsIdentity(userName, userToVerify.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credentials are invalid, or account doesn't exist
|
||||||
|
return await Task.FromResult<ClaimsIdentity>(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ using WebPush = Lib.Net.Http.WebPush;
|
|||||||
|
|
||||||
namespace PodNoms.Api.Controllers {
|
namespace PodNoms.Api.Controllers {
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
public class DebugController : AuthController {
|
public class DebugController : UserController {
|
||||||
private readonly StorageSettings _storageSettings;
|
private readonly StorageSettings _storageSettings;
|
||||||
private readonly AudioFileStorageSettings _audioFileStorageSettings;
|
private readonly AudioFileStorageSettings _audioFileStorageSettings;
|
||||||
private readonly ApplicationsSettings _applicationsSettings;
|
private readonly ApplicationsSettings _applicationsSettings;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ using PodNoms.Api.Services.Storage;
|
|||||||
namespace PodNoms.Api.Controllers {
|
namespace PodNoms.Api.Controllers {
|
||||||
|
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
public class EntryController : AuthController {
|
public class EntryController : UserController {
|
||||||
private readonly IPodcastRepository _podcastRepository;
|
private readonly IPodcastRepository _podcastRepository;
|
||||||
private readonly IEntryRepository _repository;
|
private readonly IEntryRepository _repository;
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using PodNoms.Api.Models;
|
using PodNoms.Api.Models;
|
||||||
@@ -24,10 +25,12 @@ namespace PodNoms.Api.Controllers {
|
|||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IOptions<AppSettings> _settings;
|
private readonly IOptions<AppSettings> _settings;
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
private ClaimsPrincipal _caller;
|
||||||
private readonly IUnitOfWork _uow;
|
private readonly IUnitOfWork _uow;
|
||||||
|
|
||||||
public PodcastController(IPodcastRepository repository, IUserRepository userRepository,
|
public PodcastController(IPodcastRepository repository, IUserRepository userRepository,
|
||||||
IOptions<AppSettings> options, IMapper mapper, IUnitOfWork unitOfWork) {
|
IOptions<AppSettings> options, IMapper mapper, IUnitOfWork unitOfWork, IHttpContextAccessor httpContextAccessor) {
|
||||||
|
_caller = httpContextAccessor.HttpContext.User;
|
||||||
this._uow = unitOfWork;
|
this._uow = unitOfWork;
|
||||||
this._repository = repository;
|
this._repository = repository;
|
||||||
this._userRepository = userRepository;
|
this._userRepository = userRepository;
|
||||||
@@ -37,12 +40,10 @@ namespace PodNoms.Api.Controllers {
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IEnumerable<PodcastViewModel>> Get() {
|
public async Task<IEnumerable<PodcastViewModel>> Get() {
|
||||||
var email = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
|
var userId = _caller.Claims.Single(c => c.Type == "id");
|
||||||
if (!string.IsNullOrEmpty(email)) {
|
var podcasts = await _repository.GetAllAsync(userId.Value);
|
||||||
var podcasts = await _repository.GetAllAsync(email);
|
var ret = _mapper.Map<List<Podcast>, List<PodcastViewModel>>(podcasts.ToList());
|
||||||
var ret = _mapper.Map<List<Podcast>, List<PodcastViewModel>>(podcasts.ToList());
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
throw new Exception("No local user stored!");
|
throw new Exception("No local user stored!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,9 +61,9 @@ namespace PodNoms.Api.Controllers {
|
|||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Post([FromBody] PodcastViewModel vm) {
|
public async Task<IActionResult> Post([FromBody] PodcastViewModel vm) {
|
||||||
var email = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
|
var userId = _caller.Claims.Single(c => c.Type == "id");
|
||||||
var user = _userRepository.Get(email);
|
var user = _userRepository.Get(userId.Value);
|
||||||
if (string.IsNullOrEmpty(email) || user == null)
|
if (user == null)
|
||||||
return new BadRequestObjectResult("Unable to look up user profile");
|
return new BadRequestObjectResult("Unable to look up user profile");
|
||||||
|
|
||||||
if (ModelState.IsValid) {
|
if (ModelState.IsValid) {
|
||||||
|
|||||||
33
server/Controllers/UserController.cs
Normal file
33
server/Controllers/UserController.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PodNoms.Api.Models;
|
||||||
|
using PodNoms.Api.Persistence;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Controllers {
|
||||||
|
[Authorize]
|
||||||
|
[Obsolete("This should be superceded by the new identity stuff")]
|
||||||
|
public class UserController : Controller {
|
||||||
|
protected IUserRepository _userRepository { get; }
|
||||||
|
|
||||||
|
public UserController(IUserRepository repository) {
|
||||||
|
this._userRepository = repository;
|
||||||
|
}
|
||||||
|
protected async Task<User> GetUserAsync() {
|
||||||
|
var identifier = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
|
||||||
|
var user = await this._userRepository.GetAsync(identifier);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
protected async Task<string> GetUserUidAsync() {
|
||||||
|
var user = await GetUserAsync();
|
||||||
|
return user.Uid;
|
||||||
|
}
|
||||||
|
protected async Task<int> GetUserIdAsync() {
|
||||||
|
var user = await GetUserAsync();
|
||||||
|
return user.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ namespace PodNoms.Api.Controllers {
|
|||||||
|
|
||||||
// [Authorize]
|
// [Authorize]
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
public class WebPushController : AuthController {
|
public class WebPushController : UserController {
|
||||||
private readonly IPushSubscriptionStore _subscriptionStore;
|
private readonly IPushSubscriptionStore _subscriptionStore;
|
||||||
public readonly IPushNotificationService _notificationService;
|
public readonly IPushNotificationService _notificationService;
|
||||||
|
|
||||||
|
|||||||
194
server/Migrations/20180421161830_AddedAuthTables.Designer.cs
generated
Normal file
194
server/Migrations/20180421161830_AddedAuthTables.Designer.cs
generated
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using PodNoms.Api.Persistence;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PodnomsDbContext))]
|
||||||
|
[Migration("20180421161830_AddedAuthTables")]
|
||||||
|
partial class AddedAuthTables
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.1.0-preview2-30571")
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("Playlists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsUnicode(true);
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Podcasts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<long>("AudioFileSize");
|
||||||
|
|
||||||
|
b.Property<float>("AudioLength");
|
||||||
|
|
||||||
|
b.Property<string>("AudioUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Author");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<int?>("PlaylistId");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<bool>("Processed");
|
||||||
|
|
||||||
|
b.Property<string>("ProcessingPayload");
|
||||||
|
|
||||||
|
b.Property<int>("ProcessingStatus");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlaylistId");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("PodcastEntries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ApiKey")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<string>("EmailAddress")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("ProfileImage");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken");
|
||||||
|
|
||||||
|
b.Property<string>("Sid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Slug")
|
||||||
|
.IsUnique()
|
||||||
|
.HasFilter("[Slug] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Playlist")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PlaylistId");
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
server/Migrations/20180421161830_AddedAuthTables.cs
Normal file
19
server/Migrations/20180421161830_AddedAuthTables.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
public partial class AddedAuthTables : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
406
server/Migrations/20180421162833_ChangeContextType.Designer.cs
generated
Normal file
406
server/Migrations/20180421162833_ChangeContextType.Designer.cs
generated
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using PodNoms.Api.Persistence;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PodnomsDbContext))]
|
||||||
|
[Migration("20180421162833_ChangeContextType")]
|
||||||
|
partial class ChangeContextType
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.1.0-preview2-30571")
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("Playlists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsUnicode(true);
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Podcasts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<long>("AudioFileSize");
|
||||||
|
|
||||||
|
b.Property<float>("AudioLength");
|
||||||
|
|
||||||
|
b.Property<string>("AudioUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Author");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<int?>("PlaylistId");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<bool>("Processed");
|
||||||
|
|
||||||
|
b.Property<string>("ProcessingPayload");
|
||||||
|
|
||||||
|
b.Property<int>("ProcessingStatus");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlaylistId");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("PodcastEntries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ApiKey")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<string>("EmailAddress")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("ProfileImage");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken");
|
||||||
|
|
||||||
|
b.Property<string>("Sid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Slug")
|
||||||
|
.IsUnique()
|
||||||
|
.HasFilter("[Slug] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Services.Auth.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<long?>("FacebookId");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName");
|
||||||
|
|
||||||
|
b.Property<string>("LastName");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("PictureUrl");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Playlist")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PlaylistId");
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
225
server/Migrations/20180421162833_ChangeContextType.cs
Normal file
225
server/Migrations/20180421162833_ChangeContextType.cs
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
public partial class ChangeContextType : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(nullable: false),
|
||||||
|
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUsers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(nullable: false),
|
||||||
|
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||||
|
PasswordHash = table.Column<string>(nullable: true),
|
||||||
|
SecurityStamp = table.Column<string>(nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(nullable: true),
|
||||||
|
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||||
|
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||||
|
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||||
|
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||||
|
AccessFailedCount = table.Column<int>(nullable: false),
|
||||||
|
FirstName = table.Column<string>(nullable: true),
|
||||||
|
LastName = table.Column<string>(nullable: true),
|
||||||
|
FacebookId = table.Column<long>(nullable: true),
|
||||||
|
PictureUrl = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoleClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
RoleId = table.Column<string>(nullable: false),
|
||||||
|
ClaimType = table.Column<string>(nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
UserId = table.Column<string>(nullable: false),
|
||||||
|
ClaimType = table.Column<string>(nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserLogins",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
LoginProvider = table.Column<string>(nullable: false),
|
||||||
|
ProviderKey = table.Column<string>(nullable: false),
|
||||||
|
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||||
|
UserId = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(nullable: false),
|
||||||
|
RoleId = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserTokens",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(nullable: false),
|
||||||
|
LoginProvider = table.Column<string>(nullable: false),
|
||||||
|
Name = table.Column<string>(nullable: false),
|
||||||
|
Value = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetRoleClaims_RoleId",
|
||||||
|
table: "AspNetRoleClaims",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles",
|
||||||
|
column: "NormalizedName",
|
||||||
|
unique: true,
|
||||||
|
filter: "[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserClaims_UserId",
|
||||||
|
table: "AspNetUserClaims",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserLogins_UserId",
|
||||||
|
table: "AspNetUserLogins",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserRoles_RoleId",
|
||||||
|
table: "AspNetUserRoles",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "EmailIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedEmail");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedUserName",
|
||||||
|
unique: true,
|
||||||
|
filter: "[NormalizedUserName] IS NOT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoleClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserLogins");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserTokens");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
406
server/Migrations/20180421215724_RenameUserTable.Designer.cs
generated
Normal file
406
server/Migrations/20180421215724_RenameUserTable.Designer.cs
generated
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using PodNoms.Api.Persistence;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PodnomsDbContext))]
|
||||||
|
[Migration("20180421215724_RenameUserTable")]
|
||||||
|
partial class RenameUserTable
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.1.0-preview2-30571")
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("Playlists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsUnicode(true);
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Podcasts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<long>("AudioFileSize");
|
||||||
|
|
||||||
|
b.Property<float>("AudioLength");
|
||||||
|
|
||||||
|
b.Property<string>("AudioUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Author");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<int?>("PlaylistId");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<bool>("Processed");
|
||||||
|
|
||||||
|
b.Property<string>("ProcessingPayload");
|
||||||
|
|
||||||
|
b.Property<int>("ProcessingStatus");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlaylistId");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("PodcastEntries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ApiKey")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<string>("EmailAddress")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("ProfileImage");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken");
|
||||||
|
|
||||||
|
b.Property<string>("Sid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Slug")
|
||||||
|
.IsUnique()
|
||||||
|
.HasFilter("[Slug] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("UserDetails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Services.Auth.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<long?>("FacebookId");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName");
|
||||||
|
|
||||||
|
b.Property<string>("LastName");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("PictureUrl");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Playlist")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PlaylistId");
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
server/Migrations/20180421215724_RenameUserTable.cs
Normal file
75
server/Migrations/20180421215724_RenameUserTable.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
public partial class RenameUserTable : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Podcasts_Users_UserId",
|
||||||
|
table: "Podcasts");
|
||||||
|
|
||||||
|
migrationBuilder.DropPrimaryKey(
|
||||||
|
name: "PK_Users",
|
||||||
|
table: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.RenameTable(
|
||||||
|
name: "Users",
|
||||||
|
newName: "UserDetails");
|
||||||
|
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_Users_Slug",
|
||||||
|
table: "UserDetails",
|
||||||
|
newName: "IX_UserDetails_Slug");
|
||||||
|
|
||||||
|
migrationBuilder.AddPrimaryKey(
|
||||||
|
name: "PK_UserDetails",
|
||||||
|
table: "UserDetails",
|
||||||
|
column: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Podcasts_UserDetails_UserId",
|
||||||
|
table: "Podcasts",
|
||||||
|
column: "UserId",
|
||||||
|
principalTable: "UserDetails",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Podcasts_UserDetails_UserId",
|
||||||
|
table: "Podcasts");
|
||||||
|
|
||||||
|
migrationBuilder.DropPrimaryKey(
|
||||||
|
name: "PK_UserDetails",
|
||||||
|
table: "UserDetails");
|
||||||
|
|
||||||
|
migrationBuilder.RenameTable(
|
||||||
|
name: "UserDetails",
|
||||||
|
newName: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_UserDetails_Slug",
|
||||||
|
table: "Users",
|
||||||
|
newName: "IX_Users_Slug");
|
||||||
|
|
||||||
|
migrationBuilder.AddPrimaryKey(
|
||||||
|
name: "PK_Users",
|
||||||
|
table: "Users",
|
||||||
|
column: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Podcasts_Users_UserId",
|
||||||
|
table: "Podcasts",
|
||||||
|
column: "UserId",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
414
server/Migrations/20180422013049_JiggleUserModel.Designer.cs
generated
Normal file
414
server/Migrations/20180422013049_JiggleUserModel.Designer.cs
generated
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using PodNoms.Api.Persistence;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PodnomsDbContext))]
|
||||||
|
[Migration("20180422013049_JiggleUserModel")]
|
||||||
|
partial class JiggleUserModel
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.1.0-preview2-30571")
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("Playlists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("AppUserId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsUnicode(true);
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AppUserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Podcasts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<long>("AudioFileSize");
|
||||||
|
|
||||||
|
b.Property<float>("AudioLength");
|
||||||
|
|
||||||
|
b.Property<string>("AudioUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Author");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("getdate()");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<int?>("PlaylistId");
|
||||||
|
|
||||||
|
b.Property<int>("PodcastId");
|
||||||
|
|
||||||
|
b.Property<bool>("Processed");
|
||||||
|
|
||||||
|
b.Property<string>("ProcessingPayload");
|
||||||
|
|
||||||
|
b.Property<int>("ProcessingStatus");
|
||||||
|
|
||||||
|
b.Property<string>("SourceUrl");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlaylistId");
|
||||||
|
|
||||||
|
b.HasIndex("PodcastId");
|
||||||
|
|
||||||
|
b.ToTable("PodcastEntries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ApiKey")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateDate");
|
||||||
|
|
||||||
|
b.Property<string>("EmailAddress")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("FullName")
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
b.Property<string>("ProfileImage");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken");
|
||||||
|
|
||||||
|
b.Property<string>("Sid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdateDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Slug")
|
||||||
|
.IsUnique()
|
||||||
|
.HasFilter("[Slug] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("UserDetails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Services.Auth.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<long?>("FacebookId");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName");
|
||||||
|
|
||||||
|
b.Property<string>("LastName");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("PictureUrl");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "AppUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AppUserId");
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Models.Playlist")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PlaylistId");
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
|
||||||
|
.WithMany("PodcastEntries")
|
||||||
|
.HasForeignKey("PodcastId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
server/Migrations/20180422013049_JiggleUserModel.cs
Normal file
45
server/Migrations/20180422013049_JiggleUserModel.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Migrations
|
||||||
|
{
|
||||||
|
public partial class JiggleUserModel : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "AppUserId",
|
||||||
|
table: "Podcasts",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Podcasts_AppUserId",
|
||||||
|
table: "Podcasts",
|
||||||
|
column: "AppUserId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Podcasts_AspNetUsers_AppUserId",
|
||||||
|
table: "Podcasts",
|
||||||
|
column: "AppUserId",
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Podcasts_AspNetUsers_AppUserId",
|
||||||
|
table: "Podcasts");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Podcasts_AppUserId",
|
||||||
|
table: "Podcasts");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AppUserId",
|
||||||
|
table: "Podcasts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
|||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
|
||||||
using PodNoms.Api.Models;
|
|
||||||
using PodNoms.Api.Persistence;
|
using PodNoms.Api.Persistence;
|
||||||
|
|
||||||
namespace PodNoms.Api.Migrations
|
namespace PodNoms.Api.Migrations
|
||||||
@@ -19,9 +16,117 @@ namespace PodNoms.Api.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "2.1.0-preview1-28290")
|
.HasAnnotation("ProductVersion", "2.1.0-preview2-30571")
|
||||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -47,6 +152,8 @@ namespace PodNoms.Api.Migrations
|
|||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd();
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("AppUserId");
|
||||||
|
|
||||||
b.Property<DateTime>("CreateDate")
|
b.Property<DateTime>("CreateDate")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasDefaultValueSql("getdate()");
|
.HasDefaultValueSql("getdate()");
|
||||||
@@ -68,6 +175,8 @@ namespace PodNoms.Api.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AppUserId");
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("Podcasts");
|
b.ToTable("Podcasts");
|
||||||
@@ -161,7 +270,111 @@ namespace PodNoms.Api.Migrations
|
|||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasFilter("[Slug] IS NOT NULL");
|
.HasFilter("[Slug] IS NOT NULL");
|
||||||
|
|
||||||
b.ToTable("Users");
|
b.ToTable("UserDetails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PodNoms.Api.Services.Auth.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<long?>("FacebookId");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName");
|
||||||
|
|
||||||
|
b.Property<string>("LastName");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("PictureUrl");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
|
||||||
@@ -174,6 +387,10 @@ namespace PodNoms.Api.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
|
||||||
{
|
{
|
||||||
|
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "AppUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AppUserId");
|
||||||
|
|
||||||
b.HasOne("PodNoms.Api.Models.User", "User")
|
b.HasOne("PodNoms.Api.Models.User", "User")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId");
|
.HasForeignKey("UserId");
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
namespace PodNoms.Api.Models
|
namespace PodNoms.Api.Models {
|
||||||
{
|
public class AppSettings {
|
||||||
public class AppSettings
|
|
||||||
{
|
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
public string RssUrl { get; set; }
|
public string RssUrl { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StorageSettings
|
public class StorageSettings {
|
||||||
{
|
|
||||||
public string ConnectionString { get; set; }
|
public string ConnectionString { get; set; }
|
||||||
public string CdnUrl { get; set; }
|
public string CdnUrl { get; set; }
|
||||||
|
|
||||||
|
|||||||
56
server/Models/JwtIssuerOptions.cs
Normal file
56
server/Models/JwtIssuerOptions.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Models {
|
||||||
|
public class JwtIssuerOptions {
|
||||||
|
/// <summary>
|
||||||
|
/// 4.1.1. "iss" (Issuer) Claim - The "iss" (issuer) claim identifies the principal that issued the JWT.
|
||||||
|
/// </summary>
|
||||||
|
public string Issuer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 4.1.2. "sub" (Subject) Claim - The "sub" (subject) claim identifies the principal that is the subject of the JWT.
|
||||||
|
/// </summary>
|
||||||
|
public string Subject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 4.1.3. "aud" (Audience) Claim - The "aud" (audience) claim identifies the recipients that the JWT is intended for.
|
||||||
|
/// </summary>
|
||||||
|
public string Audience { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 4.1.4. "exp" (Expiration Time) Claim - The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime Expiration => IssuedAt.Add(ValidFor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 4.1.5. "nbf" (Not Before) Claim - The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime NotBefore => DateTime.UtcNow;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 4.1.6. "iat" (Issued At) Claim - The "iat" (issued at) claim identifies the time at which the JWT was issued.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime IssuedAt => DateTime.UtcNow;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the timespan the token will be valid for (default is 120 min)
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan ValidFor { get; set; } = TimeSpan.FromMinutes(120);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "jti" (JWT ID) Claim (default ID is a GUID)
|
||||||
|
/// </summary>
|
||||||
|
public Func<Task<string>> JtiGenerator =>
|
||||||
|
() => Task.FromResult(Guid.NewGuid().ToString());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The signing key to use when generating tokens.
|
||||||
|
/// </summary>
|
||||||
|
public SigningCredentials SigningCredentials { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using PodNoms.Api.Services.Auth;
|
||||||
|
|
||||||
namespace PodNoms.Api.Models {
|
namespace PodNoms.Api.Models {
|
||||||
public class Podcast : BaseModel {
|
public class Podcast : BaseModel {
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Uid { get; set; }
|
public string Uid { get; set; }
|
||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
|
public ApplicationUser AppUser { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string Slug { get; set; }
|
public string Slug { get; set; }
|
||||||
|
|||||||
8
server/Models/ViewModels/CredentialsViewModel.cs
Normal file
8
server/Models/ViewModels/CredentialsViewModel.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using FluentValidation.Attributes;
|
||||||
|
namespace PodNoms.Api.Models.ViewModels {
|
||||||
|
[Validator(typeof(CredentialsViewModelValidator))]
|
||||||
|
public class CredentialsViewModel {
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
server/Models/ViewModels/CredentialsViewModelValidator.cs
Normal file
11
server/Models/ViewModels/CredentialsViewModelValidator.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Models.ViewModels {
|
||||||
|
public class CredentialsViewModelValidator : AbstractValidator<CredentialsViewModel> {
|
||||||
|
public CredentialsViewModelValidator() {
|
||||||
|
RuleFor(vm => vm.UserName).NotEmpty().WithMessage("Username cannot be empty");
|
||||||
|
RuleFor(vm => vm.Password).NotEmpty().WithMessage("Password cannot be empty");
|
||||||
|
RuleFor(vm => vm.Password).Length(6, 12).WithMessage("Password must be between 6 and 12 characters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
server/Models/ViewModels/RegistrationViewModel.cs
Normal file
10
server/Models/ViewModels/RegistrationViewModel.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace PodNoms.Api.Models.ViewModels {
|
||||||
|
public class RegistrationViewModel {
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
public string LastName { get; set; }
|
||||||
|
public string Location { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,12 +3,11 @@ using PodNoms.Api.Models;
|
|||||||
|
|
||||||
namespace PodNoms.Api.Persistence {
|
namespace PodNoms.Api.Persistence {
|
||||||
public interface IUserRepository {
|
public interface IUserRepository {
|
||||||
User Get(int id);
|
User Get(string id);
|
||||||
User Get(string email);
|
Task<User> GetAsync(string id);
|
||||||
Task<User> GetAsync(string email);
|
|
||||||
Task<User> GetBySlugAsync(string slug);
|
Task<User> GetBySlugAsync(string slug);
|
||||||
User UpdateRegistration(string email, string name, string sid, string providerId, string profileImage, string refreshToken);
|
User UpdateRegistration(string email, string name, string sid, string providerId, string profileImage, string refreshToken);
|
||||||
string UpdateApiKey(User email);
|
string UpdateApiKey(User user);
|
||||||
User AddOrUpdate(User user);
|
User AddOrUpdate(User user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,10 +37,11 @@ namespace PodNoms.Api.Persistence {
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
public async Task<IEnumerable<Podcast>> GetAllAsync(string emailAddress) {
|
public async Task<IEnumerable<Podcast>> GetAllAsync(string userId) {
|
||||||
var ret = _context.Podcasts
|
var ret = _context.Podcasts
|
||||||
.Where(u => u.User.EmailAddress == emailAddress)
|
.Where(u => u.AppUser.Id == userId)
|
||||||
.Include(p => p.User)
|
.Include(p => p.User)
|
||||||
|
.Include(p => p.AppUser)
|
||||||
.OrderByDescending(p => p.Id);
|
.OrderByDescending(p => p.Id);
|
||||||
return await ret.ToListAsync();
|
return await ret.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Design;
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using PodNoms.Api.Models;
|
using PodNoms.Api.Models;
|
||||||
|
using PodNoms.Api.Services.Auth;
|
||||||
|
|
||||||
namespace PodNoms.Api.Persistence {
|
namespace PodNoms.Api.Persistence {
|
||||||
|
|
||||||
public class PodnomsDbContext : DbContext {
|
public class PodnomsDbContext : IdentityDbContext<ApplicationUser> {
|
||||||
public PodnomsDbContext(DbContextOptions<PodnomsDbContext> options) : base(options) { }
|
public PodnomsDbContext(DbContextOptions<PodnomsDbContext> options) : base(options) { }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
||||||
@@ -36,6 +38,6 @@ namespace PodNoms.Api.Persistence {
|
|||||||
public DbSet<Podcast> Podcasts { get; set; }
|
public DbSet<Podcast> Podcasts { get; set; }
|
||||||
public DbSet<PodcastEntry> PodcastEntries { get; set; }
|
public DbSet<PodcastEntry> PodcastEntries { get; set; }
|
||||||
public DbSet<Playlist> Playlists { get; set; }
|
public DbSet<Playlist> Playlists { get; set; }
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> UserDetails { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,19 +15,19 @@ namespace PodNoms.Api.Persistence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public User Get(int id) {
|
public User Get(int id) {
|
||||||
return _context.Users.FirstOrDefault(u => u.Id == id);
|
return _context.UserDetails.FirstOrDefault(u => u.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public User Get(string email) {
|
public User Get(string email) {
|
||||||
return _context.Users.FirstOrDefault(u => u.EmailAddress == email);
|
return _context.UserDetails.FirstOrDefault(u => u.EmailAddress == email);
|
||||||
}
|
}
|
||||||
public async Task<User> GetAsync(string email) {
|
public async Task<User> GetAsync(string email) {
|
||||||
return await _context.Users
|
return await _context.UserDetails
|
||||||
.Where(u => u.EmailAddress == email)
|
.Where(u => u.EmailAddress == email)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
public async Task<User> GetBySlugAsync(string slug) {
|
public async Task<User> GetBySlugAsync(string slug) {
|
||||||
var user = await _context.Users
|
var user = await _context.UserDetails
|
||||||
.Where(u => u.Slug == slug)
|
.Where(u => u.Slug == slug)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
@@ -36,9 +36,9 @@ namespace PodNoms.Api.Persistence {
|
|||||||
|
|
||||||
public User AddOrUpdate(User user) {
|
public User AddOrUpdate(User user) {
|
||||||
if (user.Id != 0) {
|
if (user.Id != 0) {
|
||||||
_context.Users.Attach(user);
|
_context.UserDetails.Attach(user);
|
||||||
} else {
|
} else {
|
||||||
_context.Users.Add(user);
|
_context.UserDetails.Add(user);
|
||||||
|
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
@@ -46,7 +46,7 @@ namespace PodNoms.Api.Persistence {
|
|||||||
|
|
||||||
public User UpdateRegistration(string email, string name, string sid, string providerId, string profileImage,
|
public User UpdateRegistration(string email, string name, string sid, string providerId, string profileImage,
|
||||||
string refreshToken) {
|
string refreshToken) {
|
||||||
var user = _context.Users.FirstOrDefault(u => u.EmailAddress == email);
|
var user = _context.UserDetails.FirstOrDefault(u => u.EmailAddress == email);
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
user = new User();
|
user = new User();
|
||||||
@@ -57,7 +57,7 @@ namespace PodNoms.Api.Persistence {
|
|||||||
var c = user.FullName ?? email?.Split('@')[0] ?? string.Empty;
|
var c = user.FullName ?? email?.Split('@')[0] ?? string.Empty;
|
||||||
if (!string.IsNullOrEmpty(c)) {
|
if (!string.IsNullOrEmpty(c)) {
|
||||||
user.Slug = c.Slugify(
|
user.Slug = c.Slugify(
|
||||||
from u in _context.Users select u.Slug);
|
from u in _context.UserDetails select u.Slug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (string.IsNullOrEmpty(user.Uid)) {
|
if (string.IsNullOrEmpty(user.Uid)) {
|
||||||
@@ -81,7 +81,7 @@ namespace PodNoms.Api.Persistence {
|
|||||||
if (user != null) {
|
if (user != null) {
|
||||||
do {
|
do {
|
||||||
newKey = Randomisers.RandomString(16);
|
newKey = Randomisers.RandomString(16);
|
||||||
} while (_context.Users.FirstOrDefault(u => u.ApiKey == newKey) != null);
|
} while (_context.UserDetails.FirstOrDefault(u => u.ApiKey == newKey) != null);
|
||||||
}
|
}
|
||||||
user.ApiKey = newKey;
|
user.ApiKey = newKey;
|
||||||
return newKey;
|
return newKey;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<EmbeddedResource Include="Resources/*.xml" />
|
<EmbeddedResource Include="Resources/*.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentValidation.AspNetCore" Version="7.5.2" />
|
||||||
<PackageReference Include="Hangfire" Version="1.6.17" />
|
<PackageReference Include="Hangfire" Version="1.6.17" />
|
||||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
||||||
<PackageReference Include="Lib.Net.Http.WebPush" Version="1.3.0" />
|
<PackageReference Include="Lib.Net.Http.WebPush" Version="1.3.0" />
|
||||||
|
|||||||
@@ -9,23 +9,19 @@ using Microsoft.AspNetCore.Hosting;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace PodNoms.Api
|
namespace PodNoms.Api {
|
||||||
{
|
public class Program {
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
static bool isDevelopment =
|
static bool isDevelopment =
|
||||||
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == EnvironmentName.Development;
|
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == EnvironmentName.Development;
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args) {
|
||||||
{
|
|
||||||
CreateWebHostBuilder(args).Build().Run();
|
CreateWebHostBuilder(args).Build().Run();
|
||||||
}
|
}
|
||||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||||
WebHost.CreateDefaultBuilder(args)
|
WebHost.CreateDefaultBuilder(args)
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.UseUrls("http://0.0.0.0:5000")
|
.UseUrls("http://0.0.0.0:5000")
|
||||||
.UseKestrel(options =>
|
.UseKestrel(options => {
|
||||||
{
|
|
||||||
options.Limits.MaxRequestBodySize = 1073741824; //1Gb
|
options.Limits.MaxRequestBodySize = 1073741824; //1Gb
|
||||||
// if (isDevelopment)
|
// if (isDevelopment)
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -3,15 +3,13 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using PodNoms.Api.Models;
|
using PodNoms.Api.Models;
|
||||||
using PodNoms.Api.Models.ViewModels;
|
using PodNoms.Api.Models.ViewModels;
|
||||||
|
using PodNoms.Api.Services.Auth;
|
||||||
|
|
||||||
namespace PodNoms.Api.Providers
|
namespace PodNoms.Api.Providers {
|
||||||
{
|
public class MappingProvider : Profile {
|
||||||
public class MappingProvider : Profile
|
|
||||||
{
|
|
||||||
private readonly IConfiguration _options;
|
private readonly IConfiguration _options;
|
||||||
public MappingProvider() { }
|
public MappingProvider() { }
|
||||||
public MappingProvider(IConfiguration options)
|
public MappingProvider(IConfiguration options) {
|
||||||
{
|
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
|
||||||
//Domain to API Resource
|
//Domain to API Resource
|
||||||
@@ -29,7 +27,7 @@ namespace PodNoms.Api.Providers
|
|||||||
.ForMember(
|
.ForMember(
|
||||||
src => src.AudioUrl,
|
src => src.AudioUrl,
|
||||||
e => e.MapFrom(m => $"{this._options.GetSection("Storage")["CdnUrl"]}{m.AudioUrl}"));
|
e => e.MapFrom(m => $"{this._options.GetSection("Storage")["CdnUrl"]}{m.AudioUrl}"));
|
||||||
|
|
||||||
CreateMap<User, ProfileViewModel>()
|
CreateMap<User, ProfileViewModel>()
|
||||||
.ForMember(
|
.ForMember(
|
||||||
src => src.Name,
|
src => src.Name,
|
||||||
@@ -37,13 +35,17 @@ namespace PodNoms.Api.Providers
|
|||||||
|
|
||||||
//API Resource to Domain
|
//API Resource to Domain
|
||||||
CreateMap<PodcastViewModel, Podcast>()
|
CreateMap<PodcastViewModel, Podcast>()
|
||||||
.ForMember(v => v.ImageUrl, opt => opt.Ignore())
|
.ForMember(v => v.ImageUrl, map => map.Ignore())
|
||||||
;
|
;
|
||||||
CreateMap<PodcastEntryViewModel, PodcastEntry>()
|
CreateMap<PodcastEntryViewModel, PodcastEntry>()
|
||||||
.ForMember(
|
.ForMember(
|
||||||
e => e.ImageUrl,
|
e => e.ImageUrl,
|
||||||
opt => opt.MapFrom(m => m.ImageUrl))
|
map => map.MapFrom(vm => vm.ImageUrl))
|
||||||
;
|
;
|
||||||
|
CreateMap<RegistrationViewModel, ApplicationUser>()
|
||||||
|
.ForMember(
|
||||||
|
e => e.UserName,
|
||||||
|
map => map.MapFrom(vm => vm.Email));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
11
server/Services/Auth/ApplicationUser.cs
Normal file
11
server/Services/Auth/ApplicationUser.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Services.Auth {
|
||||||
|
public class ApplicationUser : IdentityUser {
|
||||||
|
// Extended Properties
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
public string LastName { get; set; }
|
||||||
|
public long? FacebookId { get; set; }
|
||||||
|
public string PictureUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
13
server/Services/Auth/Constants.cs
Normal file
13
server/Services/Auth/Constants.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace PodNoms.Api.Services.Auth {
|
||||||
|
public static class Constants {
|
||||||
|
public static class Strings {
|
||||||
|
public static class JwtClaimIdentifiers {
|
||||||
|
public const string Rol = "rol", Id = "id";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JwtClaims {
|
||||||
|
public const string ApiAccess = "api_access";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
server/Services/Auth/IJwtFactory.cs
Normal file
9
server/Services/Auth/IJwtFactory.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Services.Auth {
|
||||||
|
public interface IJwtFactory {
|
||||||
|
Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity);
|
||||||
|
ClaimsIdentity GenerateClaimsIdentity(string userName, string id);
|
||||||
|
}
|
||||||
|
}
|
||||||
73
server/Services/Auth/JwtFactory.cs
Normal file
73
server/Services/Auth/JwtFactory.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using PodNoms.Api.Models;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Services.Auth {
|
||||||
|
public class JwtFactory : IJwtFactory {
|
||||||
|
private readonly JwtIssuerOptions _jwtOptions;
|
||||||
|
|
||||||
|
public JwtFactory(IOptions<JwtIssuerOptions> jwtOptions) {
|
||||||
|
_jwtOptions = jwtOptions.Value;
|
||||||
|
ThrowIfInvalidOptions(_jwtOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity) {
|
||||||
|
var claims = new[]
|
||||||
|
{
|
||||||
|
new Claim(JwtRegisteredClaimNames.Sub, userName),
|
||||||
|
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
|
||||||
|
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
|
||||||
|
identity.FindFirst(Constants.Strings.JwtClaimIdentifiers.Rol),
|
||||||
|
identity.FindFirst(Constants.Strings.JwtClaimIdentifiers.Id)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the JWT security token and encode it.
|
||||||
|
var jwt = new JwtSecurityToken(
|
||||||
|
issuer: _jwtOptions.Issuer,
|
||||||
|
audience: _jwtOptions.Audience,
|
||||||
|
claims: claims,
|
||||||
|
notBefore: _jwtOptions.NotBefore,
|
||||||
|
expires: _jwtOptions.Expiration,
|
||||||
|
signingCredentials: _jwtOptions.SigningCredentials);
|
||||||
|
|
||||||
|
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
|
||||||
|
|
||||||
|
return encodedJwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClaimsIdentity GenerateClaimsIdentity(string userName, string id) {
|
||||||
|
return new ClaimsIdentity(new GenericIdentity(userName, "Token"), new[]
|
||||||
|
{
|
||||||
|
new Claim(Constants.Strings.JwtClaimIdentifiers.Id, id),
|
||||||
|
new Claim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>Date converted to seconds since Unix epoch (Jan 1, 1970, midnight UTC).</returns>
|
||||||
|
private static long ToUnixEpochDate(DateTime date)
|
||||||
|
=> (long)Math.Round((date.ToUniversalTime() -
|
||||||
|
new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero))
|
||||||
|
.TotalSeconds);
|
||||||
|
|
||||||
|
private static void ThrowIfInvalidOptions(JwtIssuerOptions options) {
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
|
if (options.ValidFor <= TimeSpan.Zero) {
|
||||||
|
throw new ArgumentException("Must be a non-zero TimeSpan.", nameof(JwtIssuerOptions.ValidFor));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.SigningCredentials == null) {
|
||||||
|
throw new ArgumentNullException(nameof(JwtIssuerOptions.SigningCredentials));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.JtiGenerator == null) {
|
||||||
|
throw new ArgumentNullException(nameof(JwtIssuerOptions.JtiGenerator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
server/Services/Auth/Tokens.cs
Normal file
20
server/Services/Auth/Tokens.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using PodNoms.Api.Models;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Services.Auth {
|
||||||
|
public class Tokens {
|
||||||
|
public static async Task<string> GenerateJwt(ClaimsIdentity identity, IJwtFactory jwtFactory, string userName,
|
||||||
|
JwtIssuerOptions jwtOptions, JsonSerializerSettings serializerSettings) {
|
||||||
|
var response = new {
|
||||||
|
id = identity.Claims.Single(c => c.Type == "id").Value,
|
||||||
|
auth_token = await jwtFactory.GenerateEncodedToken(userName, identity),
|
||||||
|
expires_in = (int)jwtOptions.ValidFor.TotalSeconds
|
||||||
|
};
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(response, serializerSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using FluentValidation.AspNetCore;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
@@ -41,9 +42,14 @@ using PodNoms.Api.Services.Push.Extensions;
|
|||||||
using Swashbuckle.AspNetCore.Swagger;
|
using Swashbuckle.AspNetCore.Swagger;
|
||||||
using PodNoms.Api.Services.Push.Formatters;
|
using PodNoms.Api.Services.Push.Formatters;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
namespace PodNoms.Api {
|
namespace PodNoms.Api {
|
||||||
public class Startup {
|
public class Startup {
|
||||||
|
private const string SecretKey = "QGfaEMNASkNMGLKA3LjgPdkPfFEy3n40"; // todo: get this from somewhere secure
|
||||||
|
private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
|
||||||
public IConfiguration Configuration { get; }
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
public Startup(IConfiguration configuration) {
|
public Startup(IConfiguration configuration) {
|
||||||
@@ -105,39 +111,65 @@ namespace PodNoms.Api {
|
|||||||
});
|
});
|
||||||
|
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
|
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
|
||||||
|
|
||||||
|
// Configure JwtIssuerOptions
|
||||||
|
services.Configure<JwtIssuerOptions>(options => {
|
||||||
|
//TODO: Remove this in production, only for testing
|
||||||
|
options.ValidFor = TimeSpan.FromDays(28);
|
||||||
|
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
|
||||||
|
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
|
||||||
|
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
|
||||||
|
});
|
||||||
|
var tokenValidationParameters = new TokenValidationParameters {
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
|
||||||
|
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
|
||||||
|
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = _signingKey,
|
||||||
|
|
||||||
|
RequireExpirationTime = false,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ClockSkew = TimeSpan.Zero
|
||||||
|
};
|
||||||
services.AddAuthentication(options => {
|
services.AddAuthentication(options => {
|
||||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
|
}).AddJwtBearer(configureOptions => {
|
||||||
}).AddJwtBearer(options => {
|
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
|
||||||
options.Audience = Configuration["auth0:clientId"];
|
configureOptions.TokenValidationParameters = tokenValidationParameters;
|
||||||
options.Authority = $"https://{Configuration["auth0:domain"]}/";
|
configureOptions.SaveToken = true;
|
||||||
options.TokenValidationParameters = new TokenValidationParameters {
|
configureOptions.Events = new JwtBearerEvents() {
|
||||||
NameClaimType = "name"
|
//Don't need this now we've removed Auth0
|
||||||
|
// OnTokenValidated = AuthenticationMiddleware.OnTokenValidated
|
||||||
};
|
};
|
||||||
options.Events = new JwtBearerEvents() {
|
configureOptions.Events.OnMessageReceived = context => {
|
||||||
OnTokenValidated = AuthenticationMiddleware.OnTokenValidated
|
|
||||||
};
|
|
||||||
options.Events.OnMessageReceived = context => {
|
|
||||||
StringValues token;
|
StringValues token;
|
||||||
if (context.Request.Path.Value.StartsWith("/hubs/") && context.Request.Query.TryGetValue("token", out token)) {
|
if (context.Request.Path.Value.StartsWith("/hubs/") && context.Request.Query.TryGetValue("token", out token)) {
|
||||||
context.Token = token;
|
context.Token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
var defaultPolicy =
|
|
||||||
new AuthorizationPolicyBuilder()
|
|
||||||
.AddAuthenticationSchemes("Bearer")
|
|
||||||
.RequireAuthenticatedUser()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
services.AddAuthorization(j => {
|
services.AddAuthorization(j => {
|
||||||
j.DefaultPolicy = defaultPolicy;
|
j.AddPolicy("ApiUser", policy => policy.RequireClaim(
|
||||||
|
Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
|
||||||
});
|
});
|
||||||
|
// add identity
|
||||||
|
var identityBuilder = services.AddIdentityCore<ApplicationUser>(o => {
|
||||||
|
// configure identity options
|
||||||
|
o.Password.RequireDigit = false;
|
||||||
|
o.Password.RequireLowercase = false;
|
||||||
|
o.Password.RequireUppercase = false;
|
||||||
|
o.Password.RequireNonAlphanumeric = false;
|
||||||
|
o.Password.RequiredLength = 6;
|
||||||
|
});
|
||||||
|
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(IdentityRole), identityBuilder.Services);
|
||||||
|
identityBuilder.AddEntityFrameworkStores<PodnomsDbContext>().AddDefaultTokenProviders();
|
||||||
|
|
||||||
services.AddMvc(options => {
|
services.AddMvc(options => {
|
||||||
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
|
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
|
||||||
@@ -150,7 +182,8 @@ namespace PodNoms.Api {
|
|||||||
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
|
||||||
})
|
})
|
||||||
.AddXmlSerializerFormatters();
|
.AddXmlSerializerFormatters()
|
||||||
|
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
|
||||||
|
|
||||||
services.AddSwaggerGen(c => {
|
services.AddSwaggerGen(c => {
|
||||||
c.SwaggerDoc("v1", new Info { Title = "Podnoms.API", Version = "v1" });
|
c.SwaggerDoc("v1", new Info { Title = "Podnoms.API", Version = "v1" });
|
||||||
@@ -174,6 +207,8 @@ namespace PodNoms.Api {
|
|||||||
|
|
||||||
services.AddTransient<IFileUploader, AzureFileUploader>();
|
services.AddTransient<IFileUploader, AzureFileUploader>();
|
||||||
services.AddTransient<IRealTimeUpdater, SignalRUpdater>();
|
services.AddTransient<IRealTimeUpdater, SignalRUpdater>();
|
||||||
|
services.TryAddTransient<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
|
services.AddSingleton<IJwtFactory, JwtFactory>();
|
||||||
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||||
services.AddScoped<IPodcastRepository, PodcastRepository>();
|
services.AddScoped<IPodcastRepository, PodcastRepository>();
|
||||||
services.AddScoped<IEntryRepository, EntryRepository>();
|
services.AddScoped<IEntryRepository, EntryRepository>();
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace PodNoms.Api.Utils
|
namespace PodNoms.Api.Utils {
|
||||||
{
|
public class DateUtils {
|
||||||
|
public static DateTime ConvertFromUnixTimestamp(double timestamp) {
|
||||||
public class DateUtils
|
|
||||||
{
|
|
||||||
public static DateTime ConvertFromUnixTimestamp(double timestamp)
|
|
||||||
{
|
|
||||||
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||||
return origin.AddSeconds(timestamp);
|
return origin.AddSeconds(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double ConvertToUnixTimestamp(DateTime date)
|
public static double ConvertToUnixTimestamp(DateTime date) {
|
||||||
{
|
|
||||||
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||||
TimeSpan diff = date.ToUniversalTime() - origin;
|
TimeSpan diff = date.ToUniversalTime() - origin;
|
||||||
return Math.Floor(diff.TotalSeconds);
|
return Math.Floor(diff.TotalSeconds);
|
||||||
|
|||||||
19
server/Utils/Errors.cs
Normal file
19
server/Utils/Errors.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace PodNoms.Api.Utils {
|
||||||
|
public static class Errors {
|
||||||
|
public static ModelStateDictionary AddErrorsToModelState(IdentityResult identityResult, ModelStateDictionary modelState) {
|
||||||
|
foreach (var e in identityResult.Errors) {
|
||||||
|
modelState.TryAddModelError(e.Code, e.Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return modelState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModelStateDictionary AddErrorToModelState(string code, string description, ModelStateDictionary modelState) {
|
||||||
|
modelState.TryAddModelError(code, description);
|
||||||
|
return modelState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user