lynx-framework
Version:
lynx is a NodeJS framework for Web Development, based on decorators and the async/await support.
287 lines (262 loc) • 8.8 kB
text/typescript
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
OneToMany
} from "typeorm";
import BaseEntity from "./base.entity";
import User from "./user.entity";
import * as Jimp from "jimp";
import { v4 } from "uuid";
const uuid = v4;
import { app } from "../app";
import * as fs from 'fs';
import { lookup } from 'mime-types';
export interface ResizeConfig {
rotate: number;
scaleX: number;
scaleY: number;
x: number;
y: number;
width: number;
height: number;
}
("media")
export default class Media extends BaseEntity {
hiddenFields = ["owner", "_children", "children", "parent"];
() id: number;
() originalName: string;
({ unique: true, default: null })
slug: string;
({ type: "smallint", default: 0 })
private is_directory: number;
get isDirectory(): boolean {
return this.is_directory == 1;
}
set isDirectory(value: boolean) {
if (value) {
this.is_directory = 1;
} else {
this.is_directory = 0;
}
}
({ nullable: true }) mimetype: string;
({ nullable: true }) size: number;
({ nullable: true }) fileName: string;
({ nullable: true }) path: string;
(_ => Media, media => media._children)
parent: Media;
(_ => Media, media => media.parent)
_children: Media[];
(_ => User, user => user.media, { eager: true })
owner: User;
({ nullable: true })
width: number;
({ nullable: true })
height: number;
get children(): Promise<Media[]> {
return Media.find({ where: { parent: this } });
}
async remove(): Promise<this> {
if (this.isDirectory) {
for (let child of await this.children) {
await child.remove();
}
return super.remove();
}
let k = await super.remove();
app.config.ufs.unlink(this.path, () => { });
return k;
}
/**
* Create a cropped image starting of the current.
* The image will be placed in the same directory as the original.
* @param config: The configuration object containing the crop parameters
* @return a promise with the new @type Media
*/
async resizeToNewEntity(config: ResizeConfig): Promise<Media> {
let cachedPath = await app.config.ufs.getToCache(
this.fileName,
app.config.cachePath
);
let img = await Jimp.read(cachedPath);
img = await img.rotate(config.rotate);
img = await img.resize(
img.getWidth() * Math.round(config.scaleX),
img.getHeight() * Math.round(config.scaleY)
);
img = await img.crop(Math.round(config.x), Math.round(config.y), Math.round(config.width), Math.round(config.height));
let newFileName = uuid();
let path = app.config.cachePath;
await img.writeAsync(path + "/" + newFileName);
await app.config.ufs.uploadFileFromCache(newFileName, path);
let newMedia = new Media();
newMedia.isDirectory = false;
newMedia.originalName = copiedName(this.originalName);
newMedia.mimetype = this.mimetype;
let stat = await app.config.ufs.stat(newFileName);
newMedia.size = stat.size;
newMedia.fileName = newFileName;
newMedia.path = newFileName;
newMedia.owner = this.owner;
newMedia.parent = this.parent;
newMedia.width = config.width;
newMedia.height = config.height;
return await newMedia.save();
}
/**
* Generate a new MediaEntity from an uploaded file using the Multer library
* @param uploadMedia the multer File
* @param user the user performing the request (aka the onwer of the file)
* @param directory the (optional) virtual parent directory
*/
static async persist(
uploadMedia: Express.Multer.File,
user: User,
directory?: Media
): Promise<Media> {
let m = new Media();
uploadMedia = await app.config.ufs.uploadFile(uploadMedia);
m.originalName = uploadMedia.originalname;
m.isDirectory = false;
m.mimetype = uploadMedia.mimetype;
m.size = uploadMedia.size;
m.fileName = uploadMedia.filename;
m.path = uploadMedia.path;
m.owner = user;
if (directory) {
m.parent = directory;
}
let cachedPath = await app.config.ufs.getToCache(
m.fileName,
app.config.cachePath
);
if (m.mimetype.startsWith('image')) {
try {
let img = await Jimp.read(cachedPath);
m.width = img.getWidth();
m.height = img.getHeight();
} catch (e) {
console.log("Error obtaining the metadata of the image.");
console.log("Image path: " + uploadMedia.path);
console.error(e);
}
}
return m.save();
}
/**
* Generate a new MediaEntity from a local temporary file
* @param originalName the original name of the file
* @param path the temporary path of the file
* @param user the user perfrming the request (aka the owner of the file)
* @param directory the (optional) virtual parent directory
*/
static async persistTempFile(originalName: string, path: string, user: User, directory?: Media): Promise<Media> {
let m = new Media();
m.originalName = originalName;
m.isDirectory = false;
m.mimetype = lookup(path) || '';
m.size = (await stat(path)).size;
let uploadedFile = await app.config.ufs.uploadTempFile(path);
m.fileName = uploadedFile.fileName;
m.path = uploadedFile.path;
m.owner = user;
if (directory) {
m.parent = directory;
}
return m.save();
}
static async mkdir(
name: string,
user?: User,
directory?: Media | null
): Promise<Media> {
let names = normalizePath(name).split("/");
let prev = directory;
for (let i = 0; i < names.length; i++) {
let n = names[i];
if (!prev) {
prev = null;
}
let where = {
where: {
originalName: n,
is_directory: 1,
parent: prev
}
};
let m = await Media.findOne(where);
if (!m) {
m = new Media();
m.originalName = n;
m.isDirectory = true;
if (user) {
m.owner = user;
}
if (prev) {
m.parent = prev;
}
await m.save();
}
prev = m;
}
return <Media>prev;
}
static async getFolder(path: string): Promise<Media | undefined> {
let names = normalizePath(path).split("/");
let prev = null;
for (let n of names) {
prev = await Media.findOne({
where: {
originalName: n,
is_directory: 1,
parent: prev
}
});
if (!prev) {
return prev;
}
}
return <Media>prev;
}
static findBySlug(slug: string): Promise<Media | undefined> {
return Media.findOne({ where: { slug: slug, is_directory: 0 } });
}
static findBySlugOrId(key: string): Promise<Media | undefined> {
return Media.getRepository()
.createQueryBuilder("m")
.where("m.slug = :slug AND is_directory = 0", { slug: key })
.orWhere("m.id = :id AND is_directory = 0", { id: key })
.getOne();
}
static findOneWithParent(id: number): Promise<Media | undefined> {
return Media.findOne({ where: { id: id }, relations: ["parent"] });
}
}
function normalizePath(path: string): string {
if (path.startsWith("/")) {
path = path.substring(1);
}
if (path.endsWith("/")) {
path = path.substring(0, path.length - 1);
}
return path;
}
function copiedName(name: string): string {
let index = name.lastIndexOf(".");
if (index == -1) {
return name + " 1";
}
return name.substring(0, index) + " 1" + name.substring(index);
}
function stat(path: string): Promise<fs.Stats> {
return new Promise((ok, fail) => {
fs.stat(path, (err, info) => {
if (err) {
return fail(err);
}
ok(info);
});
});
}