mirror of
https://github.com/fergalmoran/podnoms.git
synced 2025-12-22 09:18:08 +00:00
SignalR auth working
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
"@angular/platform-browser": "6.0.0",
|
||||
"@angular/platform-browser-dynamic": "6.0.0",
|
||||
"@angular/router": "6.0.0",
|
||||
"@aspnet/signalr": "^1.0.0-rtm-30751",
|
||||
"@ngrx/effects": "^5.1.0",
|
||||
"@ngrx/store": "^5.1.0",
|
||||
"@ngrx/store-devtools": "^5.1.0",
|
||||
|
||||
@@ -23,12 +23,29 @@ export const ADD_SUCCESS = '[Chat] Add Chat Success';
|
||||
export const ADD_FAIL = '[Chat] Add Chat Fail';
|
||||
export class AddAction implements Action {
|
||||
readonly type = ADD;
|
||||
constructor(public payload: ChatModel) {
|
||||
}
|
||||
constructor(public payload: ChatModel) {}
|
||||
}
|
||||
export class AddSuccessAction implements Action {
|
||||
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) {
|
||||
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
|
||||
@@ -37,5 +54,6 @@ export type Actions =
|
||||
| LoadSuccessAction
|
||||
| LoadFailAction
|
||||
| AddAction
|
||||
| AddSuccessAction;
|
||||
|
||||
| AddSuccessAction
|
||||
| ReceiveAction
|
||||
| ReceiveSuccessAction;
|
||||
|
||||
@@ -58,13 +58,11 @@ export class AppComponent implements OnInit {
|
||||
const chatterChannel = `${p.id}`;
|
||||
this._signalrService
|
||||
.init('userupdates')
|
||||
.then((r) => {
|
||||
this._signalrService.connection.on(
|
||||
chatterChannel,
|
||||
(result) => {
|
||||
this._toastyService.info(result);
|
||||
}
|
||||
);
|
||||
.then((listener) => {
|
||||
listener.on<string>(chatterChannel)
|
||||
.subscribe(result => {
|
||||
this._toastyService.info(result);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(
|
||||
|
||||
@@ -156,7 +156,7 @@ export function provideConfig() {
|
||||
authDomain: 'podnoms-api.firebaseapp.com',
|
||||
databaseURL: 'https://podnoms-api.firebaseio.com',
|
||||
projectId: 'podnoms-api',
|
||||
storageBucket: '',
|
||||
storageBucket: 'podnoms-api.appspot.com',
|
||||
messagingSenderId: '357461672895'
|
||||
}),
|
||||
AngularFireDatabaseModule,
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-controls" *ngIf="!loading">
|
||||
<input class="message-input" placeholder="Type your message here..." type="text" #message (keyup.enter)="sendMessage(message)">
|
||||
<div class="chat-controls">
|
||||
<input #message class="message-input" placeholder="Type your message and press enter..." type="text"
|
||||
(keyup.enter)="sendMessage(message)">
|
||||
<div class="chat-extra">
|
||||
<a href="#">
|
||||
<span class="extra-tooltip">Attach Document</span>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, ViewChild, ElementRef } from '@angular/core';
|
||||
import { ProfileService } from '../../services/profile.service';
|
||||
import { SignalRService } from '../../services/signalr.service';
|
||||
import { ProfileModel } from 'app/models/profile.model';
|
||||
@@ -15,11 +15,17 @@ import { Observable } from 'rxjs/Observable';
|
||||
templateUrl: './chat-widget.component.html',
|
||||
styleUrls: ['./chat-widget.component.css']
|
||||
})
|
||||
export class ChatWidgetComponent implements OnInit {
|
||||
export class ChatWidgetComponent {
|
||||
chatActive: boolean = false;
|
||||
loading: boolean = false;
|
||||
user: ProfileModel;
|
||||
private user: ProfileModel;
|
||||
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(
|
||||
private _profileService: ProfileService,
|
||||
@@ -28,25 +34,17 @@ export class ChatWidgetComponent implements OnInit {
|
||||
) {
|
||||
this._profileService.getProfile().subscribe((p) => (this.user = p));
|
||||
this.messages$ = _store.select(fromChat.getChat);
|
||||
this.messages$.subscribe((r) => {
|
||||
if (r.length != 0) {
|
||||
this.chatActive = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
togglePopup() {
|
||||
this.chatActive = !this.chatActive;
|
||||
|
||||
if (this.chatActive && this.user) {
|
||||
this.loading = true;
|
||||
this._signalRService.init('chat').then(() => {
|
||||
this.loading = false;
|
||||
this._signalRService.connection.on('SendMessage', (message) => {
|
||||
console.log(
|
||||
'chat-widget.component',
|
||||
'SendMessage',
|
||||
message
|
||||
);
|
||||
});
|
||||
});
|
||||
if (this.chatActive) {
|
||||
setTimeout(() => this.messageEl.nativeElement.focus(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as fromEntriesActions from 'app/actions/entries.actions';
|
||||
import { PodcastService } from '../../../services/podcast.service';
|
||||
import { AudioService } from 'app/services/audio.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { AudioProcessingMessageModel } from 'app/models/audioprocessingmessage.model';
|
||||
|
||||
@Component({
|
||||
selector: '[app-entry-list-item]',
|
||||
@@ -39,23 +40,22 @@ export class EntryListItemComponent implements OnInit {
|
||||
) {
|
||||
this._signalRService
|
||||
.init('audioprocessing')
|
||||
.then(() => {
|
||||
.then((listener) => {
|
||||
const updateChannel: string = `${
|
||||
this.entry.uid
|
||||
}__progress_update`;
|
||||
const processedChannel: string = `${
|
||||
this.entry.uid
|
||||
}__info_processed`;
|
||||
this._signalRService.connection.on(
|
||||
updateChannel,
|
||||
(result) => {
|
||||
listener
|
||||
.on<AudioProcessingMessageModel>(updateChannel)
|
||||
.subscribe((result) => {
|
||||
this.percentageProcessed = result.percentage;
|
||||
this.currentSpeed = result.currentSpeed;
|
||||
}
|
||||
);
|
||||
this._signalRService.connection.on(
|
||||
processedChannel,
|
||||
(result) => {
|
||||
});
|
||||
listener
|
||||
.on<PodcastEntryModel>(processedChannel)
|
||||
.subscribe((result) => {
|
||||
this.entry = result;
|
||||
if (this.entry.processingStatus === 'Processed') {
|
||||
// only update the store when we're finished.
|
||||
@@ -65,8 +65,7 @@ export class EntryListItemComponent implements OnInit {
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch((err) =>
|
||||
console.error(
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
LoadAction,
|
||||
LOAD_FAIL
|
||||
} from './../actions/chat.actions';
|
||||
import { LoadAction, LOAD_FAIL } from './../actions/chat.actions';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
@@ -22,17 +19,26 @@ export class ChatEffects {
|
||||
.switchMap((payload: chat.LoadAction) =>
|
||||
this._service
|
||||
.get()
|
||||
.map(res => ({ type: chat.LOAD_SUCCESS, payload: res }))
|
||||
.catch(err => {
|
||||
.map((res) => ({ type: chat.LOAD_SUCCESS, payload: res }))
|
||||
.catch((err) => {
|
||||
console.error('ChatEffects', 'get$', err);
|
||||
return Observable.of({ type: chat.LOAD_FAIL });
|
||||
})
|
||||
);
|
||||
@Effect()
|
||||
put$ = this.actions$
|
||||
.ofType(chat.ADD)
|
||||
.switchMap((action: chat.AddAction) => this._service.send(action.payload))
|
||||
.map(res => ({ type: chat.ADD_SUCCESS, payload: res }));
|
||||
@Effect()
|
||||
put$ = this.actions$
|
||||
.ofType(chat.ADD)
|
||||
.switchMap((action: chat.AddAction) =>
|
||||
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) {}
|
||||
}
|
||||
|
||||
6
client/src/app/models/audioprocessingmessage.model.ts
Normal file
6
client/src/app/models/audioprocessingmessage.model.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export class AudioProcessingMessageModel {
|
||||
percentage: number;
|
||||
totalSize: string;
|
||||
currentSpeed: string;
|
||||
eTA: string;
|
||||
}
|
||||
@@ -33,7 +33,8 @@ export function reducer(state = initialState, action: chat.Actions): State {
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
case chat.ADD_SUCCESS: {
|
||||
case chat.ADD_SUCCESS:
|
||||
case chat.RECEIVE_SUCCESS: {
|
||||
const newResults = _.clone(state.result);
|
||||
newResults.push(action.payload);
|
||||
const newState = {
|
||||
|
||||
@@ -4,11 +4,35 @@ import { Observable } from 'rxjs/Observable';
|
||||
import { environment } from 'environments/environment';
|
||||
import { ChatModel } from 'app/models/chat.model';
|
||||
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()
|
||||
export class ChatService extends BaseService {
|
||||
constructor(private _http: HttpClient) {
|
||||
constructor(
|
||||
private _http: HttpClient,
|
||||
private _signalRService: SignalRService,
|
||||
private _store: Store<ApplicationState>
|
||||
) {
|
||||
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[]> {
|
||||
|
||||
@@ -18,7 +18,7 @@ export class MessagingService {
|
||||
private _pushRegistrationServer: PushRegistrationService
|
||||
) {
|
||||
this.messaging.usePublicVapidKey(
|
||||
'BKyhUqIVZLauKNA-DXPXbIVLj5XiWurHbRV_0Rd3BOjY5cU9GOrd5ptXVJ2CNExxdveKYzZevrep2CflKeqkyqo'
|
||||
'BP05eVFzqeEh54DaL3TOe6x_8UFs60nw_gfSrI5tdILjb5VnHwas0n7c_075tsc1w5fm87u9d4Dawj_YN13PSAI'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,37 @@
|
||||
import { PodnomsAuthService } from './podnoms-auth.service';
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HubConnection,
|
||||
HubConnectionBuilder,
|
||||
JsonHubProtocol,
|
||||
LogLevel
|
||||
} from '@aspnet/signalr';
|
||||
import { HubConnection, HubConnectionBuilder, LogLevel } from '@aspnet/signalr';
|
||||
import { environment } from 'environments/environment';
|
||||
import { Observable, Subscriber } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class SignalRService {
|
||||
public connection: HubConnection;
|
||||
private _connected: boolean = false;
|
||||
private _connection: HubConnection;
|
||||
|
||||
constructor(private _auth: PodnomsAuthService) {}
|
||||
public init(hub: string): Promise<void> {
|
||||
const url = `${environment.SIGNALR_HOST}/hubs/${hub}`;
|
||||
public init(hub: string): Promise<SignalRService> {
|
||||
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();
|
||||
const options: any = {
|
||||
transport: 0
|
||||
};
|
||||
|
||||
this.connection = new HubConnectionBuilder()
|
||||
.configureLogging(LogLevel.Error)
|
||||
.withUrl(url + '?token=' + token, options)
|
||||
.withHubProtocol(new JsonHubProtocol())
|
||||
.build();
|
||||
return this.connection.start();
|
||||
public on<T>(channel: string): Observable<T> {
|
||||
const listener = new Observable<T>((subscriber: Subscriber<T>) => {
|
||||
this._connection.on(channel, (message) => {
|
||||
const result: T = message as T;
|
||||
subscriber.next(result);
|
||||
});
|
||||
});
|
||||
if (!this._connected) {
|
||||
this._connection.start().then(() => (this._connected = true));
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,6 @@
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<!-- 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>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -15,6 +15,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Providers;
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels;
|
||||
using PodNoms.Api.Services;
|
||||
using PodNoms.Api.Services.Auth;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PodNoms.Api.Services.Auth;
|
||||
|
||||
[Authorize]
|
||||
public class BaseAuthController : Controller {
|
||||
private readonly ClaimsPrincipal _caller;
|
||||
protected readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
@@ -1,29 +1,40 @@
|
||||
using System.Threading.Tasks;
|
||||
using Lib.Net.Http.WebPush;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Options;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels;
|
||||
using PodNoms.Api.Services;
|
||||
using PodNoms.Api.Services.Auth;
|
||||
using PodNoms.Api.Services.Hubs;
|
||||
using PodNoms.Api.Services.Push;
|
||||
using WebPush = Lib.Net.Http.WebPush;
|
||||
|
||||
namespace PodNoms.Api.Controllers {
|
||||
[Route("[controller]")]
|
||||
[Authorize]
|
||||
public class ChatController : BaseAuthController {
|
||||
private readonly HubLifetimeManager<ChatHub> _hub;
|
||||
private readonly ISupportChatService _supportChatService;
|
||||
|
||||
public ChatController(IHttpContextAccessor contextAccessor, UserManager<ApplicationUser> userManager,
|
||||
HubLifetimeManager<ChatHub> chatHubContext) :
|
||||
ISupportChatService supportChatService) :
|
||||
base(contextAccessor, userManager) {
|
||||
this._hub = chatHubContext;
|
||||
this._supportChatService = supportChatService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<ChatViewModel>> Post([FromBody]ChatViewModel message) {
|
||||
await this._hub.SendAllAsync("SendMessage", new object[] { message.Message });
|
||||
return Ok(message);
|
||||
//need to lookup the current support host and notify them
|
||||
message.FromUserName = _applicationUser.FullName;
|
||||
message.FromUserId = _applicationUser.Id;
|
||||
if (await _supportChatService.InitiateSupportRequest(_userId, message)) {
|
||||
return Ok(message);
|
||||
}
|
||||
return Accepted(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services.Auth;
|
||||
using PodNoms.Api.Services.Downloader;
|
||||
@@ -24,7 +25,7 @@ namespace PodNoms.Api.Controllers {
|
||||
public class DebugController : BaseAuthController {
|
||||
private readonly StorageSettings _storageSettings;
|
||||
private readonly AudioFileStorageSettings _audioFileStorageSettings;
|
||||
private readonly ApplicationsSettings _applicationsSettings;
|
||||
private readonly HelpersSettings _helpersSettings;
|
||||
private readonly ImageFileStorageSettings _imageFileStorageSettings;
|
||||
private readonly HubLifetimeManager<DebugHub> _hubManager;
|
||||
private readonly IPushSubscriptionStore _subscriptionStore;
|
||||
@@ -34,7 +35,7 @@ namespace PodNoms.Api.Controllers {
|
||||
|
||||
public DebugController(IOptions<StorageSettings> settings, IOptions<AppSettings> appSettings,
|
||||
HubLifetimeManager<DebugHub> hubManager,
|
||||
IOptions<ApplicationsSettings> applicationsSettings,
|
||||
IOptions<HelpersSettings> helpersSettings,
|
||||
IOptions<AudioFileStorageSettings> audioFileStorageSettings,
|
||||
IOptions<ImageFileStorageSettings> imageFileStorageSettings,
|
||||
IPushSubscriptionStore subscriptionStore,
|
||||
@@ -43,7 +44,7 @@ namespace PodNoms.Api.Controllers {
|
||||
IHttpContextAccessor contextAccessor) : base(contextAccessor, userManager) {
|
||||
this._appSettings = appSettings.Value;
|
||||
this._storageSettings = settings.Value;
|
||||
this._applicationsSettings = applicationsSettings.Value;
|
||||
this._helpersSettings = helpersSettings.Value;
|
||||
this._audioFileStorageSettings = audioFileStorageSettings.Value;
|
||||
this._imageFileStorageSettings = imageFileStorageSettings.Value;
|
||||
this._hubManager = hubManager;
|
||||
@@ -59,8 +60,8 @@ namespace PodNoms.Api.Controllers {
|
||||
CdnUrl = _storageSettings.CdnUrl,
|
||||
AudioContainer = _audioFileStorageSettings.ContainerName,
|
||||
ImageContainer = _imageFileStorageSettings.ContainerName,
|
||||
YouTubeDlPath = _applicationsSettings.Downloader,
|
||||
YouTubeDlVersion = AudioDownloader.GetVersion(_applicationsSettings.Downloader),
|
||||
YouTubeDlPath = _helpersSettings.Downloader,
|
||||
YouTubeDlVersion = AudioDownloader.GetVersion(_helpersSettings.Downloader),
|
||||
OSVersion = System.Environment.OSVersion,
|
||||
RssUrl = _appSettings.RssUrl
|
||||
};
|
||||
|
||||
@@ -7,12 +7,14 @@ using AutoMapper;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services;
|
||||
@@ -24,6 +26,7 @@ using PodNoms.Api.Utils.RemoteParsers;
|
||||
|
||||
namespace PodNoms.Api.Controllers {
|
||||
[Route("[controller]")]
|
||||
[Authorize]
|
||||
public class EntryController : BaseAuthController {
|
||||
private readonly IPodcastRepository _podcastRepository;
|
||||
private readonly IEntryRepository _repository;
|
||||
|
||||
@@ -22,6 +22,7 @@ using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Processing.Transforms;
|
||||
using SixLabors.ImageSharp.Processing.Filters;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
|
||||
namespace PodNoms.Api.Controllers {
|
||||
[Authorize]
|
||||
@@ -42,6 +43,7 @@ namespace PodNoms.Api.Controllers {
|
||||
this._fileUploader = fileUploader;
|
||||
this._imageFileStorageSettings = imageFileStorageSettings.Value;
|
||||
this._repository = repository;
|
||||
//this._repository = repository;
|
||||
this._unitOfWork = unitOfWork;
|
||||
this._mapper = mapper;
|
||||
this._logger = loggerFactory.CreateLogger<ImageUploadController>();
|
||||
|
||||
@@ -23,17 +23,14 @@ namespace PodNoms.Api.Controllers {
|
||||
[Route("[controller]")]
|
||||
public class PodcastController : BaseAuthController {
|
||||
private readonly IPodcastRepository _repository;
|
||||
private readonly IOptions<AppSettings> _settings;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IUnitOfWork _uow;
|
||||
|
||||
public PodcastController(IPodcastRepository repository, IOptions<AppSettings> options,
|
||||
IMapper mapper, IUnitOfWork unitOfWork,
|
||||
public PodcastController(IPodcastRepository repository, IMapper mapper, IUnitOfWork unitOfWork,
|
||||
UserManager<ApplicationUser> userManager, IHttpContextAccessor contextAccessor)
|
||||
: base(contextAccessor, userManager) {
|
||||
this._uow = unitOfWork;
|
||||
this._repository = repository;
|
||||
this._settings = options;
|
||||
this._mapper = mapper;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels.RssViewModels;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services.Auth;
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace PodNoms.Api.Models {
|
||||
namespace PodNoms.Api.Models.Settings {
|
||||
public class AppSettings {
|
||||
public string Version { get; set; }
|
||||
public string SiteUrl { get; set; }
|
||||
public string RssUrl { get; set; }
|
||||
public string GoogleApiKey { get; set; }
|
||||
public string Downloader { get; set; }
|
||||
}
|
||||
}
|
||||
5
server/Models/Settings/ApplicationsSettings.cs
Normal file
5
server/Models/Settings/ApplicationsSettings.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace PodNoms.Api.Models.Settings {
|
||||
public class HelpersSettings {
|
||||
public string Downloader { get; set; }
|
||||
}
|
||||
}
|
||||
4
server/Models/Settings/AudioFileStorageSettings.cs
Normal file
4
server/Models/Settings/AudioFileStorageSettings.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace PodNoms.Api.Models.Settings {
|
||||
public class AudioFileStorageSettings : FileStorageSettings {
|
||||
}
|
||||
}
|
||||
5
server/Models/Settings/ChatSettings.cs
Normal file
5
server/Models/Settings/ChatSettings.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace PodNoms.Api.Models.Settings {
|
||||
public class ChatSettings {
|
||||
public string CurrentChatUser { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace PodNoms.Api.Models {
|
||||
namespace PodNoms.Api.Models.Settings {
|
||||
|
||||
public class EmailSettings {
|
||||
public string ApiKey { get; set; }
|
||||
15
server/Models/Settings/FileStorageSettings.cs
Normal file
15
server/Models/Settings/FileStorageSettings.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
4
server/Models/Settings/ImageFileStorageSettings.cs
Normal file
4
server/Models/Settings/ImageFileStorageSettings.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace PodNoms.Api.Models.Settings {
|
||||
public class ImageFileStorageSettings : FileStorageSettings {
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace PodNoms.Api.Models {
|
||||
namespace PodNoms.Api.Models.Settings {
|
||||
public class StorageSettings {
|
||||
public string ConnectionString { get; set; }
|
||||
public string CdnUrl { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
namespace PodNoms.Api.Models.ViewModels {
|
||||
public class ChatViewModel {
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
namespace PodNoms.Api.Models.ViewModels
|
||||
{
|
||||
namespace PodNoms.Api.Models.ViewModels {
|
||||
public class ProcessProgressEvent {
|
||||
public double Percentage { get; set; }
|
||||
public string TotalSize;
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services.Storage;
|
||||
using PodNoms.Api.Utils;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<PackageReference Include="Hangfire" Version="1.6.17" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
||||
<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.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
|
||||
<PackageReference Include="Handlebars.NetStandard" Version="1.8.1" />
|
||||
@@ -31,7 +31,7 @@
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-dev001179" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.10-pre20180223200113" />
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,5 +8,6 @@ namespace PodNoms.Api.Services.Auth {
|
||||
public long? FacebookId { get; set; }
|
||||
public string PictureUrl { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string FullName { get => $"{FirstName} {LastName}"; }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace PodNoms.Api.Services.Auth
|
||||
{
|
||||
public static class ClaimsPrincipalExtensions
|
||||
{
|
||||
public static string GetUserId(this ClaimsPrincipal principal)
|
||||
{
|
||||
namespace PodNoms.Api.Services.Auth {
|
||||
public static class ClaimsPrincipalExtensions {
|
||||
public static string GetUserId(this ClaimsPrincipal principal) {
|
||||
if (principal == null)
|
||||
throw new ArgumentNullException(nameof(principal));
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using PodNoms.Api.Services.Gravatar;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Utils;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
|
||||
namespace PodNoms.Api.Services.Auth {
|
||||
public class PodNomsUserManager : UserManager<ApplicationUser> {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
@@ -7,15 +8,12 @@ namespace PodNoms.Api.Services.Hubs {
|
||||
[Authorize]
|
||||
public class ChatHub : Hub {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
server/Services/ISupportChatService.cs
Normal file
8
server/Services/ISupportChatService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Options;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Blob;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Persistence;
|
||||
|
||||
namespace PodNoms.Api.Services.Jobs {
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace PodNoms.Api.Services.Jobs {
|
||||
IPushNotificationService notificationService) {
|
||||
this._notificationService = notificationService;
|
||||
this._subscriptionStore = subscriptionStore;
|
||||
|
||||
}
|
||||
public async Task NotifyUser(string userId, string title, string body, string image) {
|
||||
WebPush.PushMessage pushMessage = new WebPush.PushMessage(body) {
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services.Downloader;
|
||||
using PodNoms.Api.Services.Processor;
|
||||
@@ -16,12 +17,12 @@ namespace PodNoms.Api.Services.Jobs {
|
||||
private readonly IAudioUploadProcessService _uploadService;
|
||||
private readonly IConfiguration _options;
|
||||
private readonly IPodcastRepository _podcastRepository;
|
||||
private readonly ApplicationsSettings _applicationsSettings;
|
||||
private readonly HelpersSettings _helpersSettings;
|
||||
private readonly ILogger<ProcessPlaylistItemJob> _logger;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
public ProcessPlaylistItemJob(IPlaylistRepository playlistRepository, IEntryRepository entryRepository,
|
||||
IAudioUploadProcessService uploadService, IConfiguration options,
|
||||
IPodcastRepository podcastRepository, IOptions<ApplicationsSettings> applicationsSettings,
|
||||
IPodcastRepository podcastRepository, IOptions<HelpersSettings> _helpersSettings,
|
||||
IUnitOfWork unitOfWork, ILogger<ProcessPlaylistItemJob> logger) {
|
||||
this._unitOfWork = unitOfWork;
|
||||
this._playlistRepository = playlistRepository;
|
||||
@@ -29,7 +30,7 @@ namespace PodNoms.Api.Services.Jobs {
|
||||
this._uploadService = uploadService;
|
||||
this._options = options;
|
||||
this._podcastRepository = podcastRepository;
|
||||
this._applicationsSettings = applicationsSettings.Value;
|
||||
this._helpersSettings = _helpersSettings.Value;
|
||||
this._logger = logger;
|
||||
}
|
||||
public async Task Execute() {
|
||||
@@ -42,7 +43,7 @@ namespace PodNoms.Api.Services.Jobs {
|
||||
var item = await _playlistRepository.GetParsedItem(itemId, playlistId);
|
||||
if (item != null && !string.IsNullOrEmpty(item.VideoType) && item.VideoType.Equals("youtube")) {
|
||||
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();
|
||||
if (info == AudioType.Valid) {
|
||||
var podcast = await _podcastRepository.GetAsync(item.Playlist.PodcastId);
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NYoutubeDL.Models;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services.Downloader;
|
||||
using PodNoms.Api.Utils.RemoteParsers;
|
||||
@@ -17,21 +18,21 @@ namespace PodNoms.Api.Services.Jobs {
|
||||
public class ProcessPlaylistsJob : IJob {
|
||||
public readonly IPlaylistRepository _playlistRepository;
|
||||
public readonly IEntryRepository _entryRepository;
|
||||
private readonly ApplicationsSettings _applicationsSettings;
|
||||
private readonly HelpersSettings _helpersSettings;
|
||||
private readonly ILogger<ProcessPlaylistsJob> _logger;
|
||||
private readonly YouTubeParser _youTubeParser;
|
||||
private readonly MixcloudParser _mixcloudParser;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public ProcessPlaylistsJob(IPlaylistRepository playlistRepository, IEntryRepository entryRepository,
|
||||
IUnitOfWork unitOfWork, IOptions<ApplicationsSettings> applicationsSettings,
|
||||
IUnitOfWork unitOfWork, IOptions<HelpersSettings> helpersSettings,
|
||||
ILoggerFactory logger, YouTubeParser youTubeParser, MixcloudParser mixcloudParser) {
|
||||
this._unitOfWork = unitOfWork;
|
||||
this._youTubeParser = youTubeParser;
|
||||
this._mixcloudParser = mixcloudParser;
|
||||
this._playlistRepository = playlistRepository;
|
||||
this._entryRepository = entryRepository;
|
||||
this._applicationsSettings = applicationsSettings.Value;
|
||||
this._helpersSettings = helpersSettings.Value;
|
||||
this._logger = logger.CreateLogger<ProcessPlaylistsJob>();
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ namespace PodNoms.Api.Services.Jobs {
|
||||
var resultList = new List<ParsedItemResult>();
|
||||
|
||||
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 id = ((PlaylistDownloadInfo)downloader.RawProperties).Id;
|
||||
if (info == AudioType.Playlist && downloader.RawProperties is PlaylistDownloadInfo) {
|
||||
|
||||
@@ -10,6 +10,7 @@ using PodNoms.Api.Models;
|
||||
using System.Net;
|
||||
using PodNoms.Api.Utils;
|
||||
using HandlebarsDotNet;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
|
||||
namespace PodNoms.Api.Services {
|
||||
public class MailgunSender : IMailSender {
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services.Realtime;
|
||||
|
||||
@@ -11,6 +11,7 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Services.Downloader;
|
||||
@@ -23,14 +24,14 @@ namespace PodNoms.Api.Services.Processor {
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IEntryRepository _repository;
|
||||
|
||||
public ApplicationsSettings _applicationsSettings { get; }
|
||||
public HelpersSettings _helpersSettings { get; }
|
||||
private readonly HubLifetimeManager<UserUpdatesHub> _userUpdateHub;
|
||||
|
||||
public UrlProcessService(IEntryRepository repository, IUnitOfWork unitOfWork,
|
||||
IFileUploader fileUploader, IOptions<ApplicationsSettings> applicationsSettings,
|
||||
IFileUploader fileUploader, IOptions<HelpersSettings> helpersSettings,
|
||||
HubLifetimeManager<UserUpdatesHub> userUpdateHub,
|
||||
ILoggerFactory logger, IMapper mapper, IRealTimeUpdater pusher) : base(logger, mapper, pusher) {
|
||||
this._applicationsSettings = applicationsSettings.Value;
|
||||
this._helpersSettings = helpersSettings.Value;
|
||||
this._repository = repository;
|
||||
this._unitOfWork = unitOfWork;
|
||||
this._userUpdateHub = userUpdateHub;
|
||||
@@ -56,7 +57,7 @@ namespace PodNoms.Api.Services.Processor {
|
||||
|
||||
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();
|
||||
if (ret == AudioType.Valid) {
|
||||
entry.Title = downloader.Properties?.Title;
|
||||
@@ -81,7 +82,7 @@ namespace PodNoms.Api.Services.Processor {
|
||||
if (entry == null)
|
||||
return false;
|
||||
try {
|
||||
var downloader = new AudioDownloader(entry.SourceUrl, _applicationsSettings.Downloader);
|
||||
var downloader = new AudioDownloader(entry.SourceUrl, _helpersSettings.Downloader);
|
||||
var outputFile =
|
||||
Path.Combine(System.IO.Path.GetTempPath(), $"{System.Guid.NewGuid().ToString()}.mp3");
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ namespace PodNoms.Api.Services.Push {
|
||||
private readonly ILogger<PushServicePushNotificationService> _logger;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
public string PublicKey => _options.PublicKey;
|
||||
public FirebasePushNotificationService(IOptions<PushNotificationServiceOptions> optionsAccessor,
|
||||
public FirebasePushNotificationService(IOptions<PushNotificationServiceOptions> pushOptions,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ILogger<PushServicePushNotificationService> logger) {
|
||||
_options = optionsAccessor.Value;
|
||||
_options = pushOptions.Value;
|
||||
_logger = logger;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Blob;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Services.Processor;
|
||||
using PodNoms.Api.Services.Realtime;
|
||||
using PodNoms.Api.Utils.Extensions;
|
||||
|
||||
55
server/Services/SupportChatService.cs
Normal file
55
server/Services/SupportChatService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
using PodNoms.Api.Models.ViewModels;
|
||||
using PodNoms.Api.Persistence;
|
||||
using PodNoms.Api.Providers;
|
||||
@@ -97,9 +98,10 @@ namespace PodNoms.Api {
|
||||
services.AddOptions();
|
||||
services.Configure<AppSettings>(Configuration.GetSection("App"));
|
||||
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<FacebookAuthSettings>(Configuration.GetSection("FacebookAuthSettings"));
|
||||
services.Configure<ChatSettings>(Configuration.GetSection("ChatSettings"));
|
||||
services.Configure<ImageFileStorageSettings>(Configuration.GetSection("ImageFileStorageSettings"));
|
||||
services.Configure<AudioFileStorageSettings>(Configuration.GetSection("AudioFileStorageSettings"));
|
||||
services.Configure<FormOptions>(options => {
|
||||
@@ -188,7 +190,7 @@ namespace PodNoms.Api {
|
||||
.AddJsonOptions(options => {
|
||||
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
|
||||
})
|
||||
})
|
||||
.AddXmlSerializerFormatters()
|
||||
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
|
||||
|
||||
@@ -201,10 +203,24 @@ namespace PodNoms.Api {
|
||||
x.ValueLengthLimit = int.MaxValue;
|
||||
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 => {
|
||||
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
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
@@ -224,6 +240,7 @@ namespace PodNoms.Api {
|
||||
services.AddScoped<IUrlProcessService, UrlProcessService>();
|
||||
services.AddScoped<INotifyJobCompleteService, NotifyJobCompleteService>();
|
||||
services.AddScoped<IAudioUploadProcessService, AudioUploadProcessService>();
|
||||
services.AddScoped<ISupportChatService, SupportChatService>();
|
||||
services.AddScoped<IMailSender, MailgunSender>();
|
||||
services.AddScoped<YouTubeParser>();
|
||||
services.AddScoped<MixcloudParser>();
|
||||
@@ -271,7 +288,7 @@ namespace PodNoms.Api {
|
||||
});
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseCors("AllowAllOrigins");
|
||||
app.UseCors("AllowAllPolicy");
|
||||
|
||||
app.UseSignalR(routes => {
|
||||
routes.MapHub<AudioProcessingHub>("/hubs/audioprocessing");
|
||||
|
||||
@@ -6,6 +6,7 @@ using Google.Apis.Services;
|
||||
using Google.Apis.YouTube.v3;
|
||||
using Microsoft.Extensions.Options;
|
||||
using PodNoms.Api.Models;
|
||||
using PodNoms.Api.Models.Settings;
|
||||
|
||||
namespace PodNoms.Api.Utils.RemoteParsers {
|
||||
public partial class YouTubeParser {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"Key": "9f59ab0666214980ef76",
|
||||
"Cluster": "eu"
|
||||
},
|
||||
"ApplicationsSettings": {
|
||||
"HelpersSettings": {
|
||||
"Downloader": "/usr/local/bin/youtube-dl"
|
||||
},
|
||||
"ImageFileStorageSettings": {
|
||||
|
||||
Reference in New Issue
Block a user