I always get a problem if the image is already created - node.js

This project changes the image sizes this project is using a library called sharp, but at first, if the image is created for the first time, it does not cause problems, but it always shows me that problem if it was already created.
In imageProcessing.ts File
import { Request, Response } from "express";
import fs, { promises as fsPromises } from 'fs';
import resizingTheImage from "./resizingTheImage";
const imageProcessing = async(req: Request, res: Response, next: Function): Promise<void | string> => {
const widthValue = <string>req.query.width;
const heightValue = <string>req.query.height;
const nameFileValue = <string>req.query.namefile;
const outputDir = `../../images/imageOutput/${nameFileValue}${widthValue}_${heightValue}.jpg`;
const makeDir = async (): Promise<void> => {
await fsPromises.mkdir('./images/imageOutput');
};
const imageProcessing = async (): Promise<void> => {
try {
await resizingTheImage(widthValue, heightValue, nameFileValue);
} catch {
res.send('There is a problem with the image processing');
}
};
if (fs.existsSync(outputDir)) {
return outputDir;
} else {
makeDir();
await imageProcessing();
}
next();
}
export default imageProcessing;
In resizingTheImage.ts File
import sharp from "sharp";
const resizingTheImage = async (imageWidth: string, imageHeight: string, fileName: string): Promise<void> => {
const imageDir: string = `./images/${fileName}.jpg`;
const outputDir: string = `./images/imageOutput/${fileName}${imageWidth}_${imageHeight}.jpg`;
if (isNaN(parseInt(imageWidth) && parseInt(imageHeight)) && fileName) {
console.log("the inputs is Invalid")
} else {
await sharp(imageDir)
.resize(parseInt(imageWidth), parseInt(imageHeight))
.toFile(outputDir);
}
}
export default resizingTheImage;
In error
[Error: EEXIST: file already exists, mkdir 'E:\Image Processing API\images\imageOutput'] {
errno: -4075,
code: 'EEXIST',
syscall: 'mkdir',
path: 'E:\\Image Processing API\\images\\imageOutput'
}

Related

imagemagick typeerror: cannot read properties of undefined reading 'convert'

