Add option to set the base href.

This commit is contained in:
Roger Far 2023-06-11 12:31:13 -06:00
parent dbfc348945
commit 8418b26d70
12 changed files with 107 additions and 39 deletions

View file

@ -1,44 +1,45 @@
import { APP_BASE_HREF } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private http: HttpClient) {}
constructor(private http: HttpClient, @Inject(APP_BASE_HREF) private baseHref: string) {}
public isLoggedIn(): Observable<boolean> {
return this.http.get<boolean>(`/Api/Authentication/IsLoggedIn`);
return this.http.get<boolean>(`${this.baseHref}Api/Authentication/IsLoggedIn`);
}
public create(userName: string, password: string): Observable<void> {
return this.http.post<void>(`/Api/Authentication/Create`, {
return this.http.post<void>(`${this.baseHref}Api/Authentication/Create`, {
userName,
password,
});
}
public setupProvider(provider: string, token: string): Observable<void> {
return this.http.post<void>(`/Api/Authentication/SetupProvider`, {
return this.http.post<void>(`${this.baseHref}Api/Authentication/SetupProvider`, {
provider,
token,
});
}
public login(userName: string, password: string): Observable<void> {
return this.http.post<void>(`/Api/Authentication/Login`, {
return this.http.post<void>(`${this.baseHref}Api/Authentication/Login`, {
userName,
password,
});
}
public logout() {
return this.http.post<void>(`/Api/Authentication/Logout`, {});
return this.http.post<void>(`${this.baseHref}Api/Authentication/Logout`, {});
}
public update(userName: string, password: string): Observable<void> {
return this.http.post<void>(`/Api/Authentication/Update`, {
return this.http.post<void>(`${this.baseHref}Api/Authentication/Update`, {
userName,
password,
});

View file

@ -3,7 +3,7 @@
<div class="container">
<div class="columns is-centered">
<div class="column is-5-tablet is-5-desktop is-5-widescreen">
<img src="../../assets/logo.png" />
<img src="assets/logo.png" />
<div class="box">
<form (ngSubmit)="login()">
<div class="field">

View file

@ -1,7 +1,7 @@
<nav class="navbar is-dark is-fixed-top" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" routerLink="/">
<img src="../../assets/logo.png" />
<img src="assets/logo.png" />
</a>
<a

View file

@ -1,41 +1,42 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Profile } from './models/profile.model';
import { Setting } from './models/setting.model';
import { APP_BASE_HREF } from '@angular/common';
@Injectable({
providedIn: 'root',
})
export class SettingsService {
constructor(private http: HttpClient) {}
constructor(private http: HttpClient, @Inject(APP_BASE_HREF) private baseHref: string) {}
public get(): Observable<Setting[]> {
return this.http.get<Setting[]>(`/Api/Settings`);
return this.http.get<Setting[]>(`${this.baseHref}Api/Settings`);
}
public update(settings: Setting[]): Observable<void> {
return this.http.put<void>(`/Api/Settings`, settings);
return this.http.put<void>(`${this.baseHref}Api/Settings`, settings);
}
public getProfile(): Observable<Profile> {
return this.http.get<Profile>(`/Api/Settings/Profile`);
return this.http.get<Profile>(`${this.baseHref}Api/Settings/Profile`);
}
public testPath(path: string): Observable<void> {
return this.http.post<void>(`/Api/Settings/TestPath`, { path });
return this.http.post<void>(`${this.baseHref}Api/Settings/TestPath`, { path });
}
public testDownloadSpeed(): Observable<number> {
return this.http.get<number>(`/Api/Settings/TestDownloadSpeed`);
return this.http.get<number>(`${this.baseHref}Api/Settings/TestDownloadSpeed`);
}
public testWriteSpeed(): Observable<number> {
return this.http.get<number>(`/Api/Settings/TestWriteSpeed`);
return this.http.get<number>(`${this.baseHref}Api/Settings/TestWriteSpeed`);
}
public testAria2cConnection(url: string, secret: string): Observable<{ version: string }> {
return this.http.post<{ version: string }>(`/Api/Settings/TestAria2cConnection`, {
return this.http.post<{ version: string }>(`${this.baseHref}Api/Settings/TestAria2cConnection`, {
url,
secret,
});

View file

@ -3,7 +3,7 @@
<div class="container">
<div class="columns is-centered">
<div class="column is-5-tablet is-5-desktop is-5-widescreen">
<img src="../../assets/logo.png" />
<img src="assets/logo.png" />
<div class="box" *ngIf="step === 1">
<div class="notification is-primary">
Welcome to Real-Debrid Client. Please create your account by entering a username and password.
@ -52,7 +52,7 @@
>Use this link to sign up to AllDebrid.</a
>
<br />
<a href="https://www.premiumize.me/" target="_blank" rel="noopener"
<a href="https://www.premiumize.me/" target="_blank" rel="noopener"
>Use this link to sign up to Premiumize.</a
>
<br />
@ -64,7 +64,7 @@
<select [(ngModel)]="provider">
<option [value]="0">Real-Debrid</option>
<option [value]="1">AllDebrid</option>
<option [value]="2">Premiumize</option>
<option [value]="2">Premiumize</option>
</select>
</div>
</div>
@ -85,7 +85,7 @@
>https://alldebrid.com/apikeys/</a
>.
</p>
<p class="help" *ngIf="provider === 'Premiumize'">
<p class="help" *ngIf="provider === 'Premiumize'">
You can find your API key here:
<a href="https://www.premiumize.me/account" target="_blank" rel="noopener"
>https://www.premiumize.me/account</a

View file

@ -1,8 +1,9 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import * as signalR from '@microsoft/signalr';
import { Observable, Subject } from 'rxjs';
import { Torrent, TorrentFileAvailability } from './models/torrent.model';
import { APP_BASE_HREF } from '@angular/common';
@Injectable({
providedIn: 'root',
@ -12,7 +13,7 @@ export class TorrentService {
private connection: signalR.HubConnection;
constructor(private http: HttpClient) {
constructor(private http: HttpClient, @Inject(APP_BASE_HREF) private baseHref: string) {
this.connect();
}
@ -21,7 +22,10 @@ export class TorrentService {
return;
}
this.connection = new signalR.HubConnectionBuilder().withUrl('/hub').withAutomaticReconnect().build();
this.connection = new signalR.HubConnectionBuilder()
.withUrl(`${this.baseHref}hub`)
.withAutomaticReconnect()
.build();
this.connection.start().catch((err) => console.error(err));
this.connection.on('update', (torrents: Torrent[]) => {
@ -30,15 +34,15 @@ export class TorrentService {
}
public getList(): Observable<Torrent[]> {
return this.http.get<Torrent[]>(`/Api/Torrents`);
return this.http.get<Torrent[]>(`${this.baseHref}Api/Torrents`);
}
public get(torrentId: string): Observable<Torrent> {
return this.http.get<Torrent>(`/Api/Torrents/Get/${torrentId}`);
return this.http.get<Torrent>(`${this.baseHref}Api/Torrents/Get/${torrentId}`);
}
public uploadMagnet(magnetLink: string, torrent: Torrent): Observable<void> {
return this.http.post<void>(`/Api/Torrents/UploadMagnet`, {
return this.http.post<void>(`${this.baseHref}Api/Torrents/UploadMagnet`, {
magnetLink,
torrent,
});
@ -48,11 +52,11 @@ export class TorrentService {
const formData: FormData = new FormData();
formData.append('file', file);
formData.append('formData', JSON.stringify({ torrent }));
return this.http.post<void>(`/Api/Torrents/UploadFile`, formData);
return this.http.post<void>(`${this.baseHref}Api/Torrents/UploadFile`, formData);
}
public checkFilesMagnet(magnetLink: string): Observable<TorrentFileAvailability[]> {
return this.http.post<TorrentFileAvailability[]>(`/Api/Torrents/CheckFilesMagnet`, {
return this.http.post<TorrentFileAvailability[]>(`${this.baseHref}Api/Torrents/CheckFilesMagnet`, {
magnetLink,
});
}
@ -60,7 +64,7 @@ export class TorrentService {
public checkFiles(file: File): Observable<TorrentFileAvailability[]> {
const formData: FormData = new FormData();
formData.append('file', file);
return this.http.post<TorrentFileAvailability[]>(`/Api/Torrents/CheckFiles`, formData);
return this.http.post<TorrentFileAvailability[]>(`${this.baseHref}Api/Torrents/CheckFiles`, formData);
}
public delete(
@ -69,7 +73,7 @@ export class TorrentService {
deleteRdTorrent: boolean,
deleteLocalFiles: boolean
): Observable<void> {
return this.http.post<void>(`/Api/Torrents/Delete/${torrentId}`, {
return this.http.post<void>(`${this.baseHref}Api/Torrents/Delete/${torrentId}`, {
deleteData,
deleteRdTorrent,
deleteLocalFiles,
@ -77,14 +81,14 @@ export class TorrentService {
}
public retry(torrentId: string): Observable<void> {
return this.http.post<void>(`/Api/Torrents/Retry/${torrentId}`, {});
return this.http.post<void>(`${this.baseHref}Api/Torrents/Retry/${torrentId}`, {});
}
public retryDownload(downloadId: string): Observable<void> {
return this.http.post<void>(`/Api/Torrents/RetryDownload/${downloadId}`, {});
return this.http.post<void>(`${this.baseHref}Api/Torrents/RetryDownload/${downloadId}`, {});
}
public update(torrent: Torrent): Observable<void> {
return this.http.put<void>(`/Api/Torrents/Update`, torrent);
return this.http.put<void>(`${this.baseHref}Api/Torrents/Update`, torrent);
}
}

View file

@ -6,7 +6,8 @@
<base href="/" />
<script>
(function () {
window["_app_base"] = "/" + window.location.pathname.split("/")[1];
var pathSegment = window.location.pathname.split("/")[1];
window["_app_base"] = pathSegment ? "/" + pathSegment + "/" : "/";
console.log("setting base href to " + window["_app_base"]);
})();
</script>

View file

@ -6,6 +6,7 @@ public class AppSettings
public AppSettingsDatabase? Database { get; set; }
public Int32 Port { get; set; }
public String? BasePath { get; set; }
}
public class AppSettingsLogging

View file

@ -0,0 +1,54 @@
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using RdtClient.Data.Models.Internal;
namespace RdtClient.Service.Middleware;
public class BaseHrefMiddleware
{
private readonly RequestDelegate _next;
public BaseHrefMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, AppSettings appSettings)
{
var originalBody = context.Response.Body;
try
{
using var newBody = new MemoryStream();
context.Response.Body = newBody;
await _next(context);
context.Response.Body = originalBody;
newBody.Seek(0, SeekOrigin.Begin);
var responseBody = await new StreamReader(newBody).ReadToEndAsync();
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
if (context.Response.ContentType?.Contains("text/html") == true)
{
var basePath = $"/{appSettings.BasePath!.TrimStart('/').TrimEnd('/')}/";
responseBody = Regex.Replace(responseBody, @"<base href=""/""", @$"<base href=""{basePath}""");
responseBody = Regex.Replace(responseBody, "(<script.*?src=\")(.*?)(\".*?</script>)", $"$1{basePath}$2$3");
responseBody = Regex.Replace(responseBody, "(<link.*?href=\")(.*?)(\".*?>)", $"$1{basePath}$2$3");
await context.Response.WriteAsync(responseBody);
}
else
{
await context.Response.BodyWriter.WriteAsync(newBody.ToArray());
}
}
finally
{
context.Response.Body = originalBody;
}
}
}

View file

@ -164,6 +164,11 @@ try
}
});
if (!String.IsNullOrWhiteSpace(appSettings.BasePath))
{
app.UseMiddleware<BaseHrefMiddleware>();
}
app.UseRouting();
app.UseAuthentication();

View file

@ -17,7 +17,7 @@
},
"RdtClient.Web": {
"commandName": "Project",
"launchUrl": "weatherforecast",
"launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},

View file

@ -9,5 +9,6 @@
"Database": {
"Path": "/data/db/rdtclient.db"
},
"Port": "6500"
"Port": "6500",
"BasePath": null
}