refactor: apprise API rest compliant

This commit is contained in:
Ravinou 2025-04-12 18:59:06 +02:00
commit 533bfce0d0
No known key found for this signature in database
GPG key ID: EEEE670C40F6A4D7
22 changed files with 527 additions and 594 deletions

View file

@ -1,94 +0,0 @@
import { getServerSession } from 'next-auth/next';
import { createMocks } from 'node-mocks-http';
import { ConfigService } from '~/services';
import handler from '~/pages/api/account/getAppriseAlert';
vi.mock('next-auth/next');
vi.mock('~/services');
describe('Get Apprise Alert API', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 405 if the method is not GET', async () => {
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 401 if the user is not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
expect(res._getJSONData()).toEqual({ message: 'You must be logged in.' });
});
it('should return 400 if the user does not exist', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'nonexistent' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseAlert: true,
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should return appriseAlert value if the user exists', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseAlert: true,
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({ appriseAlert: true });
});
it('should return 500 if there is an error reading the file', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockImplementation(() => {
throw new Error();
});
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});

View file

@ -1,45 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';
import { ErrorResponse, AppriseAlertResponse } from '~/types';
import { authOptions } from '../auth/[...nextauth]';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<AppriseAlertResponse | ErrorResponse>
) {
if (req.method !== 'GET') {
res.status(405).json({ message: 'Bad request on API' });
return;
}
//Verify that the user is logged in.
const session = await getServerSession(req, res, authOptions);
if (!session) {
res.status(401).json({ message: 'You must be logged in.' });
return;
}
try {
const usersList = await ConfigService.getUsersList();
//Verify that the user of the session exists
const user = usersList.find((u) => u.username === session.user?.name);
if (!user) {
res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
return;
}
res.status(200).json({ appriseAlert: user.appriseAlert });
} catch (error: any) {
console.log(error);
const errorMessage =
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator';
res.status(500).json({ status: 500, message: errorMessage });
}
}

View file

@ -1,45 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';
import { authOptions } from '../auth/[...nextauth]';
import { AppriseModeDTO, ErrorResponse } from '~/types';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<AppriseModeDTO | ErrorResponse>
) {
if (req.method !== 'GET') {
res.status(405).json({ message: 'Bad request on API' });
return;
}
//Verify that the user is logged in.
const session = await getServerSession(req, res, authOptions);
if (!session) {
res.status(401).json({ message: 'You must be logged in.' });
return;
}
try {
const usersList = await ConfigService.getUsersList();
//Verify that the user of the session exists
const user = usersList.find((u) => u.username === session.user?.name);
if (!user) {
res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
return;
}
res.status(200).json({
appriseMode: user.appriseMode,
appriseStatelessURL: user.appriseStatelessURL,
});
} catch (error: any) {
console.log(error);
const errorMessage =
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator';
res.status(500).json({ status: 500, message: errorMessage });
}
}

View file

@ -1,97 +0,0 @@
import { getServerSession } from 'next-auth/next';
import { createMocks } from 'node-mocks-http';
import { ConfigService } from '~/services';
import handler from '~/pages/api/account/getAppriseServices';
vi.mock('next-auth/next');
vi.mock('~/services');
describe('Get Apprise Services API', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 405 if the method is not GET', async () => {
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 401 if the user is not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
expect(res._getJSONData()).toEqual({ message: 'You must be logged in.' });
});
it('should return 400 if the user does not exist', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'nonexistent' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseServices: ['service1', 'service2'],
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should return appriseServices if the user exists', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseServices: ['service1', 'service2'],
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({
appriseServices: ['service1', 'service2'],
});
});
it('should return 500 if there is an error reading the file', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockImplementation(() => {
throw new Error();
});
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});

View file

@ -1,45 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';
import { authOptions } from '../auth/[...nextauth]';
import { AppriseServicesDTO, ErrorResponse } from '~/types';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<AppriseServicesDTO | ErrorResponse>
) {
if (req.method !== 'GET') {
res.status(405).json({ message: 'Bad request on API' });
return;
}
//Verify that the user is logged in.
const session = await getServerSession(req, res, authOptions);
if (!session) {
res.status(401).json({ message: 'You must be logged in.' });
return;
}
try {
const usersList = await ConfigService.getUsersList();
//Verify that the user of the session exists
const user = usersList.find((u) => u.username === session.user?.name);
if (!user) {
res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
return;
}
res.status(200).json({
appriseServices: user.appriseServices,
});
} catch (error: any) {
console.log(error);
const errorMessage =
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator';
res.status(500).json({ status: 500, message: errorMessage });
}
}

View file

@ -1,52 +0,0 @@
// Imports
import { ConfigService } from '~/services';
import { authOptions } from '../auth/[...nextauth]';
import { getServerSession } from 'next-auth/next';
import { NextApiRequest, NextApiResponse } from 'next';
import { ErrorResponse, AppriseAlertDTO, AppriseAlertResponse } from '~/types';
export default async function handler(
req: NextApiRequest & { body: AppriseAlertDTO },
res: NextApiResponse<AppriseAlertResponse | ErrorResponse>
) {
if (req.method !== 'PUT') {
return res.status(405);
}
const session = await getServerSession(req, res, authOptions);
if (!session) {
return res.status(401);
}
const { appriseAlert } = req.body;
if (typeof appriseAlert !== 'boolean') {
return res.status(422).json({ message: 'Unexpected data' });
}
try {
const usersList = await ConfigService.getUsersList();
const userIndex = usersList.findIndex((u) => u.username === session.user?.name);
if (userIndex === -1) {
return res
.status(400)
.json({ message: 'User is incorrect. Please, logout to update your session.' });
}
const updatedUsersList = usersList.map((user, index) =>
index === userIndex ? { ...user, appriseAlert } : user
);
await ConfigService.updateUsersList(updatedUsersList);
return res.status(200).json({ message: 'Successful API send' });
} catch (error: any) {
console.log(error);
return res.status(500).json({
status: 500,
message:
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator',
});
}
}

View file

@ -1,94 +0,0 @@
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/account/updateAppriseMode';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';
import { AppriseModeEnum } from '~/types/domain/config.types';
vi.mock('next-auth/next');
vi.mock('~/services');
describe('Apprise Mode API', () => {
it('should return 405 if method is not PUT', async () => {
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 401 if not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'PUT' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 422 if invalid data is provided', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'testuser' } });
const { req, res } = createMocks({
method: 'PUT',
body: { appriseMode: 'invalid-mode', appriseStatelessURL: 'https://example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(422);
});
it('should return 400 if user does not exist', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'unknownuser' } });
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseMode: AppriseModeEnum.PACKAGE,
},
]);
const { req, res } = createMocks({
method: 'PUT',
body: { appriseMode: 'stateless', appriseStatelessURL: 'https://example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
});
it('should update user settings and return 200', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'testuser' } });
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseMode: AppriseModeEnum.PACKAGE,
},
]);
vi.mocked(ConfigService.updateUsersList).mockResolvedValue();
const { req, res } = createMocks({
method: 'PUT',
body: { appriseMode: 'stateless', appriseStatelessURL: 'https://example.com' },
});
await handler(req, res);
expect(ConfigService.updateUsersList).toHaveBeenCalledWith([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseMode: AppriseModeEnum.STATELESS,
appriseStatelessURL: 'https://example.com',
},
]);
expect(res._getStatusCode()).toBe(200);
});
});

View file

@ -1,52 +0,0 @@
import { ConfigService } from '~/services';
import { authOptions } from '../auth/[...nextauth]';
import { getServerSession } from 'next-auth/next';
import { NextApiRequest, NextApiResponse } from 'next';
import { AppriseModeDTO, ErrorResponse } from '~/types';
export default async function handler(
req: NextApiRequest & { body: AppriseModeDTO },
res: NextApiResponse<ErrorResponse>
) {
if (req.method !== 'PUT') {
return res.status(405);
}
const session = await getServerSession(req, res, authOptions);
if (!session) {
return res.status(401);
}
const { appriseMode, appriseStatelessURL } = req.body;
if (!['package', 'stateless'].includes(appriseMode)) {
return res.status(422).json({ message: 'Unexpected data' });
}
try {
const usersList = await ConfigService.getUsersList();
const userIndex = usersList.findIndex((user) => user.username === session.user?.name);
if (userIndex === -1) {
return res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
}
const updatedUsersList = usersList.map((user, index) =>
index === userIndex ? { ...user, appriseMode, appriseStatelessURL } : user
);
await ConfigService.updateUsersList(updatedUsersList);
return res.status(200).json({ message: 'Successful API send' });
} catch (error: any) {
console.log(error);
return res.status(500).json({
status: 500,
message:
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator',
});
}
}

