Finish hostname override

This commit is contained in:
rubikscraft 2022-12-25 23:25:39 +01:00
parent dac43896ce
commit f69e455996
No known key found for this signature in database
GPG Key ID: 1463EBE9200A5CD4
10 changed files with 136 additions and 54 deletions

View File

@ -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),
);
}

View File

@ -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,
) {}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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}`;
}

View File

@ -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();
}
}

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class KeyService {
export class KeyStorageService {
private key: string | null = null;
constructor() {

View File

@ -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 {

View File

@ -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(),