Finish hostname override
This commit is contained in:
parent
dac43896ce
commit
f69e455996
|
@ -93,10 +93,7 @@ export class ViewSpeeddialComponent implements OnInit {
|
|||
if (this.image === null) return;
|
||||
|
||||
this.downloadService.downloadFile(
|
||||
this.imageService.CreateImageLinksFromID(
|
||||
this.image?.id,
|
||||
this.selectedFormat,
|
||||
).source,
|
||||
this.imageService.GetImageURL(this.image?.id, this.selectedFormat),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -104,10 +101,7 @@ export class ViewSpeeddialComponent implements OnInit {
|
|||
if (this.image === null) return;
|
||||
|
||||
this.downloadService.shareFile(
|
||||
this.imageService.CreateImageLinksFromID(
|
||||
this.image?.id,
|
||||
this.selectedFormat,
|
||||
).source,
|
||||
this.imageService.GetImageURL(this.image?.id, this.selectedFormat),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
Fail,
|
||||
FT,
|
||||
HasFailed,
|
||||
HasSuccess,
|
||||
HasSuccess
|
||||
} from 'picsur-shared/dist/types';
|
||||
import { ZodDtoStatic } from 'picsur-shared/dist/util/create-zod-dto';
|
||||
import { ParseMime2FileType } from 'picsur-shared/dist/util/parse-mime';
|
||||
|
@ -17,7 +17,7 @@ import { ApiError } from 'src/app/models/dto/api-error.dto';
|
|||
import { z } from 'zod';
|
||||
import { MultiPartRequest } from '../../models/dto/multi-part-request.dto';
|
||||
import { Logger } from '../logger/logger.service';
|
||||
import { KeyService } from '../storage/key.service';
|
||||
import { KeyStorageService } from '../storage/key-storage.service';
|
||||
|
||||
/*
|
||||
Proud of this, it works so smoooth
|
||||
|
@ -36,7 +36,7 @@ export class ApiService {
|
|||
}
|
||||
|
||||
constructor(
|
||||
private readonly keyService: KeyService,
|
||||
private readonly keyService: KeyStorageService,
|
||||
@Inject(WINDOW) private readonly windowRef: Window,
|
||||
) {}
|
||||
|
||||
|
|
|
@ -125,8 +125,9 @@ export class ImageService {
|
|||
|
||||
// Non api calls
|
||||
|
||||
public GetImageURL(image: string, filetype: string | null): string {
|
||||
const baseURL = this.infoService.getHostname();
|
||||
// Use for native images
|
||||
public GetImageURL(image: string, filetype: string | null, allowOverride = false): string {
|
||||
const baseURL = this.infoService.getHostname(allowOverride);
|
||||
const extension = FileType2Ext(filetype ?? '');
|
||||
|
||||
return `${baseURL}/i/${image}${
|
||||
|
@ -134,12 +135,23 @@ export class ImageService {
|
|||
}`;
|
||||
}
|
||||
|
||||
// Use for user facing urls
|
||||
public CreateImageLinks(imageURL: string): ImageLinks {
|
||||
return {
|
||||
source: imageURL,
|
||||
markdown: `![image](${imageURL})`,
|
||||
html: `<img src="${imageURL}" alt="image">`,
|
||||
rst: `.. image:: ${imageURL}`,
|
||||
bbcode: `[img]${imageURL}[/img]`,
|
||||
};
|
||||
}
|
||||
|
||||
public GetImageURLCustomized(
|
||||
image: string,
|
||||
filetype: string | null,
|
||||
options: ImageRequestParams,
|
||||
): string {
|
||||
const baseURL = this.GetImageURL(image, filetype);
|
||||
const baseURL = this.GetImageURL(image, filetype, true);
|
||||
const betterOptions = ImageRequestParams.zodSchema.safeParse(options);
|
||||
|
||||
if (!betterOptions.success) return baseURL;
|
||||
|
@ -169,20 +181,10 @@ export class ImageService {
|
|||
return baseURL + '?' + queryParams.join('&');
|
||||
}
|
||||
|
||||
public CreateImageLinks(imageURL: string): ImageLinks {
|
||||
return {
|
||||
source: imageURL,
|
||||
markdown: `![image](${imageURL})`,
|
||||
html: `<img src="${imageURL}" alt="image">`,
|
||||
rst: `.. image:: ${imageURL}`,
|
||||
bbcode: `[img]${imageURL}[/img]`,
|
||||
};
|
||||
}
|
||||
|
||||
public CreateImageLinksFromID(
|
||||
imageID: string,
|
||||
mime: string | null,
|
||||
): ImageLinks {
|
||||
return this.CreateImageLinks(this.GetImageURL(imageID, mime));
|
||||
return this.CreateImageLinks(this.GetImageURL(imageID, mime, true));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { BehaviorSubject, filter, Observable, take } from 'rxjs';
|
|||
import pkg from '../../../../package.json';
|
||||
import { ServerInfo } from '../../models/dto/server-info.dto';
|
||||
import { Logger } from '../logger/logger.service';
|
||||
import { InfoStorageService } from '../storage/info-storage.service';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@Injectable({
|
||||
|
@ -23,21 +24,16 @@ export class InfoService {
|
|||
return this.infoSubject.value;
|
||||
}
|
||||
|
||||
private infoSubject = new BehaviorSubject<ServerInfo>(new ServerInfo());
|
||||
private infoSubject = new BehaviorSubject<ServerInfo>(
|
||||
this.infoStorage.get() ?? new ServerInfo(),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly api: ApiService,
|
||||
@Inject(LOCATION) private readonly location: Location,
|
||||
private readonly api: ApiService,
|
||||
private readonly infoStorage: InfoStorageService,
|
||||
) {
|
||||
this.pollInfo().catch((e) => this.logger.warn(e));
|
||||
}
|
||||
|
||||
public async pollInfo(): AsyncFailable<ServerInfo> {
|
||||
const response = await this.api.get(InfoResponse, '/api/info');
|
||||
if (HasFailed(response)) return response;
|
||||
|
||||
this.infoSubject.next(response);
|
||||
return response;
|
||||
this.updateInfo().catch((e) => this.logger.warn(e));
|
||||
}
|
||||
|
||||
public async getLoadedSnapshot(): Promise<ServerInfo> {
|
||||
|
@ -58,12 +54,14 @@ export class InfoService {
|
|||
return pkg.version;
|
||||
}
|
||||
|
||||
public getHostname(): string {
|
||||
// const info = await this.getLoadedSnapshot();
|
||||
public getHostname(allowOverride = false): string {
|
||||
if (allowOverride) {
|
||||
const info = this.snapshot;
|
||||
|
||||
// if (info.host_override !== undefined) {
|
||||
// return info.host_override;
|
||||
// }
|
||||
if (info.host_override !== undefined) {
|
||||
return info.host_override;
|
||||
}
|
||||
}
|
||||
|
||||
return this.location.protocol + '//' + this.location.host;
|
||||
}
|
||||
|
@ -71,7 +69,7 @@ export class InfoService {
|
|||
// If either version starts with 0. it has to be exactly the same
|
||||
// If both versions start with something else, they have to match the first part
|
||||
public async isCompatibleWithServer(): AsyncFailable<boolean> {
|
||||
const info = await this.pollInfo();
|
||||
const info = await this.getLoadedSnapshot();
|
||||
if (HasFailed(info)) return info;
|
||||
|
||||
const serverVersion = info.version;
|
||||
|
@ -101,4 +99,13 @@ export class InfoService {
|
|||
public isLoaded(): boolean {
|
||||
return this.snapshot.version !== '0.0.0';
|
||||
}
|
||||
|
||||
private async updateInfo(): AsyncFailable<ServerInfo> {
|
||||
const response = await this.api.get(InfoResponse, '/api/info');
|
||||
if (HasFailed(response)) return response;
|
||||
|
||||
this.infoSubject.next(response);
|
||||
this.infoStorage.set(response);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
UserLoginResponse,
|
||||
UserMeResponse,
|
||||
UserRegisterRequest,
|
||||
UserRegisterResponse,
|
||||
UserRegisterResponse
|
||||
} from 'picsur-shared/dist/dto/api/user.dto';
|
||||
import { JwtDataSchema } from 'picsur-shared/dist/dto/jwt.dto';
|
||||
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
|
@ -16,11 +16,11 @@ import {
|
|||
Fail,
|
||||
FT,
|
||||
HasFailed,
|
||||
Open,
|
||||
Open
|
||||
} from 'picsur-shared/dist/types';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Logger } from '../logger/logger.service';
|
||||
import { KeyService } from '../storage/key.service';
|
||||
import { KeyStorageService } from '../storage/key-storage.service';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@Injectable({
|
||||
|
@ -44,7 +44,7 @@ export class UserService {
|
|||
|
||||
constructor(
|
||||
private readonly api: ApiService,
|
||||
private readonly key: KeyService,
|
||||
private readonly key: KeyStorageService,
|
||||
) {
|
||||
this.init().catch(this.logger.error);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Inject, Injectable } from '@angular/core';
|
||||
import { SESSION_STORAGE } from '@ng-web-apis/common';
|
||||
import { AsyncFailable, Failable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { Logger } from '../logger/logger.service';
|
||||
|
||||
interface dataWrapper<T> {
|
||||
data: T;
|
||||
|
@ -11,6 +12,8 @@ interface dataWrapper<T> {
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class CacheService {
|
||||
private readonly logger = new Logger(CacheService.name);
|
||||
|
||||
private readonly cacheExpiresMS = 1000 * 60 * 60;
|
||||
private cacheVersion = '0.0.0';
|
||||
|
||||
|
@ -76,6 +79,35 @@ export class CacheService {
|
|||
return finalFallback;
|
||||
}
|
||||
|
||||
public getFallbackSync<T>(
|
||||
key: string,
|
||||
finalFallback: T,
|
||||
...fallbacks: Array<(key: string) => AsyncFailable<T> | Failable<T>>
|
||||
): T {
|
||||
const cached = this.get<T>(key);
|
||||
if (cached !== null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const background = async () => {
|
||||
for (const fallback of fallbacks) {
|
||||
const result = await fallback(key);
|
||||
if (HasFailed(result)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.set(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return finalFallback;
|
||||
};
|
||||
|
||||
background().catch((e) => this.logger.error(e));
|
||||
|
||||
return finalFallback;
|
||||
}
|
||||
|
||||
private transformKey(key: string): string {
|
||||
return `${this.cacheVersion}-${key}`;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ServerInfo } from 'src/app/models/dto/server-info.dto';
|
||||
import { Logger } from '../logger/logger.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class InfoStorageService {
|
||||
private readonly logger = new Logger(InfoStorageService.name);
|
||||
|
||||
private readonly storageKey = 'server-info';
|
||||
private info: ServerInfo | null = null;
|
||||
|
||||
constructor() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private load() {
|
||||
try {
|
||||
const hasRead = localStorage.getItem(this.storageKey);
|
||||
if (hasRead === null) return;
|
||||
|
||||
this.info = JSON.parse(hasRead);
|
||||
} catch (e) {
|
||||
this.logger.warn(e);
|
||||
localStorage.removeItem(this.storageKey);
|
||||
}
|
||||
}
|
||||
|
||||
private store() {
|
||||
if (this.info)
|
||||
localStorage.setItem(this.storageKey, JSON.stringify(this.info));
|
||||
else localStorage.removeItem(this.storageKey);
|
||||
}
|
||||
|
||||
public get() {
|
||||
setTimeout(this.load.bind(this), 0);
|
||||
return this.info;
|
||||
}
|
||||
|
||||
public set(info: ServerInfo) {
|
||||
this.info = info;
|
||||
this.store();
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.info = null;
|
||||
this.store();
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
|||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class KeyService {
|
||||
export class KeyStorageService {
|
||||
private key: string | null = null;
|
||||
|
||||
constructor() {
|
|
@ -1,6 +1,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { AsyncFailable, Failure, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { KeyService } from '../services/storage/key.service';
|
||||
import { KeyStorageService } from '../services/storage/key-storage.service';
|
||||
import { QOIImage, QOIJob, QOIWorkerOut } from './qoi-worker.dto';
|
||||
|
||||
@Injectable({
|
||||
|
@ -10,7 +10,7 @@ export class QoiWorkerService {
|
|||
private worker: Worker | null = null;
|
||||
private job: Promise<QOIJob> | null = null;
|
||||
|
||||
constructor(private readonly keyService: KeyService) {
|
||||
constructor(private readonly keyService: KeyStorageService) {
|
||||
if (typeof Worker !== 'undefined') {
|
||||
this.worker = new Worker(new URL('./qoi.worker', import.meta.url));
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { z } from 'zod';
|
||||
import { HostNameRegex, URLRegex } from '../util/common-regex';
|
||||
import { URLRegex } from '../util/common-regex';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
import { IsValidMS } from '../validators/ms.validator';
|
||||
import { IsPosInt } from '../validators/positive-int.validator';
|
||||
|
@ -57,10 +57,7 @@ export const SysPreferenceValueTypes: {
|
|||
export const SysPreferenceValidators: {
|
||||
[key in SysPreference]: z.ZodTypeAny;
|
||||
} = {
|
||||
[SysPreference.HostOverride]: z
|
||||
.string()
|
||||
.regex(HostNameRegex)
|
||||
.or(z.literal('')),
|
||||
[SysPreference.HostOverride]: z.string().regex(URLRegex).or(z.literal('')),
|
||||
|
||||
[SysPreference.JwtSecret]: z.boolean(),
|
||||
[SysPreference.JwtExpiresIn]: IsValidMS(),
|
||||
|
|
Loading…
Reference in New Issue