View file

@ -1,54 +0,0 @@
import { authOptions } from '../auth/[...nextauth]';
import { getServerSession } from 'next-auth/next';
import { NextApiRequest, NextApiResponse } from 'next';
import { ConfigService } from '~/services';
import { AppriseServicesDTO, ErrorResponse } from '~/types';
export default async function handler(
req: NextApiRequest & { body: AppriseServicesDTO },
res: NextApiResponse<ErrorResponse>
) {
if (req.method !== 'PUT') {
return res.status(405);
}
const session = await getServerSession(req, res, authOptions);
if (!session) {
return res.status(401);
}
const { appriseURLs } = req.body;
try {
const usersList = await ConfigService.getUsersList();
const userIndex = usersList.findIndex((user) => user.username === session.user?.name);
if (userIndex === -1) {
return res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
}
//Build the services URLs list from form
const appriseURLsArray = appriseURLs
.replace(/ /g, '')
.split('\n')
.filter((el: string) => el != '');
const updatedUsersList = usersList.map((user, index) =>
index === userIndex ? { ...user, appriseServices: appriseURLsArray } : user
);
await ConfigService.updateUsersList(updatedUsersList);
return res.status(200).json({ message: 'Successful API send' });
} catch (error: any) {
console.log(error);
return res.status(500).json({
status: 500,
message:
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator',
});
}
}

View file

@ -1,5 +1,5 @@
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/cronjob/checkStatus';
import handler from '~/pages/api/cronjob/check-status';
import { ConfigService, NotifService, ShellService } from '~/services';
import { AppriseModeEnum } from '~/types/domain/config.types';

View file

@ -1,4 +1,4 @@
import handler from '~/pages/api/cronjob/getStorageUsed';
import handler from '~/pages/api/cronjob/check-storage';
import { createMocks } from 'node-mocks-http';
import { ConfigService, ShellService } from '~/services';

View file

@ -1,4 +1,4 @@
import handler from '~/pages/api/account/tokenManager';
import handler from '~/pages/api/integration/token-manager';
import { createMocks } from 'node-mocks-http';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';

View file

@ -1,12 +1,101 @@
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/account/updateAppriseAlert';
import { getServerSession } from 'next-auth/next';
import { createMocks } from 'node-mocks-http';
import { ConfigService } from '~/services';
import handler from 'pages/api/notif/apprise/alert';
vi.mock('next-auth/next');
vi.mock('~/services');
describe('Notifications API', () => {
describe('Get Apprise Alert API', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 405 if the method is not GET', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 401 if the user is not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 400 if the user does not exist', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'nonexistent' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseAlert: true,
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should return appriseAlert value if the user exists', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseAlert: true,
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({ appriseAlert: true });
});
it('should return 500 if there is an error reading the file', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockImplementation(() => {
throw new Error();
});
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});
describe('Update Apprise Alert API', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
@ -14,8 +103,11 @@ describe('Notifications API', () => {
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 405 if the method is not PUT', async () => {
const { req, res } = createMocks({ method: 'GET' });
it('should return 405 if the method is not allowed', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});

View file

@ -0,0 +1,75 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';
import { ErrorResponse, AppriseAlertResponse } from '~/types';
import { authOptions } from '../../auth/[...nextauth]';
import ApiResponse from '~/helpers/functions/apiResponse';
export default async function handler(
req: NextApiRequest & { body: { appriseAlert: boolean } },
res: NextApiResponse<AppriseAlertResponse | ErrorResponse>
) {
const session = await getServerSession(req, res, authOptions);
if (!session) {
return ApiResponse.unauthorized(res);
}
if (req.method == 'GET') {
try {
const usersList = await ConfigService.getUsersList();
//Verify that the user of the session exists
const user = usersList.find((u) => u.username === session.user?.name);
if (!user) {
res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
return;
}
res.status(200).json({ appriseAlert: user.appriseAlert });
} catch (error: any) {
console.log(error);
const errorMessage =
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator';
res.status(500).json({ status: 500, message: errorMessage });
}
} else if (req.method == 'PUT') {
const { appriseAlert } = req.body;
if (typeof appriseAlert !== 'boolean') {
return res.status(422).json({ message: 'Unexpected data' });
}
try {
const usersList = await ConfigService.getUsersList();
const userIndex = usersList.findIndex((u) => u.username === session.user?.name);
if (userIndex === -1) {
return res
.status(400)
.json({ message: 'User is incorrect. Please, logout to update your session.' });
}
const updatedUsersList = usersList.map((user, index) =>
index === userIndex ? { ...user, appriseAlert } : user
);
await ConfigService.updateUsersList(updatedUsersList);
return res.status(200).json({ message: 'Successful API send' });
} catch (error: any) {
console.log(error);
return res.status(500).json({
status: 500,
message:
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator',
});
}
} else {
return ApiResponse.methodNotAllowed(res);
}
}

View file

@ -1,7 +1,7 @@
import { getServerSession } from 'next-auth/next';
import { createMocks } from 'node-mocks-http';
import { ConfigService } from '~/services';
import handler from '~/pages/api/account/getAppriseMode';
import handler from '~/pages/api/notif/apprise/mode';
import { AppriseModeEnum } from '~/types/domain/config.types';
vi.mock('next-auth/next');
@ -14,6 +14,9 @@ describe('Get Apprise Mode API', () => {
});
it('should return 405 if the method is not GET', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
@ -24,7 +27,6 @@ describe('Get Apprise Mode API', () => {
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
expect(res._getJSONData()).toEqual({ message: 'You must be logged in.' });
});
it('should return 400 if the user does not exist', async () => {
@ -98,3 +100,92 @@ describe('Get Apprise Mode API', () => {
});
});
});
describe('Apprise Mode Update API', () => {
it('should return 405 if method is not allowed', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 401 if not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'PUT' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 422 if invalid data is provided', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'testuser' } });
const { req, res } = createMocks({
method: 'PUT',
body: { appriseMode: 'invalid-mode', appriseStatelessURL: 'https://example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(422);
});
it('should return 400 if user does not exist', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'unknownuser' } });
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseMode: AppriseModeEnum.PACKAGE,
},
]);
const { req, res } = createMocks({
method: 'PUT',
body: { appriseMode: 'stateless', appriseStatelessURL: 'https://example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
});
it('should update user settings and return 200', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'testuser' } });
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseMode: AppriseModeEnum.PACKAGE,
},
]);
vi.mocked(ConfigService.updateUsersList).mockResolvedValue();
const { req, res } = createMocks({
method: 'PUT',
body: { appriseMode: 'stateless', appriseStatelessURL: 'https://example.com' },
});
await handler(req, res);
expect(ConfigService.updateUsersList).toHaveBeenCalledWith([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseMode: AppriseModeEnum.STATELESS,
appriseStatelessURL: 'https://example.com',
},
]);
expect(res._getStatusCode()).toBe(200);
});
});

View file

@ -0,0 +1,78 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';
import { authOptions } from '../../auth/[...nextauth]';
import { AppriseModeDTO, ErrorResponse } from '~/types';
import ApiResponse from '~/helpers/functions/apiResponse';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<AppriseModeDTO | ErrorResponse>
) {
const session = await getServerSession(req, res, authOptions);
if (!session) {
return ApiResponse.unauthorized(res);
}
if (req.method == 'GET') {
try {
const usersList = await ConfigService.getUsersList();
//Verify that the user of the session exists
const user = usersList.find((u) => u.username === session.user?.name);
if (!user) {
res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
return;
}
res.status(200).json({
appriseMode: user.appriseMode,
appriseStatelessURL: user.appriseStatelessURL,
});
} catch (error: any) {
console.log(error);
const errorMessage =
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator';
res.status(500).json({ status: 500, message: errorMessage });
}
} else if (req.method == 'PUT') {
const { appriseMode, appriseStatelessURL } = req.body;
if (!['package', 'stateless'].includes(appriseMode)) {
return res.status(422).json({ message: 'Unexpected data' });
}
try {
const usersList = await ConfigService.getUsersList();
const userIndex = usersList.findIndex((user) => user.username === session.user?.name);
if (userIndex === -1) {
return res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
}
const updatedUsersList = usersList.map((user, index) =>
index === userIndex ? { ...user, appriseMode, appriseStatelessURL } : user
);
await ConfigService.updateUsersList(updatedUsersList);
return res.status(200).json({ message: 'Successful API send' });
} catch (error: any) {
console.log(error);
return res.status(500).json({
status: 500,
message:
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator',
});
}
} else {
return ApiResponse.methodNotAllowed(res);
}
}