I'm building a pdf conversion service for my nestjs project and I'm getting the following error: TypeError: Cannot read properties of undefined (reading 'convert')
i believe i'm importing imagemagick correctly here, but calls to im.convert() throw this error
import { InjectQueue, Process, Processor } from '#nestjs/bull';
import { Injectable, Logger } from '#nestjs/common';
import { Job, Queue } from 'bull';
import { pdfJob } from './pdfJob';
import im from 'imagemagick';
import { promises as fs } from 'fs';
#Injectable()
#Processor('pdf')
export class ImagemagickService {
constructor(#InjectQueue('pdf') private readonly pdfQueue: Queue) {}
private readonly logger = new Logger(ImagemagickService.name);
#Process()
async processPDF(job: Job<pdfJob>) {
const IMAGE_DIR = `src/../config/img/`;
const { pdfPath, baseName } = job.data;
const outputPath = `${IMAGE_DIR}${baseName}.jpg`;
this.logger.debug(`Converting ${job.data.pdfPath}`);
try {
// Promisify the callback of im.convert()
const images = await new Promise((resolve, reject) => {
// Take PDF file and generate individual JPG files
im.convert(
[
'-alpha',
'remove',
'-density',
300,
'-quality',
80,
pdfPath,
outputPath,
],
async (err) => {
if (err) {
console.log(err);
reject(err);
throw `Couldn't Process ${pdfPath}`;
} else {
// Get every file in Temporary Image Directory
let files = await fs.readdir(IMAGE_DIR);
// Append directory into filenames
files = files.map((file) => {
return IMAGE_DIR + file;
});
// We only want the files that match the source pdf's name
files = files.filter((file) => {
return file.includes(baseName);
});
const output = { pdf: pdfPath, files };
resolve(output);
}
},
);
return images;
});
} catch (e) {
this.logger.error(e);
}
}
}
import it like this instead:
import * as im from 'imagemagick';
Or use this flag: https://www.typescriptlang.org/tsconfig/#esModuleInterop

How can I set a flash variable in Next.js before a redirect?

Laravel in PHP made this easy with https://laravel.com/docs/9.x/session#flash-data, so I figured Next.js would have an easy way too.
I thought I'd be able to do something like:
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getSession(ctx);
if (!session) {
ctx.res.setHeader("yourFlashVariable", "yourFlashValue");
console.log('headers', ctx.res.getHeaders()); // Why is it not even appearing here?
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
const props = ...
return { props };
};
and then in my other page:
export const getServerSideProps: GetServerSideProps = async (context) => {
const { headers, rawHeaders } = context.req;
// look inside the headers for the variable
// ...
But the header doesn't appear.
If you know how to achieve the goal of a flash variable (even if not using headers), I'm interested in whatever approach.
(Originally I asked How can I show a toast notification when redirecting due to lack of session using Next-Auth in Next.js? but now feel like I should have asked this more generic question.)
UPDATE
I appreciate the reasonable suggestion from https://stackoverflow.com/a/72210574/470749 so have tried it.
Unfortunately, index.tsx still does not get any value from getFlash.
// getFlash.ts
import { Session } from 'next-session/lib/types';
export default function getFlash(session: Session) {
// If there's a flash message, transfer it to a context, then clear it.
const { flash = null } = session;
console.log({ flash });
// eslint-disable-next-line no-param-reassign
delete session.flash;
return flash;
}
// getNextSession.ts
import nextSession from 'next-session';
export default nextSession();
// foo.tsx
import { getSession } from 'next-auth/react';
import { GetServerSideProps, InferGetServerSidePropsType, NextApiRequest, NextApiResponse } from 'next';
import getNextSession from '../helpers/getNextSession';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getSession(ctx);
if (!session) {
const req = ctx.req as NextApiRequest;
const res = ctx.res as NextApiResponse;
const nSession = await getNextSession(req, res);
nSession.flash = 'You must be logged in to access this page.'; // THIS LINE CAUSES A WARNING
console.log({ nSession });
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
// ...
return { props };
};
// index.tsx
import { GetServerSideProps } from 'next';
import getFlash from '../helpers/getFlash';
import getNextSession from '../helpers/getNextSession';
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getNextSession(context.req, context.res);
let toast = getFlash(session);
console.log({ toast });
if (!toast) {
toast = 'no toast';
}
console.log({ toast });
return {
props: { toast }, // will be passed to the page component as props
};
};
Also, the nSession.flash = line causes this warning:
warn - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
Your first code is working fine for me (printing the headers in terminal). However, the combination will not work as intended because the headers you set in /foo (say) will be sent to browser, along with a status code of 307, and a location header of /. Now "the browser" will be redirecting to the location and it won't forward your headers. Similar threads: https://stackoverflow.com/a/30683594, https://stackoverflow.com/a/12883411.
To overcome this, you can do something like this. This works because the browser does send the cookies (in this case, set when you create a session).
// lib/session.ts
import type { IronSessionOptions } from 'iron-session'
import type { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
export const sessionOptions: IronSessionOptions = {
password: process.env.SECRET_COOKIE_PASSWORD as string,
cookieName: 'sid',
cookieOptions: { secure: process.env.NODE_ENV === 'production' },
}
declare module 'iron-session' {
interface IronSessionData {
flash?: string | undefined
}
}
export const withSessionRoute = (handler: NextApiHandler) =>
withIronSessionApiRoute(handler, sessionOptions)
export const withSessionSsr = <P extends Record<string, unknown> = Record<string, unknown>>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) => withIronSessionSsr(handler, sessionOptions)
// pages/protected.tsx
import type { NextPage } from 'next'
import { getSession } from 'next-auth/react'
import { withSessionSsr } from 'lib/session'
const ProtectedPage: NextPage = () => <h1>Protected Page</h1>
const getServerSideProps = withSessionSsr(async ({ req, res }) => {
const session = await getSession({ req })
if (!session) {
req.session.flash = 'You must be logged in to access this page.'
await req.session.save()
return { redirect: { destination: '/', permanent: false } }
}
return { props: {} }
})
export default ProtectedPage
export { getServerSideProps }
// pages/index.tsx
import type { InferGetServerSidePropsType, NextPage } from 'next'
import { withSessionSsr } from 'lib/session'
const IndexPage: NextPage<InferGetServerSidePropsType<typeof getServerSideProps>> = ({ flash }) => {
// TODO: use `flash`
}
const getServerSideProps = withSessionSsr(async ({ req }) => {
// if there's a flash message, transfer
// it to a context, then clear it
// (extract this to a separate function for ease)
const { flash = null } = req.session
delete req.session.flash
await req.session.save()
return { props: { flash } }
})
export default IndexPage
export { getServerSideProps }
This also works if you want to set flash data in an API route instead of pages:
import { withSessionRoute } from 'lib/session'
const handler = withSessionRoute(async (req, res) => {
req.session.flash = 'Test'
await req.session.save()
res.redirect(307, '/')
})
export default handler
Complete example: https://github.com/brc-dd/next-flash/tree/with-iron-session

Why when I tried to execute following test code, I got "The first character of a path should be `/` or `*`" error?

When I tried to execute following test code, I got error like "The first character of a path should be / or *"
archives.test.ts
import ...
describe('Get All Archived', () => {
test('it must return ok', async () => {
const app = init()
const response = await app.inject().get('/client/archives')
const body = JSON.parse(response.body)
expect(response.statusCode).toBe(200)
expect(body.status).toBe('ok')
expect(body.data.length).toBe(0)
})
})
My code:
api.ts
import fastify, { FastifyInstance } from 'fastify'
other imports...
export const init = (): FastifyInstance => {
const app = fastify()
app.addContentTypeParser('application/json', { parseAs: 'string' }, parseApplicationJson)
app.register(ArchivesPlugin)
return app
}
const proxy = awsLambdaFastify(init())
export const handler = proxy
ArchivesPlugin.ts
import fp from 'fastify-plugin'
import { FastifyInstance, FastifyPluginAsync } from 'fastify'
import { register } from '../controllers/archives'
const ArchivesPlugin: FastifyPluginAsync = async (app: FastifyInstance) => {
await register(app)
}
export default fp(ArchivesPlugin)
register.ts
import { FastifyInstance, FastifyRequest } from 'fastify'
other imports...
const handlers = {
code content
},
}
export async function register(app: FastifyInstance) {
app.route({
method: 'GET',
url: `${prefix}/archives`,
schema: { ...GetArchiveListResponse, tags: ['archives'] },
handler: handlers.index,
})
}
Appreciate any help.
The problem was caused by ${prefix} in register.ts which is given above. When called register.ts, prefix seemed undefined. That's why I was getting the error.

