mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-20 13:23:20 -07:00
Added the new notification provider to the settings page
This commit is contained in:
parent
a6c6894010
commit
03fc7d4c19
14 changed files with 250 additions and 23 deletions
|
@ -1,4 +1,6 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Ombi.Helpers;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
@ -14,17 +16,18 @@ namespace Ombi.Api.CloudService
|
||||||
{
|
{
|
||||||
private readonly IApi _api;
|
private readonly IApi _api;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private const string BaseUrl = "https://ombinotifications.azurewebsites.net/api/";
|
private readonly string _baseUrl;
|
||||||
|
|
||||||
public CloudMobileNotification(IApi api, ILogger<CloudMobileNotification> logger)
|
public CloudMobileNotification(IApi api, ILogger<CloudMobileNotification> logger, IOptions<ApplicationSettings> settings)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
|
_baseUrl = settings.Value.NotificationService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SendMessage(MobileNotificationRequest notification)
|
public async Task<bool> SendMessage(MobileNotificationRequest notification)
|
||||||
{
|
{
|
||||||
var request = new Request("MobileNotification", BaseUrl, HttpMethod.Post);
|
var request = new Request("MobileNotification", _baseUrl, HttpMethod.Post);
|
||||||
request.AddJsonBody(notification);
|
request.AddJsonBody(notification);
|
||||||
var response = await _api.Request(request);
|
var response = await _api.Request(request);
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
public class ApplicationSettings
|
public class ApplicationSettings
|
||||||
{
|
{
|
||||||
public string OmbiService { get; set; }
|
public string OmbiService { get; set; }
|
||||||
|
public string NotificationService { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -71,6 +71,18 @@ export interface IMobileUsersViewModel {
|
||||||
devices: number;
|
devices: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ICloudMobileModel {
|
||||||
|
userId: string;
|
||||||
|
username: string;
|
||||||
|
devices: ICloudMobileDevices[];
|
||||||
|
}
|
||||||
|
export interface ICloudMobileDevices {
|
||||||
|
token: string;
|
||||||
|
userId: string;
|
||||||
|
addedAt: Date;
|
||||||
|
user: IUser;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IMassEmailUserModel {
|
export interface IMassEmailUserModel {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
|
22
src/Ombi/ClientApp/src/app/services/cloudmobile.service.ts
Normal file
22
src/Ombi/ClientApp/src/app/services/cloudmobile.service.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { APP_BASE_HREF } from "@angular/common";
|
||||||
|
import { Injectable, Inject } from "@angular/core";
|
||||||
|
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
|
import { ICloudMobileDevices, ICloudMobileModel } from "../interfaces";
|
||||||
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CloudMobileService extends ServiceHelpers {
|
||||||
|
constructor(http: HttpClient, @Inject(APP_BASE_HREF) href:string) {
|
||||||
|
super(http, "/api/v2/mobile/", href);
|
||||||
|
}
|
||||||
|
public getDevices(): Observable<ICloudMobileModel[]> {
|
||||||
|
return this.http.get<ICloudMobileModel[]>(`${this.url}users/`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
public send(userId: string, message: string): Promise<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}send/`, { userId, message }, {headers: this.headers}).toPromise();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<settings-menu>
|
||||||
|
</settings-menu>
|
||||||
|
<div *ngIf="form" class="container">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Mobile Notifications</legend>
|
||||||
|
|
||||||
|
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
<table *ngIf="devices" mat-table [dataSource]="devices" class="mat-elevation-z8">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="select">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let row">
|
||||||
|
<mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="username">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Username </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.username}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="md-form-field">
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Message</mat-label>
|
||||||
|
<textarea matInput [(ngModel)]="message" [ngModelOptions]="{standalone: true}"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="md-form-field">
|
||||||
|
<button mat-raised-button type="button" color="primary" (click)="sendMessage(form)" [disabled]="selection.selected.length === 0">Send Notification</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<notification-templates [templates]="templates" [showSubject]="false"></notification-templates>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md-form-field ">
|
||||||
|
<div>
|
||||||
|
<button mat-raised-button type="submit " color="primary" [disabled]="form.invalid ">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
|
import { IMobileNotifcationSettings, IMobileUsersViewModel, INotificationTemplates, NotificationType, ICloudMobileDevices, ICloudMobileModel } from "../../interfaces";
|
||||||
|
import { TesterService } from "../../services";
|
||||||
|
import { NotificationService } from "../../services";
|
||||||
|
import { MobileService, SettingsService } from "../../services";
|
||||||
|
import { CloudMobileService } from "../../services/cloudmobile.service";
|
||||||
|
import { SelectionModel } from "@angular/cdk/collections";
|
||||||
|
import { MatTableDataSource } from "@angular/material";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./cloudmobile.component.html",
|
||||||
|
})
|
||||||
|
export class CloudMobileComponent implements OnInit {
|
||||||
|
|
||||||
|
public NotificationType = NotificationType;
|
||||||
|
public templates: INotificationTemplates[];
|
||||||
|
public form: FormGroup;
|
||||||
|
public devices: MatTableDataSource<ICloudMobileModel>;
|
||||||
|
public selection = new SelectionModel<ICloudMobileModel>(true, []);
|
||||||
|
displayedColumns: string[] = ['select', 'username'];
|
||||||
|
public message: string;
|
||||||
|
|
||||||
|
constructor(private settingsService: SettingsService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private mobileService: CloudMobileService) { }
|
||||||
|
|
||||||
|
public async ngOnInit() {
|
||||||
|
this.settingsService.getMobileNotificationSettings().subscribe(x => {
|
||||||
|
this.templates = x.notificationTemplates;
|
||||||
|
|
||||||
|
this.form = this.fb.group({
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await this.mobileService.getDevices().toPromise();
|
||||||
|
if (result.length > 0) {
|
||||||
|
this.devices = new MatTableDataSource(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSubmit(form: FormGroup) {
|
||||||
|
if (form.invalid) {
|
||||||
|
this.notificationService.error("Please check your entered values");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = <IMobileNotifcationSettings> form.value;
|
||||||
|
settings.notificationTemplates = this.templates;
|
||||||
|
|
||||||
|
this.settingsService.saveMobileNotificationSettings(settings).subscribe(x => {
|
||||||
|
if (x) {
|
||||||
|
this.notificationService.success("Successfully saved the Mobile settings");
|
||||||
|
} else {
|
||||||
|
this.notificationService.success("There was an error when saving the Mobile settings");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendMessage(form: FormGroup) {
|
||||||
|
if (form.invalid) {
|
||||||
|
this.notificationService.error("Please check your entered values");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.selection.selected.length <= 0) {
|
||||||
|
this.notificationService.warning("Warning", "Please select a user to send the test notification");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.selection.selected.forEach(async (u) => {
|
||||||
|
await this.mobileService.send(u.userId, this.message);
|
||||||
|
|
||||||
|
this.notificationService.success(
|
||||||
|
"Successfully sent a Mobile message");
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
</settings-menu>
|
</settings-menu>
|
||||||
<div *ngIf="form">
|
<div *ngIf="form">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Mobile Notifications</legend>
|
<legend>Legacy Mobile Notifications</legend>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
|
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -58,6 +58,8 @@ import { HubService } from "../services/hub.service";
|
||||||
import { LogsComponent } from "./logs/logs.component";
|
import { LogsComponent } from "./logs/logs.component";
|
||||||
import { TwilioComponent } from "./notifications/twilio/twilio.component";
|
import { TwilioComponent } from "./notifications/twilio/twilio.component";
|
||||||
import { WhatsAppComponent } from "./notifications/twilio/whatsapp.component";
|
import { WhatsAppComponent } from "./notifications/twilio/whatsapp.component";
|
||||||
|
import { CloudMobileComponent } from "./notifications/cloudmobile.coponent";
|
||||||
|
import { CloudMobileService } from "../services/cloudmobile.service";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "Ombi", component: OmbiComponent, canActivate: [AuthGuard] },
|
{ path: "Ombi", component: OmbiComponent, canActivate: [AuthGuard] },
|
||||||
|
@ -94,6 +96,7 @@ const routes: Routes = [
|
||||||
{ path: "TheMovieDb", component: TheMovieDbComponent, canActivate: [AuthGuard] },
|
{ path: "TheMovieDb", component: TheMovieDbComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "FailedRequests", component: FailedRequestsComponent, canActivate: [AuthGuard] },
|
{ path: "FailedRequests", component: FailedRequestsComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Logs", component: LogsComponent, canActivate: [AuthGuard] },
|
{ path: "Logs", component: LogsComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: "CloudMobile", component: CloudMobileComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -157,6 +160,7 @@ const routes: Routes = [
|
||||||
LogsComponent,
|
LogsComponent,
|
||||||
TwilioComponent,
|
TwilioComponent,
|
||||||
WhatsAppComponent,
|
WhatsAppComponent,
|
||||||
|
CloudMobileComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
@ -181,6 +185,7 @@ const routes: Routes = [
|
||||||
SystemService,
|
SystemService,
|
||||||
FileDownloadService,
|
FileDownloadService,
|
||||||
TheMovieDbService,
|
TheMovieDbService,
|
||||||
|
CloudMobileService,
|
||||||
],
|
],
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -38,7 +38,8 @@
|
||||||
|
|
||||||
<button mat-button [matMenuTriggerFor]="notificationMenu"><i class="fa fa-bell-o" aria-hidden="true"></i> Notifications</button>
|
<button mat-button [matMenuTriggerFor]="notificationMenu"><i class="fa fa-bell-o" aria-hidden="true"></i> Notifications</button>
|
||||||
<mat-menu #notificationMenu="matMenu">
|
<mat-menu #notificationMenu="matMenu">
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Mobile']">Mobile</button>
|
<button mat-menu-item [routerLink]="['/Settings/CloudMobile']">Mobile</button>
|
||||||
|
<button mat-menu-item [routerLink]="['/Settings/Mobile']">Legacy Mobile</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Email']">Email</button>
|
<button mat-menu-item [routerLink]="['/Settings/Email']">Email</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/MassEmail']">MassEmail</button>
|
<button mat-menu-item [routerLink]="['/Settings/MassEmail']">MassEmail</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Newsletter']">Newsletter</button>
|
<button mat-menu-item [routerLink]="['/Settings/Newsletter']">Newsletter</button>
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace Ombi.Config
|
|
||||||
{
|
|
||||||
public class UserSettings
|
|
||||||
{
|
|
||||||
public string WebsiteUrl { get; set; }
|
|
||||||
public bool UseHttps { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,10 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ombi.Api.CloudService;
|
||||||
|
using Ombi.Attributes;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Models.V2;
|
using Ombi.Models.V2;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
@ -19,14 +22,16 @@ namespace Ombi.Controllers.V2
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class MobileController : ControllerBase
|
public class MobileController : ControllerBase
|
||||||
{
|
{
|
||||||
public MobileController(IRepository<MobileDevices> mobileDevices, OmbiUserManager user)
|
public MobileController(IRepository<MobileDevices> mobileDevices, OmbiUserManager user, ICloudMobileNotification mobileNotificationService)
|
||||||
{
|
{
|
||||||
_mobileDevices = mobileDevices;
|
_mobileDevices = mobileDevices;
|
||||||
_userManager = user;
|
_userManager = user;
|
||||||
|
_mobileNotificationService = mobileNotificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IRepository<MobileDevices> _mobileDevices;
|
private readonly IRepository<MobileDevices> _mobileDevices;
|
||||||
private readonly OmbiUserManager _userManager;
|
private readonly OmbiUserManager _userManager;
|
||||||
|
private readonly ICloudMobileNotification _mobileNotificationService;
|
||||||
|
|
||||||
[HttpPost("Notification")]
|
[HttpPost("Notification")]
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
@ -90,5 +95,47 @@ namespace Ombi.Controllers.V2
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost("Send")]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
[Admin]
|
||||||
|
public async Task<IActionResult> SendNotification([FromBody] SendMobileNotification model)
|
||||||
|
{
|
||||||
|
|
||||||
|
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == model.UserId);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
// Check if we already have this notification id
|
||||||
|
var currentDevices = await _mobileDevices.GetAll().Where(x => x.UserId == model.UserId).ToListAsync();
|
||||||
|
|
||||||
|
if (currentDevices == null || !currentDevices.Any())
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var d in currentDevices)
|
||||||
|
{
|
||||||
|
await _mobileNotificationService.SendMessage(new MobileNotificationRequest
|
||||||
|
{
|
||||||
|
To = d.Token,
|
||||||
|
Body = model.Message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("Users")] [ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
[Admin]
|
||||||
|
public async Task<IActionResult> GetUsers()
|
||||||
|
{
|
||||||
|
var devices = await _mobileDevices.GetAll().Include(x => x.User).ToListAsync();
|
||||||
|
var unique = devices.GroupBy(x => x.UserId, (key, g) => new { UserId = key, Username = g.Select(x => x.User.UserName).FirstOrDefault(), Devices = g.ToList() });
|
||||||
|
return Ok(unique);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,6 @@ namespace Ombi
|
||||||
public static void AddAppSettingsValues(this IServiceCollection services, IConfigurationRoot configuration)
|
public static void AddAppSettingsValues(this IServiceCollection services, IConfigurationRoot configuration)
|
||||||
{
|
{
|
||||||
services.Configure<ApplicationSettings>(configuration.GetSection("ApplicationSettings"));
|
services.Configure<ApplicationSettings>(configuration.GetSection("ApplicationSettings"));
|
||||||
services.Configure<UserSettings>(configuration.GetSection("UserSettings"));
|
|
||||||
services.Configure<TokenAuthentication>(configuration.GetSection("TokenAuthentication"));
|
services.Configure<TokenAuthentication>(configuration.GetSection("TokenAuthentication"));
|
||||||
services.Configure<LandingPageBackground>(configuration.GetSection("LandingPageBackground"));
|
services.Configure<LandingPageBackground>(configuration.GetSection("LandingPageBackground"));
|
||||||
services.Configure<DemoLists>(configuration.GetSection("Demo"));
|
services.Configure<DemoLists>(configuration.GetSection("Demo"));
|
||||||
|
|
13
src/Ombi/Models/V2/SendMobileNotification.cs
Normal file
13
src/Ombi/Models/V2/SendMobileNotification.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Models.V2
|
||||||
|
{
|
||||||
|
public class SendMobileNotification
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,13 +11,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ApplicationSettings": {
|
"ApplicationSettings": {
|
||||||
"Verison": "{{VERSIONNUMBER}}",
|
"NotificationService": "https://ombinotifications.azurewebsites.net/api/",
|
||||||
"Branch": "{{BRANCH}}",
|
"OmbiService": "?"
|
||||||
"FriendlyVersion": "v3.0.0"
|
|
||||||
},
|
|
||||||
"UserSettings": {
|
|
||||||
"WebsiteUrl": "http://localhost:52038",
|
|
||||||
"UseHttps": false
|
|
||||||
},
|
},
|
||||||
"TokenAuthentication": {
|
"TokenAuthentication": {
|
||||||
"SecretKey": "secretkey_secretkey1234!"
|
"SecretKey": "secretkey_secretkey1234!"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue