Add OpenAPI Specs and Response DTOs (#320)

* Added swagger bearer auth method authentication accordingly

* Update Auth endpoint

* Added additional api information for authentication

* Added Swagger CLI pluggin

* Added DTO for /user endpoint

* Added /device-info reponse DTOs

* Implement server version

* Added DTOs for /server-info

* Added DTOs for /assets

* Added album to Swagger group

* Added generated specs file

* Add Client API generator for web

* Remove incorrectly placed node_modules

* Created class to handle access token

* Remove password and hash when getting all user

* PR feedback

* Fixed video from CLI doesn't get metadata extracted

* Fixed issue with TSConfig to work with generated openAPI

* PR feedback

* Remove console.log
This commit is contained in:
Alex
2022-07-08 21:26:50 -05:00
committed by GitHub
parent 25985c732d
commit 7f236c5b18
59 changed files with 5477 additions and 226 deletions
@@ -19,6 +19,8 @@ import { DeleteAssetDto } from './dto/delete-asset.dto';
import { SearchAssetDto } from './dto/search-asset.dto';
import fs from 'fs/promises';
import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
import { AssetResponseDto, mapAsset } from './response-dto/asset-response.dto';
const fileInfo = promisify(stat);
@@ -80,49 +82,55 @@ export class AssetService {
return res;
}
public async getAllAssets(authUser: AuthUserDto) {
try {
return await this.assetRepository.find({
where: {
userId: authUser.id,
resizePath: Not(IsNull()),
},
relations: ['exifInfo'],
order: {
createdAt: 'DESC',
},
});
} catch (e) {
Logger.error(e, 'getAllAssets');
}
public async getAllAssets(authUser: AuthUserDto): Promise<AssetResponseDto[]> {
const assets = await this.assetRepository.find({
where: {
userId: authUser.id,
resizePath: Not(IsNull()),
},
relations: ['exifInfo'],
order: {
createdAt: 'DESC',
},
});
return assets.map((asset) => mapAsset(asset));
}
public async findOne(deviceId: string, assetId: string): Promise<AssetEntity> {
public async findAssetOfDevice(deviceId: string, assetId: string): Promise<AssetResponseDto> {
const rows = await this.assetRepository.query(
'SELECT * FROM assets a WHERE a."deviceAssetId" = $1 AND a."deviceId" = $2',
[assetId, deviceId],
);
if (rows.lengh == 0) {
throw new BadRequestException('Not Found');
throw new NotFoundException('Not Found');
}
return rows[0] as AssetEntity;
const assetOnDevice = rows[0] as AssetEntity;
return mapAsset(assetOnDevice);
}
public async getAssetById(authUser: AuthUserDto, assetId: string) {
return await this.assetRepository.findOne({
public async getAssetById(authUser: AuthUserDto, assetId: string): Promise<AssetResponseDto> {
const asset = await this.assetRepository.findOne({
where: {
id: assetId,
},
relations: ['exifInfo'],
});
if (!asset) {
throw new NotFoundException('Asset not found');
}
return mapAsset(asset);
}
public async downloadFile(query: ServeFileDto, res: Res) {
try {
let fileReadStream = null;
const asset = await this.findOne(query.did, query.aid);
const asset = await this.findAssetOfDevice(query.did, query.aid);
if (query.isThumb === 'false' || !query.isThumb) {
const { size } = await fileInfo(asset.originalPath);
@@ -188,7 +196,7 @@ export class AssetService {
public async serveFile(authUser: AuthUserDto, query: ServeFileDto, res: Res, headers: any) {
let fileReadStream: ReadStream;
const asset = await this.findOne(query.did, query.aid);
const asset = await this.findAssetOfDevice(query.did, query.aid);
if (!asset) {
throw new NotFoundException('Asset does not exist');
@@ -258,12 +266,13 @@ export class AssetService {
try {
// Handle Video
let videoPath = asset.originalPath;
let mimeType = asset.mimeType;
await fs.access(videoPath, constants.R_OK | constants.W_OK);
if (query.isWeb && asset.mimeType == 'video/quicktime') {
videoPath = asset.encodedVideoPath == '' ? asset.originalPath : asset.encodedVideoPath;
videoPath = asset.encodedVideoPath == '' ? String(asset.originalPath) : String(asset.encodedVideoPath);
mimeType = asset.encodedVideoPath == '' ? asset.mimeType : 'video/mp4';
}
@@ -390,7 +399,7 @@ export class AssetService {
return Array.from(possibleSearchTerm).filter((x) => x != null);
}
async searchAsset(authUser: AuthUserDto, searchAssetDto: SearchAssetDto) {
async searchAsset(authUser: AuthUserDto, searchAssetDto: SearchAssetDto): Promise<AssetResponseDto[]> {
const query = `
SELECT a.*
FROM assets a
@@ -406,7 +415,12 @@ export class AssetService {
);
`;
return await this.assetRepository.query(query, [authUser.id, searchAssetDto.searchTerm]);
const searchResults: AssetEntity[] = await this.assetRepository.query(query, [
authUser.id,
searchAssetDto.searchTerm,
]);
return searchResults.map((asset) => mapAsset(asset));
}
async getCuratedLocation(authUser: AuthUserDto) {
@@ -423,8 +437,8 @@ export class AssetService {
);
}
async getCuratedObject(authUser: AuthUserDto) {
return await this.assetRepository.query(
async getCuratedObject(authUser: AuthUserDto): Promise<CuratedObjectsResponseDto[]> {
const curatedObjects: CuratedObjectsResponseDto[] = await this.assetRepository.query(
`
SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."resizePath", a."deviceAssetId", a."deviceId"
FROM assets a
@@ -434,9 +448,11 @@ export class AssetService {
`,
[authUser.id],
);
return curatedObjects;
}
async checkDuplicatedAsset(authUser: AuthUserDto, checkDuplicateAssetDto: CheckDuplicateAssetDto) {
async checkDuplicatedAsset(authUser: AuthUserDto, checkDuplicateAssetDto: CheckDuplicateAssetDto): Promise<boolean> {
const res = await this.assetRepository.findOne({
where: {
deviceAssetId: checkDuplicateAssetDto.deviceAssetId,