cannot mock a fetch call with `fetch-mock-jest 1.5.1` lib

I'm trying to mock a fetch call using thisfetch-mock-jest but it the code still trys to go to the remote address and eventually fail with error message FetchError: request to https://some.domain.io/app-config.yaml failed, reason: getaddrinfo ENOTFOUND some.domain.io].
Here the the test code
import { AppConfig } from '#backstage/config';
import { loadConfig } from './loader';
import mockFs from 'mock-fs';
import fetchMock from 'fetch-mock-jest';
describe('loadConfig', () => {
beforeEach(() => {
fetchMock.mock({
matcher: '*',
response: `app:
title: Example App
sessionKey: 'abc123'
`
});
});
afterEach(() => {
fetchMock.mockReset();
});
it('load config from remote path', async () => {
const configUrl = 'https://some.domain.io/app-config.yaml';
await expect(
loadConfig({
configRoot: '/root',
configTargets: [{ url: configUrl }],
env: 'production',
remote: {
reloadIntervalSeconds: 30,
},
})
).resolves.toEqual([
{
context: configUrl,
data: {
app: {
title: 'Example App',
sessionKey: 'abc123',
},
},
},
]);
expect(fetchMock).toHaveBeenCalledTimes(1);
});
function defer<T>() {
let resolve: (value: T) => void;
const promise = new Promise<T>(_resolve => {
resolve = _resolve;
});
return { promise, resolve: resolve! };
}
});
loadConfig has the fetch code that I'm trying to mock.
export async function loadConfig(
options: LoadConfigOptions,
): Promise<AppConfig[]> {
const loadRemoteConfigFiles = async () => {
const configs: AppConfig[] = [];
const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => {
const response = await fetch(remoteConfigProp.url);
if (!response.ok) {
throw new Error(
`Could not read config file at ${remoteConfigProp.url}`,
);
}
remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined;
remoteConfigProp.newETag =
response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined;
remoteConfigProp.content = await response.text();
return remoteConfigProp;
};
.......
return configs;
}
let remoteConfigs: AppConfig[] = [];
if (remote) {
try {
remoteConfigs = await loadRemoteConfigFiles();
} catch (error) {
throw new Error(`Failed to read remote configuration file, ${error}`);
}
}
........ do some stuff with config then return
return remoteConfigs;
}
The config is a yaml file, that eventually gets parsed and converted into config object.
Any idea why is it failing to mock the fetch call?
replaced
import fetchMock from 'fetch-mock-jest';
with
const fetchMock = require('fetch-mock').sandbox();
const nodeFetch = require('node-fetch');
nodeFetch.default = fetchMock;
and fetchMock.mockReset(); with fetchMock.restore();

