mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(web)!: SPA (#5069)
* feat(web): SPA * chore: remove unnecessary prune * feat(web): merge with immich-server * Correct method name * fix: bugs, docs, workflows, etc. * chore: keep dockerignore for dev * chore: remove license * fix: expose 2283 --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
5118d261ab
commit
adae5dd758
115 changed files with 730 additions and 1446 deletions
|
|
@ -15,7 +15,7 @@ export class EnablePasswordLoginCommand extends CommandRunner {
|
|||
const config = await this.configService.getConfig();
|
||||
config.passwordLogin.enabled = true;
|
||||
await this.configService.updateConfig(config);
|
||||
await axios.post('http://localhost:3001/refresh-config');
|
||||
await axios.post('http://localhost:3001/api/refresh-config');
|
||||
console.log('Password login has been enabled.');
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ export class DisablePasswordLoginCommand extends CommandRunner {
|
|||
const config = await this.configService.getConfig();
|
||||
config.passwordLogin.enabled = false;
|
||||
await this.configService.updateConfig(config);
|
||||
await axios.post('http://localhost:3001/refresh-config');
|
||||
await axios.post('http://localhost:3001/api/refresh-config');
|
||||
console.log('Password login has been disabled.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -306,9 +306,9 @@ describe(SystemConfigService.name, () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getTheme', () => {
|
||||
describe('getCustomCss', () => {
|
||||
it('should return the default theme', async () => {
|
||||
await expect(sut.getTheme()).resolves.toEqual(defaults.theme);
|
||||
await expect(sut.getCustomCss()).resolves.toEqual(defaults.theme.customCss);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { JobName } from '../job';
|
||||
import { CommunicationEvent, ICommunicationRepository, IJobRepository, ISystemConfigRepository } from '../repositories';
|
||||
import { SystemConfigThemeDto } from './dto/system-config-theme.dto';
|
||||
import { SystemConfigDto, mapConfig } from './dto/system-config.dto';
|
||||
import { SystemConfigTemplateStorageOptionDto } from './response-dto/system-config-template-storage-option.dto';
|
||||
import {
|
||||
|
|
@ -31,11 +30,6 @@ export class SystemConfigService {
|
|||
return this.core.config$;
|
||||
}
|
||||
|
||||
async getTheme(): Promise<SystemConfigThemeDto> {
|
||||
const { theme } = await this.core.getConfig();
|
||||
return theme;
|
||||
}
|
||||
|
||||
async getConfig(): Promise<SystemConfigDto> {
|
||||
const config = await this.core.getConfig();
|
||||
return mapConfig(config);
|
||||
|
|
@ -87,4 +81,9 @@ export class SystemConfigService {
|
|||
|
||||
return JSON.parse(await this.repository.readFile(`./assets/style-${theme}.json`));
|
||||
}
|
||||
|
||||
async getCustomCss(): Promise<string> {
|
||||
const { theme } = await this.core.getConfig();
|
||||
return theme.customCss;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
SwaggerDocumentOptions,
|
||||
SwaggerModule,
|
||||
} from '@nestjs/swagger';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { writeFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
|
|
@ -56,6 +57,12 @@ const patchOpenAPI = (document: OpenAPIObject) => {
|
|||
document.components.schemas = sortKeys(document.components.schemas);
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(document.paths)) {
|
||||
const newKey = key.replace('/api/', '/');
|
||||
delete document.paths[key];
|
||||
document.paths[newKey] = value;
|
||||
}
|
||||
|
||||
for (const path of Object.values(document.paths)) {
|
||||
const operations = {
|
||||
get: path.get,
|
||||
|
|
@ -94,6 +101,14 @@ const patchOpenAPI = (document: OpenAPIObject) => {
|
|||
return document;
|
||||
};
|
||||
|
||||
export const indexFallback = (excludePaths: string[]) => (req: Request, res: Response, next: NextFunction) => {
|
||||
if (req.url.startsWith('/api') || req.method.toLowerCase() !== 'get' || excludePaths.indexOf(req.url) !== -1) {
|
||||
next();
|
||||
} else {
|
||||
res.sendFile('/www/index.html', { root: process.cwd() });
|
||||
}
|
||||
};
|
||||
|
||||
export const useSwagger = (app: INestApplication, isDev: boolean) => {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Immich')
|
||||
|
|
|
|||
|
|
@ -1,15 +1,34 @@
|
|||
import { SystemConfigService } from '@app/domain';
|
||||
import { Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { Controller, Get, Header, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { ApiExcludeEndpoint } from '@nestjs/swagger';
|
||||
import { PublicRoute } from '../app.guard';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private configService: SystemConfigService) {}
|
||||
constructor(private service: SystemConfigService) {}
|
||||
|
||||
@ApiExcludeEndpoint()
|
||||
@Get('.well-known/immich')
|
||||
getImmichWellKnown() {
|
||||
return {
|
||||
api: {
|
||||
endpoint: '/api',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ApiExcludeEndpoint()
|
||||
@PublicRoute()
|
||||
@Get('custom.css')
|
||||
@Header('Content-Type', 'text/css')
|
||||
getCustomCss() {
|
||||
return this.service.getCustomCss();
|
||||
}
|
||||
|
||||
@ApiExcludeEndpoint()
|
||||
@Post('refresh-config')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
public reloadConfig() {
|
||||
return this.configService.refreshConfig();
|
||||
return this.service.refreshConfig();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { NestExpressApplication } from '@nestjs/platform-express';
|
|||
import { json } from 'body-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import { AppModule } from './app.module';
|
||||
import { useSwagger } from './app.utils';
|
||||
import { indexFallback, useSwagger } from './app.utils';
|
||||
|
||||
const logger = new Logger('ImmichServer');
|
||||
const port = Number(process.env.SERVER_PORT) || 3001;
|
||||
|
|
@ -24,6 +24,11 @@ export async function bootstrap() {
|
|||
app.useWebSocketAdapter(new RedisIoAdapter(app));
|
||||
useSwagger(app, isDev);
|
||||
|
||||
const excludePaths = ['/.well-known/immich', '/custom.css'];
|
||||
app.setGlobalPrefix('api', { exclude: excludePaths });
|
||||
app.useStaticAssets('www');
|
||||
app.use(indexFallback(excludePaths));
|
||||
|
||||
const server = await app.listen(port);
|
||||
server.requestTimeout = 30 * 60 * 1000;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Logger } from '@nestjs/common';
|
|||
import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, WebSocketServer } from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
|
||||
@WebSocketGateway({ cors: true })
|
||||
@WebSocketGateway({ cors: true, path: '/api/socket.io' })
|
||||
export class CommunicationRepository implements OnGatewayConnection, OnGatewayDisconnect, ICommunicationRepository {
|
||||
private logger = new Logger(CommunicationRepository.name);
|
||||
private onConnectCallbacks: Callback[] = [];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue