Set TypeScript to strict mode and fix issues related to server types (#261)

* Fix lint issues and some other TS issues

- set TypeScript in strict mode
- add npm commands to lint / check code
- fix all lint issues
- fix some TS issues
- rename User reponse DTO to make it consistent with the other ones
- override Express/User interface to use UserResponseDto interface
 This is for when the accessing the `user` from a Express Request,
 like in `asset-upload-config`

* Fix the rest of TS issues

- fix all the remaining TypeScript errors
- add missing `@types/mapbox__mapbox-sdk` package

* Move global.d.ts to server `src` folder

* Update AssetReponseDto duration type

This is now of type `string` that defaults to '0:00:00.00000' if not set
which is what the mobile app currently expects

* Set context when logging error in asset.service

Use `ServeFile` as the context for logging an error when
asset.resizePath is not set

* Fix wrong AppController merge conflict resolution

`redirectToWebpage` was removed in main as is no longer used.
This commit is contained in:
Jaime Baez
2022-06-25 19:53:06 +02:00
committed by GitHub
parent cca2f7d178
commit c918f5b001
64 changed files with 415 additions and 273 deletions
@@ -1,10 +1,16 @@
import { BadRequestException, Injectable, InternalServerErrorException, Logger, StreamableFile } from '@nestjs/common';
import {
BadRequestException,
Injectable,
InternalServerErrorException,
Logger,
NotFoundException,
StreamableFile,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { IsNull, Not, Repository } from 'typeorm';
import { AuthUserDto } from '../../decorators/auth-user.decorator';
import { CreateAssetDto } from './dto/create-asset.dto';
import { AssetEntity, AssetType } from '@app/database/entities/asset.entity';
import _ from 'lodash';
import { createReadStream, stat } from 'fs';
import { ServeFileDto } from './dto/serve-file.dto';
import { Response as Res } from 'express';
@@ -33,7 +39,12 @@ export class AssetService {
return updatedAsset.raw[0];
}
public async createUserAsset(authUser: AuthUserDto, assetInfo: CreateAssetDto, path: string, mimeType: string) {
public async createUserAsset(
authUser: AuthUserDto,
assetInfo: CreateAssetDto,
path: string,
mimeType: string,
): Promise<AssetEntity | undefined> {
const asset = new AssetEntity();
asset.deviceAssetId = assetInfo.deviceAssetId;
asset.userId = authUser.id;
@@ -44,10 +55,14 @@ export class AssetService {
asset.modifiedAt = assetInfo.modifiedAt;
asset.isFavorite = assetInfo.isFavorite;
asset.mimeType = mimeType;
asset.duration = assetInfo.duration;
asset.duration = assetInfo.duration || null;
try {
return await this.assetRepository.save(asset);
const createdAsset = await this.assetRepository.save(asset);
if (!createdAsset) {
throw new Error('Asset not created');
}
return createdAsset;
} catch (e) {
Logger.error(`Error Create New Asset ${e}`, 'createUserAsset');
}
@@ -62,7 +77,7 @@ export class AssetService {
select: ['deviceAssetId'],
});
const res = [];
const res: string[] = [];
rows.forEach((v) => res.push(v.deviceAssetId));
return res;
}
@@ -119,6 +134,9 @@ export class AssetService {
});
file = createReadStream(asset.originalPath);
} else {
if (!asset.resizePath) {
throw new Error('resizePath not set');
}
const { size } = await fileInfo(asset.resizePath);
res.set({
'Content-Type': 'image/jpeg',
@@ -134,16 +152,25 @@ export class AssetService {
}
}
public async getAssetThumbnail(assetId: string) {
public async getAssetThumbnail(assetId: string): Promise<StreamableFile> {
try {
const asset = await this.assetRepository.findOne({ id: assetId });
if (!asset) {
throw new NotFoundException('Asset not found');
}
if (asset.webpPath && asset.webpPath.length > 0) {
return new StreamableFile(createReadStream(asset.webpPath));
} else {
if (!asset.resizePath) {
throw new Error('resizePath not set');
}
return new StreamableFile(createReadStream(asset.resizePath));
}
} catch (e) {
if (e instanceof NotFoundException) {
throw e;
}
Logger.error('Error serving asset thumbnail ', e);
throw new InternalServerErrorException('Failed to serve asset thumbnail', 'GetAssetThumbnail');
}
@@ -154,6 +181,7 @@ export class AssetService {
const asset = await this.findOne(query.did, query.aid);
if (!asset) {
// TODO: maybe this should be a NotFoundException?
throw new BadRequestException('Asset does not exist');
}
@@ -166,6 +194,10 @@ export class AssetService {
res.set({
'Content-Type': 'image/jpeg',
});
if (!asset.resizePath) {
Logger.error('Error serving IMAGE asset for web', 'ServeFile');
throw new InternalServerErrorException(`Failed to serve image asset for web`, 'ServeFile');
}
return new StreamableFile(createReadStream(asset.resizePath));
}
@@ -189,6 +221,9 @@ export class AssetService {
res.set({
'Content-Type': 'image/jpeg',
});
if (!asset.resizePath) {
throw new Error('resizePath not set');
}
file = createReadStream(asset.resizePath);
}
}
@@ -297,6 +332,7 @@ export class AssetService {
async getAssetSearchTerm(authUser: AuthUserDto): Promise<string[]> {
const possibleSearchTerm = new Set<string>();
// TODO: should use query builder
const rows = await this.assetRepository.query(
`
select distinct si.tags, si.objects, e.orientation, e."lensModel", e.make, e.model , a.type, e.city, e.state, e.country
@@ -308,12 +344,12 @@ export class AssetService {
[authUser.id],
);
rows.forEach((row) => {
rows.forEach((row: { [x: string]: any }) => {
// tags
row['tags']?.map((tag) => possibleSearchTerm.add(tag?.toLowerCase()));
row['tags']?.map((tag: string) => possibleSearchTerm.add(tag?.toLowerCase()));
// objects
row['objects']?.map((object) => possibleSearchTerm.add(object?.toLowerCase()));
row['objects']?.map((object: string) => possibleSearchTerm.add(object?.toLowerCase()));
// asset's tyoe
possibleSearchTerm.add(row['type']?.toLowerCase());
@@ -345,7 +381,7 @@ export class AssetService {
LEFT JOIN exif e ON a.id = e."assetId"
WHERE a."userId" = $1
AND
AND
(
TO_TSVECTOR('english', ARRAY_TO_STRING(si.tags, ',')) @@ PLAINTO_TSQUERY('english', $2) OR
TO_TSVECTOR('english', ARRAY_TO_STRING(si.objects, ',')) @@ PLAINTO_TSQUERY('english', $2) OR
@@ -362,7 +398,7 @@ export class AssetService {
select distinct on (e.city) a.id, e.city, a."resizePath", a."deviceAssetId", a."deviceId"
from assets a
left join exif e on a.id = e."assetId"
where a."userId" = $1
where a."userId" = $1
and e.city is not null
and a.type = 'IMAGE';
`,
@@ -376,7 +412,7 @@ export class AssetService {
select distinct on (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."resizePath", a."deviceAssetId", a."deviceId"
from assets a
left join smart_info si on a.id = si."assetId"
where a."userId" = $1
where a."userId" = $1
and si.objects is not null
`,
[authUser.id],