diff --git a/src/app.ts b/src/app.ts index e1694d1..7fd1f94 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,6 +5,7 @@ import HomeRoutes from "./routes/home.routes.js"; import { initDB } from "./config/db.config"; import { DatasetController } from "./controllers/dataset.controller"; import { OaiController } from "./controllers/oai.controller"; +import { FileController } from "./controllers/file.controller"; import * as path from 'path'; export class App extends Server { @@ -54,7 +55,7 @@ export class App extends Server { // addroutes // this.app.use('/api/dataset', DatasetRoutes); - super.addControllers([new DatasetController(), new OaiController()]); + super.addControllers([new DatasetController(), new OaiController(), new FileController()]); this.app.use("/api/", HomeRoutes); this.app.get("/", (request, response) => { diff --git a/src/controllers/file.controller.ts b/src/controllers/file.controller.ts new file mode 100644 index 0000000..d032dc1 --- /dev/null +++ b/src/controllers/file.controller.ts @@ -0,0 +1,54 @@ +import { Controller, Get } from "@overnightjs/core"; +import { File } from "../models/init-models.js"; +import { StatusCodes } from "http-status-codes"; +import { Request, Response } from "express"; +import * as path from "path"; +import * as fs from "fs"; + +@Controller("file") +export class FileController { + constructor() {} + + @Get("download/:id") + public async findOne(req: Request, res: Response) { + const id = req.params.id; + const file = await File.findOne({ + where: { id: id }, + }); + if (file) { + let filePath = "/storage/app/public/" + file.path_name; + const ext = path.extname(filePath); + let fileName = file.label + ext; + try { + fs.accessSync(filePath, fs.constants.R_OK | fs.constants.W_OK); + // console.log("can read/write:", path); + res.set({ + "Cache-Control": "no-cache private", + "Content-Description": "File Transfer", + "Content-Type": file.mime_type, + "Content-Disposition": "inline; filename=" + fileName, + "Content-Transfer-Encoding": "binary", + }); + res.status(StatusCodes.OK).sendFile(filePath); + } catch (err) { + // console.log("no access:", path); + res.status(StatusCodes.NOT_FOUND).send({ + message: `File with id ${id} doesn't exist on file server`, + }); + } + + + // res.status(StatusCodes.OK).sendFile(filePath, (err) => { + // // res.setHeader("Content-Type", "application/json"); + // // res.removeHeader("Content-Disposition"); + // res.status(StatusCodes.NOT_FOUND).send({ + // message: `File with id ${id} doesn't exist on file server`, + // }); + // }); + } else { + res.status(StatusCodes.NOT_FOUND).send({ + message: `Cannot find File with id=${id}.`, + }); + } + } +} diff --git a/src/controllers/oai.controller.ts b/src/controllers/oai.controller.ts index 1bb5fa8..184c4dc 100644 --- a/src/controllers/oai.controller.ts +++ b/src/controllers/oai.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get } from "@overnightjs/core"; +import { Controller, Get, Post } from "@overnightjs/core"; import Sequelize from "sequelize"; import { NextFunction, Request, Response } from "express"; import { StatusCodes } from "http-status-codes"; @@ -55,6 +55,7 @@ export class OaiController { this.configuration = new Configuration(); } + @Post("") @Get("") public async index(request: Request, response: Response, next: NextFunction) { this.xml = create( @@ -87,7 +88,6 @@ export class OaiController { try { await this.handleRequest(oaiRequest, request); } catch (error) { - // return next(error); if (error instanceof OaiModelException) { this.xsltParameter["oai_error_code"] = error.oaiCode; this.xsltParameter["oai_error_message"] = error.message; @@ -151,7 +151,7 @@ export class OaiController { // $this->loadStyleSheet('datasetxml2oai-pmh.xslt'); // Set response time - const now: dayjs.Dayjs = dayjs(); + const now: Dayjs = dayjs(); this.xsltParameter["responseDate"] = now.format("YYYY-MM-DDThh:mm:ss[Z]"); this.xsltParameter["unixTimestamp"] = now.unix(); @@ -182,12 +182,8 @@ export class OaiController { this.handleIllegalVerb(); } } else { - // const err = new HttpException(404, 'Not Found') - // next(err); - // try { // console.log("Async code example.") - // const err = new HttpException(404, 'Not Found'); const err = new PageNotFoundException("verb not found"); throw err; // } catch (error) { // manually catching @@ -363,21 +359,8 @@ export class OaiController { let totalIds = 0; let start = maxRecords + 1; let reldocIds: (number | null)[] = []; - let metadataPrefix = null; - // const tokenWorker = new TokenWorker(86400); - // await tokenWorker.connect(); - // $tokenWorker->setResumptionPath($tokenTempPath); - // const url = process.env.REDIS_URL || "redis://redis:6379"; - // const redisClient = createClient({ - // url - // }); - // redisClient.on('error', (error) => { - // const err = new InternalServerErrorException("Error occured while connecting or accessing redis server'"); - // throw err; - // }); - // resumptionToken is defined if ("resumptionToken" in oaiRequest) { const resParam = oaiRequest["resumptionToken"]; //e.g. "158886496600000" diff --git a/src/models/File.ts b/src/models/File.ts new file mode 100644 index 0000000..bbbac70 --- /dev/null +++ b/src/models/File.ts @@ -0,0 +1,71 @@ +import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional } from "sequelize"; +import sequelizeConnection from "../config/db.config"; + +class File extends Model, InferCreationAttributes> { + // id can be undefined during creation when using `autoIncrement` + declare id: CreationOptional; + declare path_name: string; // not nullable fields + declare label: string | null; + declare comment: string | null; + declare mime_type: string; + declare language: string | null; + declare file_size: bigint; + declare visible_in_frontdoor: boolean; + declare visible_in_oai: boolean; + declare sort_order: number; + + // createdAt can be undefined during creation + declare created_at: CreationOptional; + // updatedAt can be undefined during creation + declare updated_at: CreationOptional; +} + +File.init( + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + }, + path_name: { + type: DataTypes.STRING(100), + allowNull: false, + }, + label: DataTypes.STRING(100), + comment: DataTypes.STRING(255), + mime_type: { + type: DataTypes.STRING(255), + allowNull: false, + }, + language: DataTypes.STRING(3), + file_size: { + type: DataTypes.BIGINT, + allowNull: false, + }, + visible_in_frontdoor: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + visible_in_oai: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + sort_order: { + type: DataTypes.INTEGER, + allowNull: false, + }, + + + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, + { + createdAt: "created_at", + updatedAt: "updated_at", + tableName: "document_files", + sequelize: sequelizeConnection, + }, +); + +export default File; diff --git a/src/models/init-models.js b/src/models/init-models.js index db2f9a9..998bcda 100644 --- a/src/models/init-models.js +++ b/src/models/init-models.js @@ -11,13 +11,14 @@ import Person from "./Person"; import { Sequelize } from "sequelize"; import Subject from "./subject.model.js"; import Project from "./Project"; -import File from "./file.model.js"; +// import File from "./file.model.js"; +import File from "./File"; import Identifier from "./Identifier"; import DocumentXmlCache from "./DocumentXmlCache"; const dbContext = initModels(); -export { Dataset, Title, Abstract, User, Person, Subject, Coverage, License, Project, Identifier, DocumentXmlCache }; +export { Dataset, Title, Abstract, User, Person, Subject, Coverage, License, Project, Identifier, DocumentXmlCache, File }; export default dbContext; export function initModels() {