View file

@ -4,7 +4,7 @@ import { getServerSession } from 'next-auth/next';
import { promisify } from 'util';
import { ConfigService } from '~/services';
import { ErrorResponse, SuccessResponse } from '~/types';
import { authOptions } from '../auth/[...nextauth]';
import { authOptions } from '../../auth/[...nextauth]';
const execAsync = promisify(exec);

View file

@ -1,12 +1,104 @@
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/account/updateAppriseServices';
import { getServerSession } from 'next-auth/next';
import { createMocks } from 'node-mocks-http';
import { ConfigService } from '~/services';
import handler from '~/pages/api/notif/apprise/services';
vi.mock('next-auth/next');
vi.mock('~/services');
describe('PUT /api/account/updateAppriseURLs', () => {
describe('Get Apprise Services API', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 405 if the method is not GET', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 401 if the user is not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 400 if the user does not exist', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'nonexistent' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseServices: ['service1', 'service2'],
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should return appriseServices if the user exists', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockResolvedValue([
{
id: 1,
username: 'testuser',
password: 'hashedpassword',
roles: ['user'],
email: 'testuser@example.com',
appriseServices: ['service1', 'service2'],
},
]);
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({
appriseServices: ['service1', 'service2'],
});
});
it('should return 500 if there is an error reading the file', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'testuser' },
});
vi.mocked(ConfigService.getUsersList).mockImplementation(() => {
throw new Error();
});
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});
describe('PUT Apprise services API', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
@ -24,8 +116,11 @@ describe('PUT /api/account/updateAppriseURLs', () => {
expect(res._getStatusCode()).toBe(401);
});
it('should return 405 if method is not PUT', async () => {
const { req, res } = createMocks({ method: 'GET' });
it('should return 405 if method is not handling', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({ method: 'DELETE' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);

View file

@ -0,0 +1,80 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getServerSession } from 'next-auth/next';
import { ConfigService } from '~/services';
import { authOptions } from '../../auth/[...nextauth]';
import { AppriseServicesDTO, ErrorResponse } from '~/types';
import ApiResponse from '~/helpers/functions/apiResponse';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<AppriseServicesDTO | ErrorResponse>
) {
const session = await getServerSession(req, res, authOptions);
if (!session) {
return ApiResponse.unauthorized(res);
}
if (req.method == 'GET') {
try {
const usersList = await ConfigService.getUsersList();
//Verify that the user of the session exists
const user = usersList.find((u) => u.username === session.user?.name);
if (!user) {
res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
return;
}
res.status(200).json({
appriseServices: user.appriseServices,
});
} catch (error: any) {
console.log(error);
const errorMessage =
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator';
res.status(500).json({ status: 500, message: errorMessage });
}
} else if (req.method == 'PUT') {
const { appriseURLs } = req.body;
try {
const usersList = await ConfigService.getUsersList();
const userIndex = usersList.findIndex((user) => user.username === session.user?.name);
if (userIndex === -1) {
return res.status(400).json({
message: 'User is incorrect. Please, logout to update your session.',
});
}
//Build the services URLs list from form
const appriseURLsArray = appriseURLs
.replace(/ /g, '')
.split('\n')
.filter((el: string) => el != '');
const updatedUsersList = usersList.map((user, index) =>
index === userIndex ? { ...user, appriseServices: appriseURLsArray } : user
);
await ConfigService.updateUsersList(updatedUsersList);
return res.status(200).json({ message: 'Successful API send' });
} catch (error: any) {
console.log(error);
return res.status(500).json({
status: 500,
message:
error.code === 'ENOENT'
? 'No such file or directory'
: 'API error, contact the administrator',
});
}
} else {
return ApiResponse.methodNotAllowed(res);
}
}