Skip to content

Commit

Permalink
nestjs 9.x
Browse files Browse the repository at this point in the history
  • Loading branch information
oznu committed Jul 10, 2022
1 parent 83638ce commit c8adb17
Show file tree
Hide file tree
Showing 16 changed files with 1,343 additions and 1,229 deletions.
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
### Other Changes

* **i18n:** Improvements to Swedish language translations ([#1380](https://github.com/oznu/homebridge-config-ui-x/pull/1380))
* **Dashboard:** Display CPU temperature on the dashboard when running on Synology DSM 7 ([#1382](https://github.com/oznu/homebridge-config-ui-x/pull/1382))

### Bug Fixes

Expand Down
2,345 changes: 1,247 additions & 1,098 deletions package-lock.json

Large diffs are not rendered by default.

31 changes: 15 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "homebridge-config-ui-x",
"displayName": "Homebridge UI",
"version": "4.49.1-test.2",
"version": "4.49.1-test.3",
"description": "A web based management, configuration and control platform for Homebridge",
"license": "MIT",
"author": "oznu <[email protected]>",
Expand Down Expand Up @@ -50,25 +50,25 @@
"upgrade-install": "./upgrade-install.sh"
},
"dependencies": {
"@nestjs/axios": "0.0.7",
"@nestjs/common": "8.4.4",
"@nestjs/core": "8.4.4",
"@nestjs/jwt": "8.0.0",
"@nestjs/passport": "8.2.1",
"@nestjs/platform-fastify": "8.4.4",
"@nestjs/platform-socket.io": "8.4.4",
"@nestjs/swagger": "5.2.1",
"@nestjs/websockets": "8.4.4",
"@fastify/multipart": "7.1.0",
"@fastify/static": "6.4.0",
"@fastify/swagger": "7.4.1",
"@nestjs/axios": "0.1.0",
"@nestjs/common": "9.0.1",
"@nestjs/core": "9.0.1",
"@nestjs/jwt": "9.0.0",
"@nestjs/passport": "9.0.0",
"@nestjs/platform-fastify": "9.0.1",
"@nestjs/platform-socket.io": "9.0.1",
"@nestjs/swagger": "6.0.1",
"@nestjs/websockets": "9.0.1",
"@oznu/hap-client": "1.9.0",
"axios": "0.26.1",
"class-transformer": "0.5.1",
"class-validator": "^0.13.1",
"commander": "6.2.1",
"dayjs": "1.11.0",
"fastify": "3.28.0",
"fastify-multipart": "5.3.1",
"fastify-static": "4.6.1",
"fastify-swagger": "5.1.0",
"fastify": "4.2.0",
"fs-extra": "9.1.0",
"helmet": "4.4.1",
"node-cache": "^5.1.2",
Expand All @@ -78,7 +78,6 @@
"p-limit": "3.1.0",
"passport": "0.5.2",
"passport-jwt": "4.0.0",
"pino-pretty": "^4.8.0",
"reflect-metadata": "^0.1.12",
"rxjs": "7.5.5",
"semver": "7.3.6",
Expand All @@ -89,7 +88,7 @@
"unzipper": "^0.10.11"
},
"devDependencies": {
"@nestjs/testing": "8.4.4",
"@nestjs/testing": "^9.0.1",
"@types/fs-extra": "^9.0.12",
"@types/jest": "^27.4.1",
"@types/node": "^16.11.26",
Expand Down
12 changes: 4 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import './self-check';

import * as path from 'path';
import { fastify, FastifyReply, FastifyRequest } from 'fastify';
import fastifyMultipart from 'fastify-multipart';
import { FastifyReply, FastifyRequest } from 'fastify';
import fastifyMultipart from '@fastify/multipart';
import * as helmet from 'helmet';
import * as fs from 'fs-extra';
import { NestFactory } from '@nestjs/core';
Expand All @@ -23,15 +23,11 @@ process.env.UIX_BASE_PATH = process.env.UIX_BASE_PATH_OVERRIDE || path.resolve(_
async function bootstrap(): Promise<NestFastifyApplication> {
const startupConfig = await getStartupConfig();

const server = fastify({
const fAdapter = new FastifyAdapter({
https: startupConfig.httpsOptions,
logger: startupConfig.debug ? {
prettyPrint: true,
} : false,
logger: startupConfig.debug || false,
});

const fAdapter = new FastifyAdapter(server);

fAdapter.register(fastifyMultipart, {
limits: {
files: 1,
Expand Down
44 changes: 21 additions & 23 deletions src/modules/backup/backup.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Controller, Get, Post, Put, UseGuards, Res, Req, InternalServerErrorException, Param } from '@nestjs/common';
import { Controller, Get, Post, Put, UseGuards, Res, Req, InternalServerErrorException, Param, StreamableFile } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags, ApiBearerAuth, ApiOperation, ApiBody, ApiConsumes, ApiParam } from '@nestjs/swagger';
import { FastifyReply } from 'fastify';
import { FastifyRequest } from 'fastify';

import { BackupService } from './backup.service';
import { AdminGuard } from '../../core/auth/guards/admin.guard';
Expand All @@ -21,9 +21,9 @@ export class BackupController {
@UseGuards(AdminGuard)
@ApiOperation({ summary: 'Download a .tar.gz of the Homebridge instance.' })
@Get('/download')
async downloadBackup(@Res() reply) {
async downloadBackup(@Res({ passthrough: true }) res): Promise<StreamableFile> {
try {
return await this.backupService.downloadBackup(reply);
return await this.backupService.downloadBackup(res);
} catch (e) {
console.error(e);
this.logger.error('Backup Failed ' + e);
Expand All @@ -49,7 +49,7 @@ export class BackupController {
@ApiOperation({ summary: 'Download a system generated instance backup.' })
@ApiParam({ name: 'backupId', type: 'string' })
@Get('/scheduled-backups/:backupId')
async getScheduledBackup(@Param('backupId') backupId) {
async getScheduledBackup(@Param('backupId') backupId): Promise<StreamableFile> {
return this.backupService.getScheduledBackup(backupId);
}

Expand All @@ -71,15 +71,14 @@ export class BackupController {
},
},
})
restoreBackup(@Req() req, @Res() res: FastifyReply) {
req.multipart(async (field, file, filename, encoding, mimetype) => {
this.backupService.uploadBackupRestore(file);
}, (err) => {
if (err) {
return res.send(500).send(err.message);
}
return res.code(200).send();
});
async restoreBackup(@Req() req: FastifyRequest) {
try {
const data = await req.file();
await this.backupService.uploadBackupRestore(data);
} catch (err) {
this.logger.error('Restore backup failed:', err.message);
throw new InternalServerErrorException(err.message);
}
}

@UseGuards(AdminGuard)
Expand Down Expand Up @@ -110,15 +109,14 @@ export class BackupController {
},
})
@Post('/restore/hbfx')
restoreHbfx(@Req() req, @Res() res: FastifyReply) {
req.multipart(async (field, file, filename, encoding, mimetype) => {
this.backupService.uploadHbfxRestore(file);
}, (err) => {
if (err) {
return res.send(500).send(err.message);
}
return res.code(200).send();
});
async restoreHbfx(@Req() req: FastifyRequest) {
try {
const data = await req.file();
await this.backupService.uploadHbfxRestore(data);
} catch (err) {
this.logger.error('Restore backup failed:', err.message);
throw new InternalServerErrorException(err.message);
}
}

@UseGuards(AdminGuard)
Expand Down
41 changes: 19 additions & 22 deletions src/modules/backup/backup.service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import * as os from 'os';
import * as tar from 'tar';
import * as path from 'path';
import * as util from 'util';
import * as fs from 'fs-extra';
import * as color from 'bash-color';
import * as unzipper from 'unzipper';
import * as child_process from 'child_process';
import * as dayjs from 'dayjs';
import { pipeline } from 'stream';
import { EventEmitter } from 'events';
import { Injectable, BadRequestException, NotFoundException, InternalServerErrorException } from '@nestjs/common';
import { Injectable, BadRequestException, NotFoundException, InternalServerErrorException, StreamableFile } from '@nestjs/common';
import { FastifyReply } from 'fastify';
import { MultipartFile } from '@fastify/multipart';

import { PluginsService } from '../plugins/plugins.service';
import { SchedulerService } from '../../core/scheduler/scheduler.service';
Expand All @@ -17,6 +20,8 @@ import { HomebridgeIpcService } from '../..//core/homebridge-ipc/homebridge-ipc.
import { Logger } from '../../core/logger/logger.service';
import { HomebridgePlugin } from '../plugins/types';

const pump = util.promisify(pipeline);

@Injectable()
export class BackupService {
private restoreDirectory;
Expand Down Expand Up @@ -253,21 +258,21 @@ export class BackupService {
/**
* Downloads a scheduled backup .tar.gz
*/
async getScheduledBackup(backupId: string) {
async getScheduledBackup(backupId: string): Promise<StreamableFile> {
const backupPath = path.resolve(this.configService.instanceBackupPath, 'homebridge-backup-' + backupId + '.tar.gz');

// check the file exists
if (!await fs.pathExists(backupPath)) {
return new NotFoundException();
throw new NotFoundException();
}

return fs.createReadStream(backupPath);
return new StreamableFile(fs.createReadStream(backupPath));
}

/**
* Create and download backup archive of the current homebridge instance
*/
async downloadBackup(reply: FastifyReply) {
async downloadBackup(reply: FastifyReply): Promise<StreamableFile> {
const { backupDir, backupPath, backupFileName } = await this.createBackup();

// remove temp files (called when download finished)
Expand All @@ -286,33 +291,26 @@ export class BackupService {
reply.raw.setHeader('access-control-allow-origin', 'http://localhost:4200');
}

// start download
fs.createReadStream(backupPath)
.on('close', cleanup.bind(this))
.pipe(reply.raw);
return new StreamableFile(fs.createReadStream(backupPath).on('close', cleanup.bind(this)));
}

/**
* Restore a backup file
* File upload handler
*/
async uploadBackupRestore(file) {
async uploadBackupRestore(data: MultipartFile) {
// clear restore directory
this.restoreDirectory = undefined;

// prepare a temp working directory
const backupDir = await fs.mkdtemp(path.join(os.tmpdir(), 'homebridge-backup-'));

// create a write stream and pipe the upload into it
file.pipe(tar.x({
// pipe the data to the temp directory
await pump(data.file, tar.x({
cwd: backupDir,
}).on('error', (err) => {
this.logger.error(err);
}));

file.on('end', () => {
this.restoreDirectory = backupDir;
});
this.restoreDirectory = backupDir;
}

/**
Expand Down Expand Up @@ -488,7 +486,7 @@ export class BackupService {
/**
* Upload a .hbfx backup file
*/
async uploadHbfxRestore(file: fs.ReadStream) {
async uploadHbfxRestore(data: MultipartFile) {
// clear restore directory
this.restoreDirectory = undefined;

Expand All @@ -497,13 +495,12 @@ export class BackupService {

this.logger.log(`Extracting .hbfx file to ${backupDir}`);

file.pipe(unzipper.Extract({
// pipe the data to the temp directory
await pump(data.file, unzipper.Extract({
path: backupDir,
}));

file.on('end', () => {
this.restoreDirectory = backupDir;
});
this.restoreDirectory = backupDir;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, UseGuards, Get, Header } from '@nestjs/common';
import { Controller, UseGuards, Get, Header, StreamableFile } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { AdminGuard } from '../../../core/auth/guards/admin.guard';
Expand All @@ -18,7 +18,7 @@ export class HomebridgeHueController {
@Get('/dump-file')
@Header('Content-disposition', 'attachment; filename=homebridge-hue.json.gz')
@Header('Content-Type', 'application/json+gzip')
async exchangeCredentials() {
async exchangeCredentials(): Promise<StreamableFile> {
return this.homebridgeHueService.streamDumpFile();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, NotFoundException, StreamableFile } from '@nestjs/common';
import { ConfigService } from '../../../core/config/config.service';

@Injectable()
Expand All @@ -9,7 +9,7 @@ export class HomebridgeHueService {
private configService: ConfigService,
) { }

async streamDumpFile(): Promise<fs.ReadStream> {
async streamDumpFile(): Promise<StreamableFile> {
const dumpPath = path.resolve(this.configService.storagePath, 'homebridge-hue.json.gz');

// check file exists
Expand All @@ -18,6 +18,6 @@ export class HomebridgeHueService {
}

// stream file to client
return fs.createReadStream(dumpPath);
return new StreamableFile(fs.createReadStream(dumpPath));
}
}
4 changes: 2 additions & 2 deletions test/e2e/backup.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { EventEmitter } from 'events';
import { ValidationPipe } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import fastifyMultipart from 'fastify-multipart';
import fastifyMultipart from '@fastify/multipart';
import * as FormData from 'form-data';

import { AuthModule } from '../../src/core/auth/auth.module';
Expand Down Expand Up @@ -238,7 +238,7 @@ describe('BackupController (e2e)', () => {
payload,
});

expect(res.statusCode).toBe(200);
expect(res.statusCode).toBe(201);

await new Promise((resolve) => setTimeout(resolve, 100));

Expand Down
3 changes: 1 addition & 2 deletions test/e2e/custom-plugins.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import axios from 'axios';
import { ValidationPipe } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { Test, TestingModule } from '@nestjs/testing';
Expand Down Expand Up @@ -35,7 +34,7 @@ describe('CustomPluginsController (e2e)', () => {
await fs.copy(path.resolve(__dirname, '../mocks', '.uix-secrets'), secretsFilePath);

// create httpService instance
httpService = new HttpService(axios.create({}));
httpService = new HttpService();

const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [CustomPluginsModule, AuthModule],
Expand Down
6 changes: 2 additions & 4 deletions test/e2e/fastify.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ValidationPipe } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { fastify } from 'fastify';
import fastifyMultipart from 'fastify-multipart';
import fastifyMultipart from '@fastify/multipart';

import { AppModule } from '../../src/app.module';

Expand Down Expand Up @@ -35,9 +35,7 @@ describe('FastifyOptions (e2e)', () => {

// setup fastify
const server = fastify({
logger: {
prettyPrint: true,
},
logger: true,
});

const fAdapter = new FastifyAdapter(server);
Expand Down
Loading

0 comments on commit c8adb17

Please sign in to comment.