SignalR auth working

This commit is contained in:
Fergal Moran
2018-05-09 09:48:07 +01:00
parent 8725d632f0
commit fd22da93b8
54 changed files with 333 additions and 167 deletions

View File

@@ -24,6 +24,7 @@
"@angular/platform-browser": "6.0.0", "@angular/platform-browser": "6.0.0",
"@angular/platform-browser-dynamic": "6.0.0", "@angular/platform-browser-dynamic": "6.0.0",
"@angular/router": "6.0.0", "@angular/router": "6.0.0",
"@aspnet/signalr": "^1.0.0-rtm-30751",
"@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",

View File

@@ -23,12 +23,29 @@ export const ADD_SUCCESS = '[Chat] Add Chat Success';
export const ADD_FAIL = '[Chat] Add Chat Fail'; export const ADD_FAIL = '[Chat] Add Chat Fail';
export class AddAction implements Action { export class AddAction implements Action {
readonly type = ADD; readonly type = ADD;
constructor(public payload: ChatModel) { constructor(public payload: ChatModel) {}
}
} }
export class AddSuccessAction implements Action { export class AddSuccessAction implements Action {
readonly type = ADD_SUCCESS; readonly type = ADD_SUCCESS;
constructor(public payload: ChatModel) {}
}
//#endregion
//#region Add
// the receive action is for messages from signalR
// we don't want to initiate a POST to the server.
export const RECEIVE = '[Chat] Receive Chat';
export const RECEIVE_SUCCESS = '[Chat] Receive Chat Success';
export const RECEIVE_FAIL = '[Chat] Receive Chat Fail';
export class ReceiveAction implements Action {
readonly type = RECEIVE;
constructor(public payload: ChatModel) { constructor(public payload: ChatModel) {
console.log('chat.actions', 'RECEIVE', payload);
}
}
export class ReceiveSuccessAction implements Action {
readonly type = RECEIVE_SUCCESS;
constructor(public payload: ChatModel) {
console.log('chat.actions', 'RECEIVE_SUCESS', payload);
} }
} }
//#endregion //#endregion
@@ -37,5 +54,6 @@ export type Actions =
| LoadSuccessAction | LoadSuccessAction
| LoadFailAction | LoadFailAction
| AddAction | AddAction
| AddSuccessAction; | AddSuccessAction
| ReceiveAction
| ReceiveSuccessAction;

View File

@@ -58,13 +58,11 @@ export class AppComponent implements OnInit {
const chatterChannel = `${p.id}`; const chatterChannel = `${p.id}`;
this._signalrService this._signalrService
.init('userupdates') .init('userupdates')
.then((r) => { .then((listener) => {
this._signalrService.connection.on( listener.on<string>(chatterChannel)
chatterChannel, .subscribe(result => {
(result) => { this._toastyService.info(result);
this._toastyService.info(result); });
}
);
}) })
.catch((err) => { .catch((err) => {
console.error( console.error(

View File

@@ -156,7 +156,7 @@ export function provideConfig() {
authDomain: 'podnoms-api.firebaseapp.com', authDomain: 'podnoms-api.firebaseapp.com',
databaseURL: 'https://podnoms-api.firebaseio.com', databaseURL: 'https://podnoms-api.firebaseio.com',
projectId: 'podnoms-api', projectId: 'podnoms-api',
storageBucket: '', storageBucket: 'podnoms-api.appspot.com',
messagingSenderId: '357461672895' messagingSenderId: '357461672895'
}), }),
AngularFireDatabaseModule, AngularFireDatabaseModule,

View File

@@ -25,8 +25,9 @@
</div> </div>
</div> </div>
<div class="chat-controls" *ngIf="!loading"> <div class="chat-controls">
<input class="message-input" placeholder="Type your message here..." type="text" #message (keyup.enter)="sendMessage(message)"> <input #message class="message-input" placeholder="Type your message and press enter..." type="text"
(keyup.enter)="sendMessage(message)">
<div class="chat-extra"> <div class="chat-extra">
<a href="#"> <a href="#">
<span class="extra-tooltip">Attach Document</span> <span class="extra-tooltip">Attach Document</span>

View File

@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, ViewChild, ElementRef } from '@angular/core';
import { ProfileService } from '../../services/profile.service'; import { ProfileService } from '../../services/profile.service';
import { SignalRService } from '../../services/signalr.service'; import { SignalRService } from '../../services/signalr.service';
import { ProfileModel } from 'app/models/profile.model'; import { ProfileModel } from 'app/models/profile.model';
@@ -15,11 +15,17 @@ import { Observable } from 'rxjs/Observable';
templateUrl: './chat-widget.component.html', templateUrl: './chat-widget.component.html',
styleUrls: ['./chat-widget.component.css'] styleUrls: ['./chat-widget.component.css']
}) })
export class ChatWidgetComponent implements OnInit { export class ChatWidgetComponent {
chatActive: boolean = false; chatActive: boolean = false;
loading: boolean = false; private user: ProfileModel;
user: ProfileModel;
messages$: Observable<ChatModel[]>; messages$: Observable<ChatModel[]>;
private messageEl: ElementRef;
// have to handle ViewChild like this as it's hidden with ngIf
@ViewChild('message')
set content(content: ElementRef) {
this.messageEl = content;
}
constructor( constructor(
private _profileService: ProfileService, private _profileService: ProfileService,
@@ -28,25 +34,17 @@ export class ChatWidgetComponent implements OnInit {
) { ) {
this._profileService.getProfile().subscribe((p) => (this.user = p)); this._profileService.getProfile().subscribe((p) => (this.user = p));
this.messages$ = _store.select(fromChat.getChat); this.messages$ = _store.select(fromChat.getChat);
this.messages$.subscribe((r) => {
if (r.length != 0) {
this.chatActive = true;
}
});
} }
ngOnInit() {}
togglePopup() { togglePopup() {
this.chatActive = !this.chatActive; this.chatActive = !this.chatActive;
if (this.chatActive) {
if (this.chatActive && this.user) { setTimeout(() => this.messageEl.nativeElement.focus(), 0);
this.loading = true;
this._signalRService.init('chat').then(() => {
this.loading = false;
this._signalRService.connection.on('SendMessage', (message) => {
console.log(
'chat-widget.component',
'SendMessage',
message
);
});
});
} }
} }

View File

@@ -10,6 +10,7 @@ import * as fromEntriesActions from 'app/actions/entries.actions';
import { PodcastService } from '../../../services/podcast.service'; import { PodcastService } from '../../../services/podcast.service';
import { AudioService } from 'app/services/audio.service'; import { AudioService } from 'app/services/audio.service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { AudioProcessingMessageModel } from 'app/models/audioprocessingmessage.model';
@Component({ @Component({
selector: '[app-entry-list-item]', selector: '[app-entry-list-item]',
@@ -39,23 +40,22 @@ export class EntryListItemComponent implements OnInit {
) { ) {
this._signalRService this._signalRService
.init('audioprocessing') .init('audioprocessing')
.then(() => { .then((listener) => {
const updateChannel: string = `${ const updateChannel: string = `${
this.entry.uid this.entry.uid
}__progress_update`; }__progress_update`;
const processedChannel: string = `${ const processedChannel: string = `${
this.entry.uid this.entry.uid
}__info_processed`; }__info_processed`;
this._signalRService.connection.on( listener
updateChannel, .on<AudioProcessingMessageModel>(updateChannel)
(result) => { .subscribe((result) => {
this.percentageProcessed = result.percentage; this.percentageProcessed = result.percentage;
this.currentSpeed = result.currentSpeed; this.currentSpeed = result.currentSpeed;
} });
); listener
this._signalRService.connection.on( .on<PodcastEntryModel>(processedChannel)
processedChannel, .subscribe((result) => {
(result) => {
this.entry = result; this.entry = result;
if (this.entry.processingStatus === 'Processed') { if (this.entry.processingStatus === 'Processed') {
// only update the store when we're finished. // only update the store when we're finished.
@@ -65,8 +65,7 @@ export class EntryListItemComponent implements OnInit {
) )
); );
} }
} });
);
}) })
.catch((err) => .catch((err) =>
console.error( console.error(

View File

@@ -1,7 +1,4 @@
import { import { LoadAction, LOAD_FAIL } from './../actions/chat.actions';
LoadAction,
LOAD_FAIL
} from './../actions/chat.actions';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects'; import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
@@ -22,17 +19,26 @@ export class ChatEffects {
.switchMap((payload: chat.LoadAction) => .switchMap((payload: chat.LoadAction) =>
this._service this._service
.get() .get()
.map(res => ({ type: chat.LOAD_SUCCESS, payload: res })) .map((res) => ({ type: chat.LOAD_SUCCESS, payload: res }))
.catch(err => { .catch((err) => {
console.error('ChatEffects', 'get$', err); console.error('ChatEffects', 'get$', err);
return Observable.of({ type: chat.LOAD_FAIL }); return Observable.of({ type: chat.LOAD_FAIL });
}) })
); );
@Effect() @Effect()
put$ = this.actions$ put$ = this.actions$
.ofType(chat.ADD) .ofType(chat.ADD)
.switchMap((action: chat.AddAction) => this._service.send(action.payload)) .switchMap((action: chat.AddAction) =>
.map(res => ({ type: chat.ADD_SUCCESS, payload: res })); this._service.send(action.payload)
)
.map((res) => ({ type: chat.ADD_SUCCESS, payload: res }));
@Effect()
receive$ = this.actions$
.ofType(chat.RECEIVE)
.map((res: chat.ReceiveAction) => ({ type: chat.RECEIVE_SUCCESS, payload: res.payload }));
// receive$ = this.actions$.ofType(chat.RECEIVE).map((res) => {
// return { type: chat.RECEIVE_SUCCESS, payload: res };
// });
constructor(private _service: ChatService, private actions$: Actions) {} constructor(private _service: ChatService, private actions$: Actions) {}
} }

View File

@@ -0,0 +1,6 @@
export class AudioProcessingMessageModel {
percentage: number;
totalSize: string;
currentSpeed: string;
eTA: string;
}

View File

@@ -33,7 +33,8 @@ export function reducer(state = initialState, action: chat.Actions): State {
loading: false loading: false
}; };
} }
case chat.ADD_SUCCESS: { case chat.ADD_SUCCESS:
case chat.RECEIVE_SUCCESS: {
const newResults = _.clone(state.result); const newResults = _.clone(state.result);
newResults.push(action.payload); newResults.push(action.payload);
const newState = { const newState = {

View File

@@ -4,11 +4,35 @@ import { Observable } from 'rxjs/Observable';
import { environment } from 'environments/environment'; import { environment } from 'environments/environment';
import { ChatModel } from 'app/models/chat.model'; import { ChatModel } from 'app/models/chat.model';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { SignalRService } from './signalr.service';
import { Subject } from 'rxjs';
import * as fromChat from 'app/reducers';
import * as fromChatActions from 'app/actions/chat.actions';
import { ApplicationState } from '../store';
import { Store } from '@ngrx/store';
@Injectable() @Injectable()
export class ChatService extends BaseService { export class ChatService extends BaseService {
constructor(private _http: HttpClient) { constructor(
private _http: HttpClient,
private _signalRService: SignalRService,
private _store: Store<ApplicationState>
) {
super(); super();
this._signalRService.init('chat').then((listener) => {
listener
.on<ChatModel>('SendMessage')
.subscribe((message: ChatModel) => {
console.log(
'chat-widget.component',
'SendMessage',
message
);
this._store.dispatch(
new fromChatActions.ReceiveAction(message)
);
});
});
} }
get(): Observable<ChatModel[]> { get(): Observable<ChatModel[]> {

View File

@@ -18,7 +18,7 @@ export class MessagingService {
private _pushRegistrationServer: PushRegistrationService private _pushRegistrationServer: PushRegistrationService
) { ) {
this.messaging.usePublicVapidKey( this.messaging.usePublicVapidKey(
'BKyhUqIVZLauKNA-DXPXbIVLj5XiWurHbRV_0Rd3BOjY5cU9GOrd5ptXVJ2CNExxdveKYzZevrep2CflKeqkyqo' 'BP05eVFzqeEh54DaL3TOe6x_8UFs60nw_gfSrI5tdILjb5VnHwas0n7c_075tsc1w5fm87u9d4Dawj_YN13PSAI'
); );
} }

View File

@@ -1,31 +1,37 @@
import { PodnomsAuthService } from './podnoms-auth.service'; import { PodnomsAuthService } from './podnoms-auth.service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { import { HubConnection, HubConnectionBuilder, LogLevel } from '@aspnet/signalr';
HubConnection,
HubConnectionBuilder,
JsonHubProtocol,
LogLevel
} from '@aspnet/signalr';
import { environment } from 'environments/environment'; import { environment } from 'environments/environment';
import { Observable, Subscriber } from 'rxjs';
@Injectable() @Injectable()
export class SignalRService { export class SignalRService {
public connection: HubConnection; private _connected: boolean = false;
private _connection: HubConnection;
constructor(private _auth: PodnomsAuthService) {} constructor(private _auth: PodnomsAuthService) {}
public init(hub: string): Promise<void> { public init(hub: string): Promise<SignalRService> {
const url = `${environment.SIGNALR_HOST}/hubs/${hub}`; return new Promise((resolve) => {
const url = `${environment.SIGNALR_HOST}/hubs/${hub}`;
const token = this._auth.getToken();
this._connection = new HubConnectionBuilder()
.configureLogging(LogLevel.Debug)
.withUrl(url + '?token=' + token)
.build();
resolve(this);
});
}
const token = this._auth.getToken(); public on<T>(channel: string): Observable<T> {
const options: any = { const listener = new Observable<T>((subscriber: Subscriber<T>) => {
transport: 0 this._connection.on(channel, (message) => {
}; const result: T = message as T;
subscriber.next(result);
this.connection = new HubConnectionBuilder() });
.configureLogging(LogLevel.Error) });
.withUrl(url + '?token=' + token, options) if (!this._connected) {
.withHubProtocol(new JsonHubProtocol()) this._connection.start().then(() => (this._connected = true));
.build(); }
return this.connection.start(); return listener;
} }
} }

View File

@@ -32,9 +32,6 @@
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
<!-- End Google Analytics --> <!-- End Google Analytics -->
<!-- Begin Crisp -->
<script type="text/javascript">window.$crisp = []; window.CRISP_WEBSITE_ID = "af7d6aa7-0cb8-4f74-89aa-7f3742058e85"; (function () { d = document; s = d.createElement("script"); s.src = "https://client.crisp.chat/l.js"; s.async = 1; d.getElementsByTagName("head")[0].appendChild(s); })();</script>
<!-- End Crisp -->
</body> </body>
</html> </html>

View File

@@ -15,6 +15,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Providers; using PodNoms.Api.Providers;

View File

@@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Services; using PodNoms.Api.Services;
using PodNoms.Api.Services.Auth; using PodNoms.Api.Services.Auth;

View File

@@ -1,10 +1,12 @@
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using PodNoms.Api.Services.Auth; using PodNoms.Api.Services.Auth;
[Authorize]
public class BaseAuthController : Controller { public class BaseAuthController : Controller {
private readonly ClaimsPrincipal _caller; private readonly ClaimsPrincipal _caller;
protected readonly UserManager<ApplicationUser> _userManager; protected readonly UserManager<ApplicationUser> _userManager;

View File

@@ -1,29 +1,40 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Lib.Net.Http.WebPush;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Options;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Services;
using PodNoms.Api.Services.Auth; using PodNoms.Api.Services.Auth;
using PodNoms.Api.Services.Hubs; using PodNoms.Api.Services.Hubs;
using PodNoms.Api.Services.Push;
using WebPush = Lib.Net.Http.WebPush;
namespace PodNoms.Api.Controllers { namespace PodNoms.Api.Controllers {
[Route("[controller]")] [Route("[controller]")]
[Authorize] [Authorize]
public class ChatController : BaseAuthController { public class ChatController : BaseAuthController {
private readonly HubLifetimeManager<ChatHub> _hub; private readonly ISupportChatService _supportChatService;
public ChatController(IHttpContextAccessor contextAccessor, UserManager<ApplicationUser> userManager, public ChatController(IHttpContextAccessor contextAccessor, UserManager<ApplicationUser> userManager,
HubLifetimeManager<ChatHub> chatHubContext) : ISupportChatService supportChatService) :
base(contextAccessor, userManager) { base(contextAccessor, userManager) {
this._hub = chatHubContext; this._supportChatService = supportChatService;
} }
[HttpPost] [HttpPost]
public async Task<ActionResult<ChatViewModel>> Post([FromBody]ChatViewModel message) { public async Task<ActionResult<ChatViewModel>> Post([FromBody]ChatViewModel message) {
await this._hub.SendAllAsync("SendMessage", new object[] { message.Message }); //need to lookup the current support host and notify them
return Ok(message); message.FromUserName = _applicationUser.FullName;
message.FromUserId = _applicationUser.Id;
if (await _supportChatService.InitiateSupportRequest(_userId, message)) {
return Ok(message);
}
return Accepted(message);
} }
} }
} }

View File

@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services.Auth; using PodNoms.Api.Services.Auth;
using PodNoms.Api.Services.Downloader; using PodNoms.Api.Services.Downloader;
@@ -24,7 +25,7 @@ namespace PodNoms.Api.Controllers {
public class DebugController : BaseAuthController { public class DebugController : BaseAuthController {
private readonly StorageSettings _storageSettings; private readonly StorageSettings _storageSettings;
private readonly AudioFileStorageSettings _audioFileStorageSettings; private readonly AudioFileStorageSettings _audioFileStorageSettings;
private readonly ApplicationsSettings _applicationsSettings; private readonly HelpersSettings _helpersSettings;
private readonly ImageFileStorageSettings _imageFileStorageSettings; private readonly ImageFileStorageSettings _imageFileStorageSettings;
private readonly HubLifetimeManager<DebugHub> _hubManager; private readonly HubLifetimeManager<DebugHub> _hubManager;
private readonly IPushSubscriptionStore _subscriptionStore; private readonly IPushSubscriptionStore _subscriptionStore;
@@ -34,7 +35,7 @@ namespace PodNoms.Api.Controllers {
public DebugController(IOptions<StorageSettings> settings, IOptions<AppSettings> appSettings, public DebugController(IOptions<StorageSettings> settings, IOptions<AppSettings> appSettings,
HubLifetimeManager<DebugHub> hubManager, HubLifetimeManager<DebugHub> hubManager,
IOptions<ApplicationsSettings> applicationsSettings, IOptions<HelpersSettings> helpersSettings,
IOptions<AudioFileStorageSettings> audioFileStorageSettings, IOptions<AudioFileStorageSettings> audioFileStorageSettings,
IOptions<ImageFileStorageSettings> imageFileStorageSettings, IOptions<ImageFileStorageSettings> imageFileStorageSettings,
IPushSubscriptionStore subscriptionStore, IPushSubscriptionStore subscriptionStore,
@@ -43,7 +44,7 @@ namespace PodNoms.Api.Controllers {
IHttpContextAccessor contextAccessor) : base(contextAccessor, userManager) { IHttpContextAccessor contextAccessor) : base(contextAccessor, userManager) {
this._appSettings = appSettings.Value; this._appSettings = appSettings.Value;
this._storageSettings = settings.Value; this._storageSettings = settings.Value;
this._applicationsSettings = applicationsSettings.Value; this._helpersSettings = helpersSettings.Value;
this._audioFileStorageSettings = audioFileStorageSettings.Value; this._audioFileStorageSettings = audioFileStorageSettings.Value;
this._imageFileStorageSettings = imageFileStorageSettings.Value; this._imageFileStorageSettings = imageFileStorageSettings.Value;
this._hubManager = hubManager; this._hubManager = hubManager;
@@ -59,8 +60,8 @@ namespace PodNoms.Api.Controllers {
CdnUrl = _storageSettings.CdnUrl, CdnUrl = _storageSettings.CdnUrl,
AudioContainer = _audioFileStorageSettings.ContainerName, AudioContainer = _audioFileStorageSettings.ContainerName,
ImageContainer = _imageFileStorageSettings.ContainerName, ImageContainer = _imageFileStorageSettings.ContainerName,
YouTubeDlPath = _applicationsSettings.Downloader, YouTubeDlPath = _helpersSettings.Downloader,
YouTubeDlVersion = AudioDownloader.GetVersion(_applicationsSettings.Downloader), YouTubeDlVersion = AudioDownloader.GetVersion(_helpersSettings.Downloader),
OSVersion = System.Environment.OSVersion, OSVersion = System.Environment.OSVersion,
RssUrl = _appSettings.RssUrl RssUrl = _appSettings.RssUrl
}; };

View File

@@ -7,12 +7,14 @@ using AutoMapper;
using Hangfire; using Hangfire;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services; using PodNoms.Api.Services;
@@ -24,6 +26,7 @@ using PodNoms.Api.Utils.RemoteParsers;
namespace PodNoms.Api.Controllers { namespace PodNoms.Api.Controllers {
[Route("[controller]")] [Route("[controller]")]
[Authorize]
public class EntryController : BaseAuthController { public class EntryController : BaseAuthController {
private readonly IPodcastRepository _podcastRepository; private readonly IPodcastRepository _podcastRepository;
private readonly IEntryRepository _repository; private readonly IEntryRepository _repository;

View File

@@ -22,6 +22,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Transforms; using SixLabors.ImageSharp.Processing.Transforms;
using SixLabors.ImageSharp.Processing.Filters; using SixLabors.ImageSharp.Processing.Filters;
using PodNoms.Api.Models.Settings;
namespace PodNoms.Api.Controllers { namespace PodNoms.Api.Controllers {
[Authorize] [Authorize]
@@ -42,6 +43,7 @@ namespace PodNoms.Api.Controllers {
this._fileUploader = fileUploader; this._fileUploader = fileUploader;
this._imageFileStorageSettings = imageFileStorageSettings.Value; this._imageFileStorageSettings = imageFileStorageSettings.Value;
this._repository = repository; this._repository = repository;
//this._repository = repository;
this._unitOfWork = unitOfWork; this._unitOfWork = unitOfWork;
this._mapper = mapper; this._mapper = mapper;
this._logger = loggerFactory.CreateLogger<ImageUploadController>(); this._logger = loggerFactory.CreateLogger<ImageUploadController>();

View File

@@ -23,17 +23,14 @@ namespace PodNoms.Api.Controllers {
[Route("[controller]")] [Route("[controller]")]
public class PodcastController : BaseAuthController { public class PodcastController : BaseAuthController {
private readonly IPodcastRepository _repository; private readonly IPodcastRepository _repository;
private readonly IOptions<AppSettings> _settings;
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly IUnitOfWork _uow; private readonly IUnitOfWork _uow;
public PodcastController(IPodcastRepository repository, IOptions<AppSettings> options, public PodcastController(IPodcastRepository repository, IMapper mapper, IUnitOfWork unitOfWork,
IMapper mapper, IUnitOfWork unitOfWork,
UserManager<ApplicationUser> userManager, IHttpContextAccessor contextAccessor) UserManager<ApplicationUser> userManager, IHttpContextAccessor contextAccessor)
: base(contextAccessor, userManager) { : base(contextAccessor, userManager) {
this._uow = unitOfWork; this._uow = unitOfWork;
this._repository = repository; this._repository = repository;
this._settings = options;
this._mapper = mapper; this._mapper = mapper;
} }

View File

@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels.RssViewModels; using PodNoms.Api.Models.ViewModels.RssViewModels;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services.Auth; using PodNoms.Api.Services.Auth;

View File

@@ -1,29 +0,0 @@
using System.IO;
using System.Linq;
namespace PodNoms.Api.Models
{
public class ImageFileStorageSettings : FileStorageSettings
{
}
public class AudioFileStorageSettings : FileStorageSettings
{
}
public class FileStorageSettings
{
public string ContainerName { get; set; }
public long MaxUploadFileSize { get; set; }
public string[] AllowedFileTypes { get; set; }
public bool IsSupported(string fileName)
{
return AllowedFileTypes.Any(s => s == Path.GetExtension(fileName).ToLower());
}
}
public class ApplicationsSettings
{
public string Downloader { get; set; }
}
}

View File

@@ -1,8 +1,9 @@
namespace PodNoms.Api.Models { namespace PodNoms.Api.Models.Settings {
public class AppSettings { public class AppSettings {
public string Version { get; set; } public string Version { get; set; }
public string SiteUrl { get; set; } public string SiteUrl { get; set; }
public string RssUrl { get; set; } public string RssUrl { get; set; }
public string GoogleApiKey { get; set; } public string GoogleApiKey { get; set; }
public string Downloader { get; set; }
} }
} }

View File

@@ -0,0 +1,5 @@
namespace PodNoms.Api.Models.Settings {
public class HelpersSettings {
public string Downloader { get; set; }
}
}

View File

@@ -0,0 +1,4 @@
namespace PodNoms.Api.Models.Settings {
public class AudioFileStorageSettings : FileStorageSettings {
}
}

View File

@@ -0,0 +1,5 @@
namespace PodNoms.Api.Models.Settings {
public class ChatSettings {
public string CurrentChatUser { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
namespace PodNoms.Api.Models { namespace PodNoms.Api.Models.Settings {
public class EmailSettings { public class EmailSettings {
public string ApiKey { get; set; } public string ApiKey { get; set; }

View File

@@ -0,0 +1,15 @@
using System.IO;
using System.Linq;
namespace PodNoms.Api.Models.Settings {
public class FileStorageSettings {
public string ContainerName { get; set; }
public long MaxUploadFileSize { get; set; }
public string[] AllowedFileTypes { get; set; }
public bool IsSupported(string fileName) {
return AllowedFileTypes.Any(s => s == Path.GetExtension(fileName).ToLower());
}
}
}

View File

@@ -0,0 +1,4 @@
namespace PodNoms.Api.Models.Settings {
public class ImageFileStorageSettings : FileStorageSettings {
}
}

View File

@@ -1,7 +1,6 @@
namespace PodNoms.Api.Models { namespace PodNoms.Api.Models.Settings {
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; }
} }
} }

View File

@@ -1,5 +1,9 @@
namespace PodNoms.Api.Models.ViewModels { namespace PodNoms.Api.Models.ViewModels {
public class ChatViewModel { public class ChatViewModel {
public string Message { get; set; } public string Message { get; set; }
public string FromUserId { get; set; }
public string FromUserName { get; set; }
public string ToUserId { get; set; }
public string ToUserName { get; set; }
} }
} }

View File

@@ -1,5 +1,4 @@
namespace PodNoms.Api.Models.ViewModels namespace PodNoms.Api.Models.ViewModels {
{
public class ProcessProgressEvent { public class ProcessProgressEvent {
public double Percentage { get; set; } public double Percentage { get; set; }
public string TotalSize; public string TotalSize;

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services.Storage; using PodNoms.Api.Services.Storage;
using PodNoms.Api.Utils; using PodNoms.Api.Utils;

View File

@@ -19,7 +19,7 @@
<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" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-preview2-final" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-rc1-final" />
<PackageReference Include="AutoMapper" Version="6.2.2" /> <PackageReference Include="AutoMapper" Version="6.2.2" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
<PackageReference Include="Handlebars.NetStandard" Version="1.8.1" /> <PackageReference Include="Handlebars.NetStandard" Version="1.8.1" />
@@ -31,7 +31,7 @@
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-dev001179" /> <PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-dev001179" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.10-pre20180223200113" /> <PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.10-pre20180223200113" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.3.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="2.3.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0-preview2-26406-04" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0-rc1" />
<PackageReference Include="WindowsAzure.Storage" Version="8.2.1" /> <PackageReference Include="WindowsAzure.Storage" Version="8.2.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -8,5 +8,6 @@ namespace PodNoms.Api.Services.Auth {
public long? FacebookId { get; set; } public long? FacebookId { get; set; }
public string PictureUrl { get; set; } public string PictureUrl { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string FullName { get => $"{FirstName} {LastName}"; }
} }
} }

View File

@@ -1,12 +1,9 @@
using System; using System;
using System.Security.Claims; using System.Security.Claims;
namespace PodNoms.Api.Services.Auth namespace PodNoms.Api.Services.Auth {
{ public static class ClaimsPrincipalExtensions {
public static class ClaimsPrincipalExtensions public static string GetUserId(this ClaimsPrincipal principal) {
{
public static string GetUserId(this ClaimsPrincipal principal)
{
if (principal == null) if (principal == null)
throw new ArgumentNullException(nameof(principal)); throw new ArgumentNullException(nameof(principal));

View File

@@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
using PodNoms.Api.Services.Gravatar; using PodNoms.Api.Services.Gravatar;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Utils; using PodNoms.Api.Utils;
using PodNoms.Api.Models.Settings;
namespace PodNoms.Api.Services.Auth { namespace PodNoms.Api.Services.Auth {
public class PodNomsUserManager : UserManager<ApplicationUser> { public class PodNomsUserManager : UserManager<ApplicationUser> {

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
@@ -7,15 +8,12 @@ namespace PodNoms.Api.Services.Hubs {
[Authorize] [Authorize]
public class ChatHub : Hub { public class ChatHub : Hub {
public override async Task OnConnectedAsync() { public override async Task OnConnectedAsync() {
await Clients.All.SendAsync("SendAction", Context.User.Identity.Name, "joined"); // await Clients.All.SendAsync("SendAction", Context.User.Identity.Name, "joined");
} }
public override async Task OnDisconnectedAsync(Exception ex) { public override async Task OnDisconnectedAsync(Exception ex) {
await Clients.All.SendAsync("SendAction", Context.User.Identity.Name, "left"); // await Clients.All.SendAsync("SendAction", Context.User.Identity.Name, "left");
}
public async Task Send(string message) {
await Clients.All.SendAsync("SendMessage", Context.User.Identity.Name, message);
} }
} }
} }

View File

@@ -0,0 +1,8 @@
using System.Threading.Tasks;
using PodNoms.Api.Models.ViewModels;
namespace PodNoms.Api.Services {
public interface ISupportChatService {
Task<bool> InitiateSupportRequest(string fromUser, ChatViewModel message);
}
}

View File

@@ -5,6 +5,7 @@ using Microsoft.Extensions.Options;
using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob; using Microsoft.WindowsAzure.Storage.Blob;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
namespace PodNoms.Api.Services.Jobs { namespace PodNoms.Api.Services.Jobs {

View File

@@ -12,7 +12,6 @@ namespace PodNoms.Api.Services.Jobs {
IPushNotificationService notificationService) { IPushNotificationService notificationService) {
this._notificationService = notificationService; this._notificationService = notificationService;
this._subscriptionStore = subscriptionStore; this._subscriptionStore = subscriptionStore;
} }
public async Task NotifyUser(string userId, string title, string body, string image) { public async Task NotifyUser(string userId, string title, string body, string image) {
WebPush.PushMessage pushMessage = new WebPush.PushMessage(body) { WebPush.PushMessage pushMessage = new WebPush.PushMessage(body) {

View File

@@ -4,6 +4,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services.Downloader; using PodNoms.Api.Services.Downloader;
using PodNoms.Api.Services.Processor; using PodNoms.Api.Services.Processor;
@@ -16,12 +17,12 @@ namespace PodNoms.Api.Services.Jobs {
private readonly IAudioUploadProcessService _uploadService; private readonly IAudioUploadProcessService _uploadService;
private readonly IConfiguration _options; private readonly IConfiguration _options;
private readonly IPodcastRepository _podcastRepository; private readonly IPodcastRepository _podcastRepository;
private readonly ApplicationsSettings _applicationsSettings; private readonly HelpersSettings _helpersSettings;
private readonly ILogger<ProcessPlaylistItemJob> _logger; private readonly ILogger<ProcessPlaylistItemJob> _logger;
private readonly IUnitOfWork _unitOfWork; private readonly IUnitOfWork _unitOfWork;
public ProcessPlaylistItemJob(IPlaylistRepository playlistRepository, IEntryRepository entryRepository, public ProcessPlaylistItemJob(IPlaylistRepository playlistRepository, IEntryRepository entryRepository,
IAudioUploadProcessService uploadService, IConfiguration options, IAudioUploadProcessService uploadService, IConfiguration options,
IPodcastRepository podcastRepository, IOptions<ApplicationsSettings> applicationsSettings, IPodcastRepository podcastRepository, IOptions<HelpersSettings> _helpersSettings,
IUnitOfWork unitOfWork, ILogger<ProcessPlaylistItemJob> logger) { IUnitOfWork unitOfWork, ILogger<ProcessPlaylistItemJob> logger) {
this._unitOfWork = unitOfWork; this._unitOfWork = unitOfWork;
this._playlistRepository = playlistRepository; this._playlistRepository = playlistRepository;
@@ -29,7 +30,7 @@ namespace PodNoms.Api.Services.Jobs {
this._uploadService = uploadService; this._uploadService = uploadService;
this._options = options; this._options = options;
this._podcastRepository = podcastRepository; this._podcastRepository = podcastRepository;
this._applicationsSettings = applicationsSettings.Value; this._helpersSettings = _helpersSettings.Value;
this._logger = logger; this._logger = logger;
} }
public async Task Execute() { public async Task Execute() {
@@ -42,7 +43,7 @@ namespace PodNoms.Api.Services.Jobs {
var item = await _playlistRepository.GetParsedItem(itemId, playlistId); var item = await _playlistRepository.GetParsedItem(itemId, playlistId);
if (item != null && !string.IsNullOrEmpty(item.VideoType) && item.VideoType.Equals("youtube")) { if (item != null && !string.IsNullOrEmpty(item.VideoType) && item.VideoType.Equals("youtube")) {
var url = $"https://www.youtube.com/watch?v={item.VideoId}"; var url = $"https://www.youtube.com/watch?v={item.VideoId}";
var downloader = new AudioDownloader(url, _applicationsSettings.Downloader); var downloader = new AudioDownloader(url, _helpersSettings.Downloader);
var info = downloader.GetInfo(); var info = downloader.GetInfo();
if (info == AudioType.Valid) { if (info == AudioType.Valid) {
var podcast = await _podcastRepository.GetAsync(item.Playlist.PodcastId); var podcast = await _podcastRepository.GetAsync(item.Playlist.PodcastId);

View File

@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using NYoutubeDL.Models; using NYoutubeDL.Models;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services.Downloader; using PodNoms.Api.Services.Downloader;
using PodNoms.Api.Utils.RemoteParsers; using PodNoms.Api.Utils.RemoteParsers;
@@ -17,21 +18,21 @@ namespace PodNoms.Api.Services.Jobs {
public class ProcessPlaylistsJob : IJob { public class ProcessPlaylistsJob : IJob {
public readonly IPlaylistRepository _playlistRepository; public readonly IPlaylistRepository _playlistRepository;
public readonly IEntryRepository _entryRepository; public readonly IEntryRepository _entryRepository;
private readonly ApplicationsSettings _applicationsSettings; private readonly HelpersSettings _helpersSettings;
private readonly ILogger<ProcessPlaylistsJob> _logger; private readonly ILogger<ProcessPlaylistsJob> _logger;
private readonly YouTubeParser _youTubeParser; private readonly YouTubeParser _youTubeParser;
private readonly MixcloudParser _mixcloudParser; private readonly MixcloudParser _mixcloudParser;
private readonly IUnitOfWork _unitOfWork; private readonly IUnitOfWork _unitOfWork;
public ProcessPlaylistsJob(IPlaylistRepository playlistRepository, IEntryRepository entryRepository, public ProcessPlaylistsJob(IPlaylistRepository playlistRepository, IEntryRepository entryRepository,
IUnitOfWork unitOfWork, IOptions<ApplicationsSettings> applicationsSettings, IUnitOfWork unitOfWork, IOptions<HelpersSettings> helpersSettings,
ILoggerFactory logger, YouTubeParser youTubeParser, MixcloudParser mixcloudParser) { ILoggerFactory logger, YouTubeParser youTubeParser, MixcloudParser mixcloudParser) {
this._unitOfWork = unitOfWork; this._unitOfWork = unitOfWork;
this._youTubeParser = youTubeParser; this._youTubeParser = youTubeParser;
this._mixcloudParser = mixcloudParser; this._mixcloudParser = mixcloudParser;
this._playlistRepository = playlistRepository; this._playlistRepository = playlistRepository;
this._entryRepository = entryRepository; this._entryRepository = entryRepository;
this._applicationsSettings = applicationsSettings.Value; this._helpersSettings = helpersSettings.Value;
this._logger = logger.CreateLogger<ProcessPlaylistsJob>(); this._logger = logger.CreateLogger<ProcessPlaylistsJob>();
} }
@@ -40,7 +41,7 @@ namespace PodNoms.Api.Services.Jobs {
var resultList = new List<ParsedItemResult>(); var resultList = new List<ParsedItemResult>();
foreach (var playlist in playlists) { foreach (var playlist in playlists) {
var downloader = new AudioDownloader(playlist.SourceUrl, _applicationsSettings.Downloader); var downloader = new AudioDownloader(playlist.SourceUrl, _helpersSettings.Downloader);
var info = downloader.GetInfo(); var info = downloader.GetInfo();
var id = ((PlaylistDownloadInfo)downloader.RawProperties).Id; var id = ((PlaylistDownloadInfo)downloader.RawProperties).Id;
if (info == AudioType.Playlist && downloader.RawProperties is PlaylistDownloadInfo) { if (info == AudioType.Playlist && downloader.RawProperties is PlaylistDownloadInfo) {

View File

@@ -10,6 +10,7 @@ using PodNoms.Api.Models;
using System.Net; using System.Net;
using PodNoms.Api.Utils; using PodNoms.Api.Utils;
using HandlebarsDotNet; using HandlebarsDotNet;
using PodNoms.Api.Models.Settings;
namespace PodNoms.Api.Services { namespace PodNoms.Api.Services {
public class MailgunSender : IMailSender { public class MailgunSender : IMailSender {

View File

@@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services.Realtime; using PodNoms.Api.Services.Realtime;

View File

@@ -11,6 +11,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Services.Downloader; using PodNoms.Api.Services.Downloader;
@@ -23,14 +24,14 @@ namespace PodNoms.Api.Services.Processor {
private readonly IUnitOfWork _unitOfWork; private readonly IUnitOfWork _unitOfWork;
private readonly IEntryRepository _repository; private readonly IEntryRepository _repository;
public ApplicationsSettings _applicationsSettings { get; } public HelpersSettings _helpersSettings { get; }
private readonly HubLifetimeManager<UserUpdatesHub> _userUpdateHub; private readonly HubLifetimeManager<UserUpdatesHub> _userUpdateHub;
public UrlProcessService(IEntryRepository repository, IUnitOfWork unitOfWork, public UrlProcessService(IEntryRepository repository, IUnitOfWork unitOfWork,
IFileUploader fileUploader, IOptions<ApplicationsSettings> applicationsSettings, IFileUploader fileUploader, IOptions<HelpersSettings> helpersSettings,
HubLifetimeManager<UserUpdatesHub> userUpdateHub, HubLifetimeManager<UserUpdatesHub> userUpdateHub,
ILoggerFactory logger, IMapper mapper, IRealTimeUpdater pusher) : base(logger, mapper, pusher) { ILoggerFactory logger, IMapper mapper, IRealTimeUpdater pusher) : base(logger, mapper, pusher) {
this._applicationsSettings = applicationsSettings.Value; this._helpersSettings = helpersSettings.Value;
this._repository = repository; this._repository = repository;
this._unitOfWork = unitOfWork; this._unitOfWork = unitOfWork;
this._userUpdateHub = userUpdateHub; this._userUpdateHub = userUpdateHub;
@@ -56,7 +57,7 @@ namespace PodNoms.Api.Services.Processor {
public async Task<AudioType> GetInformation(PodcastEntry entry) { public async Task<AudioType> GetInformation(PodcastEntry entry) {
var downloader = new AudioDownloader(entry.SourceUrl, _applicationsSettings.Downloader); var downloader = new AudioDownloader(entry.SourceUrl, _helpersSettings.Downloader);
var ret = downloader.GetInfo(); var ret = downloader.GetInfo();
if (ret == AudioType.Valid) { if (ret == AudioType.Valid) {
entry.Title = downloader.Properties?.Title; entry.Title = downloader.Properties?.Title;
@@ -81,7 +82,7 @@ namespace PodNoms.Api.Services.Processor {
if (entry == null) if (entry == null)
return false; return false;
try { try {
var downloader = new AudioDownloader(entry.SourceUrl, _applicationsSettings.Downloader); var downloader = new AudioDownloader(entry.SourceUrl, _helpersSettings.Downloader);
var outputFile = var outputFile =
Path.Combine(System.IO.Path.GetTempPath(), $"{System.Guid.NewGuid().ToString()}.mp3"); Path.Combine(System.IO.Path.GetTempPath(), $"{System.Guid.NewGuid().ToString()}.mp3");

View File

@@ -13,10 +13,10 @@ namespace PodNoms.Api.Services.Push {
private readonly ILogger<PushServicePushNotificationService> _logger; private readonly ILogger<PushServicePushNotificationService> _logger;
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
public string PublicKey => _options.PublicKey; public string PublicKey => _options.PublicKey;
public FirebasePushNotificationService(IOptions<PushNotificationServiceOptions> optionsAccessor, public FirebasePushNotificationService(IOptions<PushNotificationServiceOptions> pushOptions,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
ILogger<PushServicePushNotificationService> logger) { ILogger<PushServicePushNotificationService> logger) {
_options = optionsAccessor.Value; _options = pushOptions.Value;
_logger = logger; _logger = logger;
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
} }

View File

@@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob; using Microsoft.WindowsAzure.Storage.Blob;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Services.Processor; using PodNoms.Api.Services.Processor;
using PodNoms.Api.Services.Realtime; using PodNoms.Api.Services.Realtime;
using PodNoms.Api.Utils.Extensions; using PodNoms.Api.Utils.Extensions;
@@ -28,7 +29,7 @@ namespace PodNoms.Api.Services.Storage {
CloudBlockBlob blockBlob = container.GetBlockBlobReference(destinationFile); CloudBlockBlob blockBlob = container.GetBlockBlobReference(destinationFile);
blockBlob.Properties.ContentType = contentType; blockBlob.Properties.ContentType = contentType;
var blockSize = 256 * 1024; var blockSize = 256 * 1024;
blockBlob.StreamWriteSizeInBytes = blockSize; blockBlob.StreamWriteSizeInBytes = blockSize;
long bytesToUpload = (new FileInfo(sourceFile)).Length; long bytesToUpload = (new FileInfo(sourceFile)).Length;

View File

@@ -0,0 +1,55 @@
using System.Threading.Tasks;
using Lib.Net.Http.WebPush;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Services.Auth;
using PodNoms.Api.Services.Hubs;
using PodNoms.Api.Services.Push;
using WebPush = Lib.Net.Http.WebPush;
namespace PodNoms.Api.Services {
public class SupportChatService : ISupportChatService {
private readonly ChatSettings _chatSettings;
private readonly IPushNotificationService _notificationService;
private readonly HubLifetimeManager<ChatHub> _chatHub;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IPushSubscriptionStore _subscriptionStore;
private readonly HubLifetimeManager<ChatHub> _hub;
public SupportChatService(UserManager<ApplicationUser> userManager, IOptions<ChatSettings> chatSettings,
IPushSubscriptionStore subscriptionStore, IPushNotificationService notificationService,
HubLifetimeManager<ChatHub> chatHub) {
this._chatSettings = chatSettings.Value;
this._notificationService = notificationService;
this._chatHub = chatHub;
this._userManager = userManager;
this._subscriptionStore = subscriptionStore;
}
public async Task<bool> InitiateSupportRequest(string fromUser, ChatViewModel message) {
if (!string.IsNullOrEmpty(_chatSettings.CurrentChatUser)) {
var user = await _userManager.FindByEmailAsync(_chatSettings.CurrentChatUser);
if (!string.IsNullOrEmpty(user?.Id)) {
message.ToUserId = user.Id;
message.ToUserName = user.FullName;
//send firebase message to notify via web worker
WebPush.PushMessage pushMessage = new WebPush.PushMessage(message.Message) {
Topic = "New support chat message",
Urgency = PushMessageUrgency.Normal
};
await _subscriptionStore.ForEachSubscriptionAsync(user.Id, (WebPush.PushSubscription subscription) => {
_notificationService.SendNotificationAsync(subscription, pushMessage);
});
//send SignalR message to notify in chat.component
await _chatHub.SendUserAsync(user.Email, "SendMessage", new object[] { message });
return true;
}
}
return false;
}
}
}

View File

@@ -26,6 +26,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Models.ViewModels;
using PodNoms.Api.Persistence; using PodNoms.Api.Persistence;
using PodNoms.Api.Providers; using PodNoms.Api.Providers;
@@ -97,9 +98,10 @@ namespace PodNoms.Api {
services.AddOptions(); services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection("App")); services.Configure<AppSettings>(Configuration.GetSection("App"));
services.Configure<StorageSettings>(Configuration.GetSection("Storage")); services.Configure<StorageSettings>(Configuration.GetSection("Storage"));
services.Configure<ApplicationsSettings>(Configuration.GetSection("ApplicationsSettings")); services.Configure<HelpersSettings>(Configuration.GetSection("HelpersSettings"));
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings")); services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
services.Configure<FacebookAuthSettings>(Configuration.GetSection("FacebookAuthSettings")); services.Configure<FacebookAuthSettings>(Configuration.GetSection("FacebookAuthSettings"));
services.Configure<ChatSettings>(Configuration.GetSection("ChatSettings"));
services.Configure<ImageFileStorageSettings>(Configuration.GetSection("ImageFileStorageSettings")); services.Configure<ImageFileStorageSettings>(Configuration.GetSection("ImageFileStorageSettings"));
services.Configure<AudioFileStorageSettings>(Configuration.GetSection("AudioFileStorageSettings")); services.Configure<AudioFileStorageSettings>(Configuration.GetSection("AudioFileStorageSettings"));
services.Configure<FormOptions>(options => { services.Configure<FormOptions>(options => {
@@ -188,7 +190,7 @@ namespace PodNoms.Api {
.AddJsonOptions(options => { .AddJsonOptions(options => {
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>()); .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
@@ -201,10 +203,24 @@ namespace PodNoms.Api {
x.ValueLengthLimit = int.MaxValue; x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = int.MaxValue; // In case of multipart x.MultipartBodyLengthLimit = int.MaxValue; // In case of multipart
}); });
services.AddSignalR(config => { });
services.AddSignalR()
.AddJsonProtocol(options => options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver() {
NamingStrategy = new CamelCaseNamingStrategy() {
ProcessDictionaryKeys = true
}
});
services.AddCors(options => { services.AddCors(options => {
options.AddPolicy("AllowAllOrigins", options.AddPolicy("PodNomsClientPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:4200", "https://*.podnoms.com")
.AllowCredentials());
});
services.AddCors(options => {
options.AddPolicy("AllowAllPolicy",
builder => builder builder => builder
.AllowAnyOrigin() .AllowAnyOrigin()
.AllowAnyMethod() .AllowAnyMethod()
@@ -224,6 +240,7 @@ namespace PodNoms.Api {
services.AddScoped<IUrlProcessService, UrlProcessService>(); services.AddScoped<IUrlProcessService, UrlProcessService>();
services.AddScoped<INotifyJobCompleteService, NotifyJobCompleteService>(); services.AddScoped<INotifyJobCompleteService, NotifyJobCompleteService>();
services.AddScoped<IAudioUploadProcessService, AudioUploadProcessService>(); services.AddScoped<IAudioUploadProcessService, AudioUploadProcessService>();
services.AddScoped<ISupportChatService, SupportChatService>();
services.AddScoped<IMailSender, MailgunSender>(); services.AddScoped<IMailSender, MailgunSender>();
services.AddScoped<YouTubeParser>(); services.AddScoped<YouTubeParser>();
services.AddScoped<MixcloudParser>(); services.AddScoped<MixcloudParser>();
@@ -271,7 +288,7 @@ namespace PodNoms.Api {
}); });
app.UseAuthentication(); app.UseAuthentication();
app.UseCors("AllowAllOrigins"); app.UseCors("AllowAllPolicy");
app.UseSignalR(routes => { app.UseSignalR(routes => {
routes.MapHub<AudioProcessingHub>("/hubs/audioprocessing"); routes.MapHub<AudioProcessingHub>("/hubs/audioprocessing");

View File

@@ -6,6 +6,7 @@ using Google.Apis.Services;
using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using PodNoms.Api.Models; using PodNoms.Api.Models;
using PodNoms.Api.Models.Settings;
namespace PodNoms.Api.Utils.RemoteParsers { namespace PodNoms.Api.Utils.RemoteParsers {
public partial class YouTubeParser { public partial class YouTubeParser {

View File

@@ -23,7 +23,7 @@
"Key": "9f59ab0666214980ef76", "Key": "9f59ab0666214980ef76",
"Cluster": "eu" "Cluster": "eu"
}, },
"ApplicationsSettings": { "HelpersSettings": {
"Downloader": "/usr/local/bin/youtube-dl" "Downloader": "/usr/local/bin/youtube-dl"
}, },
"ImageFileStorageSettings": { "ImageFileStorageSettings": {