"then" is not exuceted after a Promise<void> when writing a file from a POST Request

I have currently an express server. I am trying to make a POST request without success.
Here is my ccontroller :
import { BAD_REQUEST } from '#app/constant';
import { SaveDrawService } from '#app/services/save-draw.service';
import { TYPES } from '#app/types';
import { Image } from '#common/communication/Image';
import { NextFunction, Request, Response, Router } from 'express';
import { inject, injectable } from 'inversify';
import 'reflect-metadata';
#injectable()
export class SaveDrawController {
router: Router;
constructor(#inject(TYPES.SaveDrawService) private saveDrawService: SaveDrawService) {
this.configureRouter();
}
private configureRouter(): void {
this.router = Router();
this.router.post('/write', (req: Request, res: Response, next: NextFunction) => {
if (!req.body) return res.sendStatus(BAD_REQUEST);
this.saveDrawService.writeData(req.body as Image);
return res.sendStatus(this.saveDrawService.code);
});
this.router.get('/read', (req: Request, res: Response, next: NextFunction) => {
return res.send(this.saveDrawService.readImageData());
});
}
}
Image here is a interface that i want to POST with these parameters:
export interface Image {
title: string;
tags: string[];
data: string; // base64 image from HTML canvas
}
Here is my service where I try to write the file :
import { ERROR, OK } from '#app/constant';
import { Image } from '#common/communication/Image';
import { readFile, writeFile } from 'fs';
import { injectable } from 'inversify';
import 'reflect-metadata';
import * as util from 'util';
#injectable()
export class SaveDrawService {
code: number;
constructor() {}
async writeData(image: Image): Promise<void> {
const base64Data = image.data.replace('data:image/png;base64,', '');
const write = util.promisify(writeFile);
return await write('test.png', base64Data, 'base64')
.then(() => {
this.code = OK; // 200
})
.catch((error: Error) => {
console.error(error);
this.code = ERROR; // 500
});
}
async readImageData(): Promise<string> {
const read = util.promisify(readFile);
return await read('test.png', { encoding: 'base64' });
}
extractFormat(base64Data: string) {}
}
The problem is that the "then" in write is not executed after the write and the "this.code" is therefore never updated and makes the request crash. I just started and I really don't know what can be causing this.
Here is my request I make to test the code:
On my server the POST is received and my server log this :
POST /api/draw/write 500 20.825 ms - 92
UPDATE: both my GET and POST return a error, but they are writing and reading the file on the server (I verify by making a POST and after a GET with logs to see if they are the same)
I think this is what you should change. Don't use async/await with then/catch, these are two different notation to wait for asynchronous code and get data.
async writeData(image: Image): Promise<void> {
const base64Data = image.data.replace('data:image/png;base64,', '');
const write = util.promisify(writeFile);
const resp = await write('test.png', base64Data, 'base64');
if (resp.ok) // whatever your condition
{
this.code = OK;
} else {
console.error(resp.error); // show error here
this.code = ERROR;
}
}
Check here for more details.

Resources