"feat: implement AccessLog and AppLog APIs with CRUD operations and test cases"
This commit is contained in:
24
03_source/cms_backend/src/app/api/AccessLog/_GUIDELINES.md
Normal file
24
03_source/cms_backend/src/app/api/AccessLog/_GUIDELINES.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# GUIDELINE
|
||||
|
||||
- this is a AppLog api endpoint
|
||||
- this is a demo to handle AppLog record
|
||||
- use single file for single db table/collection only
|
||||
|
||||
## `route.ts`
|
||||
|
||||
`route.ts` - handle `GET`, `POST`, `PUT`, `DELETE`
|
||||
`detail/route.ts` - handle `GET` request of specific `AppLog` by id
|
||||
|
||||
## `test.http`
|
||||
|
||||
store test request
|
||||
|
||||
## `../../services/AppLog.service.ts`
|
||||
|
||||
AppLog schema CRUD handler
|
||||
|
||||
`listAppLogs` - list AppLog record
|
||||
`getAppLog` - get AppLog record by id
|
||||
`createNewAppLog` - create AppLog record
|
||||
`updateAppLog` - update AppLog record by id
|
||||
`deleteAppLog` - delete AppLog record by id
|
38
03_source/cms_backend/src/app/api/AccessLog/detail/route.ts
Normal file
38
03_source/cms_backend/src/app/api/AccessLog/detail/route.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
// src/app/api/AccessLog/detail/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// Get single AccessLog record detail
|
||||
//
|
||||
// RULES:
|
||||
// - Return complete AccessLog details
|
||||
//
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { AccessLogService } from '../../../../modules/AccessLog/AccessLog.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
**************************************
|
||||
* GET - Get AccessLog details
|
||||
**************************************
|
||||
*/
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = req.nextUrl;
|
||||
|
||||
// RULES: accessLogId must exist
|
||||
const accessLogId = searchParams.get('accessLogId');
|
||||
if (!accessLogId) return response({ message: 'accessLogId is required!' }, STATUS.BAD_REQUEST);
|
||||
|
||||
const accessLog = await AccessLogService.findById(accessLogId);
|
||||
|
||||
if (!accessLog) return response({ message: 'AccessLog not found!' }, STATUS.NOT_FOUND);
|
||||
|
||||
return response({ accessLog }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('AccessLog - Get details', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/AppLog/detail?appLogId=e1bb8e4a-da37-4dbc-b8f2-9ad233a102ad
|
||||
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/AppLog/detail?start=2025-01-01&end=2025-12-31
|
76
03_source/cms_backend/src/app/api/AccessLog/route.ts
Normal file
76
03_source/cms_backend/src/app/api/AccessLog/route.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { listAccessLogs } from 'src/app/services/AccessLog.service';
|
||||
|
||||
// import prisma from '../../lib/prisma';
|
||||
|
||||
export async function GET(req: NextRequest, res: NextResponse) {
|
||||
try {
|
||||
const result = await listAccessLogs();
|
||||
|
||||
return response(result, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('AccessLog - Get all', error);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// ***************************************
|
||||
// * POST - create AccessLog
|
||||
// ***************************************
|
||||
// */
|
||||
// export async function POST(req: NextRequest) {
|
||||
// const { data } = await req.json();
|
||||
|
||||
// try {
|
||||
// const createResult = await AccessLogService.create(data);
|
||||
|
||||
// return response(createResult, STATUS.OK);
|
||||
// } catch (error) {
|
||||
// return handleError('AccessLog - Create', error);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// ***************************************
|
||||
// * PUT - update AccessLog
|
||||
// ***************************************
|
||||
// */
|
||||
// export async function PUT(req: NextRequest) {
|
||||
// const { searchParams } = req.nextUrl;
|
||||
// const accessLogId = searchParams.get('accessLogId');
|
||||
|
||||
// const { data } = await req.json();
|
||||
|
||||
// try {
|
||||
// if (!accessLogId) throw new Error('accessLogId cannot be null');
|
||||
|
||||
// const updateResult = await AccessLogService.update(accessLogId, data);
|
||||
|
||||
// return response(updateResult, STATUS.OK);
|
||||
// } catch (error) {
|
||||
// return handleError('AccessLog - Update', error);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// ***************************************
|
||||
// * DELETE - delete AccessLog
|
||||
// ***************************************
|
||||
// */
|
||||
// export async function DELETE(req: NextRequest) {
|
||||
// const { searchParams } = req.nextUrl;
|
||||
// const accessLogId = searchParams.get('accessLogId');
|
||||
|
||||
// try {
|
||||
// if (!accessLogId) throw new Error('accessLogId cannot be null');
|
||||
|
||||
// const deleteResult = await AccessLogService.delete(accessLogId);
|
||||
|
||||
// return response(deleteResult, STATUS.OK);
|
||||
// } catch (error) {
|
||||
// return handleError('AccessLog - Delete', error);
|
||||
// }
|
||||
// }
|
33
03_source/cms_backend/src/app/api/AccessLog/test.http
Normal file
33
03_source/cms_backend/src/app/api/AccessLog/test.http
Normal file
@@ -0,0 +1,33 @@
|
||||
###
|
||||
GET http://localhost:7272/api/AccessLog
|
||||
|
||||
###
|
||||
GET http://localhost:7272/api/AccessLog?accessLogId=51f2f5dd-78be-4069-ba29-09d2a5026191
|
||||
|
||||
###
|
||||
POST http://localhost:7272/api/AccessLog
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"userId": "user123",
|
||||
"ipAddress": "192.168.1.1",
|
||||
"userAgent": "Mozilla/5.0",
|
||||
"action": "LOGIN",
|
||||
"path": "/api/auth/login",
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
|
||||
###
|
||||
PUT http://localhost:7272/api/AccessLog?accessLogId=51f2f5dd-78be-4069-ba29-09d2a5026191
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
|
||||
###
|
||||
DELETE http://localhost:7272/api/AccessLog?accessLogId=51f2f5dd-78be-4069-ba29-09d2a5026191
|
24
03_source/cms_backend/src/app/api/AppLog/_GUIDELINES.md
Normal file
24
03_source/cms_backend/src/app/api/AppLog/_GUIDELINES.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# GUIDELINE
|
||||
|
||||
- this is a AppLog api endpoint
|
||||
- this is a demo to handle AppLog record
|
||||
- use single file for single db table/collection only
|
||||
|
||||
## `route.ts`
|
||||
|
||||
`route.ts` - handle `GET`, `POST`, `PUT`, `DELETE`
|
||||
`detail/route.ts` - handle `GET` request of specific `AppLog` by id
|
||||
|
||||
## `test.http`
|
||||
|
||||
store test request
|
||||
|
||||
## `../../services/AppLog.service.ts`
|
||||
|
||||
AppLog schema CRUD handler
|
||||
|
||||
`listAppLogs` - list AppLog record
|
||||
`getAppLog` - get AppLog record by id
|
||||
`createNewAppLog` - create AppLog record
|
||||
`updateAppLog` - update AppLog record by id
|
||||
`deleteAppLog` - delete AppLog record by id
|
39
03_source/cms_backend/src/app/api/AppLog/detail/route.ts
Normal file
39
03_source/cms_backend/src/app/api/AppLog/detail/route.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// src/app/api/AppLog/detail/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// Get single AppLog record detail
|
||||
//
|
||||
// RULES:
|
||||
// - Return complete AppLog details
|
||||
//
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
**************************************
|
||||
* GET - Get AppLog details
|
||||
**************************************
|
||||
*/
|
||||
export async function GET(req: NextRequest) {
|
||||
// Original user details functionality
|
||||
try {
|
||||
const { searchParams } = req.nextUrl;
|
||||
|
||||
// RULES: appLogId must exist
|
||||
const appLogId = searchParams.get('appLogId');
|
||||
if (!appLogId) return response({ message: 'appLogId is required!' }, STATUS.BAD_REQUEST);
|
||||
|
||||
const appLog = await prisma.appLog.findFirst({ where: { id: appLogId } });
|
||||
|
||||
if (!appLog) return response({ message: 'AppLog not found!' }, STATUS.NOT_FOUND);
|
||||
|
||||
return response({ appLog }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('AppLog - Get details', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/AppLog/detail?appLogId=e1bb8e4a-da37-4dbc-b8f2-9ad233a102ad
|
||||
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/AppLog/detail?start=2025-01-01&end=2025-12-31
|
80
03_source/cms_backend/src/app/api/AppLog/route.ts
Normal file
80
03_source/cms_backend/src/app/api/AppLog/route.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { listAppLogs, deleteAppLog, updateAppLog, createNewAppLog } from 'src/app/services/AppLog.service';
|
||||
|
||||
// import prisma from '../../lib/prisma';
|
||||
|
||||
export async function GET(req: NextRequest, res: NextResponse) {
|
||||
try {
|
||||
const result = await listAppLogs();
|
||||
|
||||
return response(result, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('Post - Get latest', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* POST - create AppLog
|
||||
***************************************
|
||||
*/
|
||||
export async function POST(req: NextRequest) {
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
const createResult = await createNewAppLog(data);
|
||||
|
||||
return response(createResult, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('AppLog - Create', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* PUT - update AppLog
|
||||
***************************************
|
||||
*/
|
||||
export async function PUT(req: NextRequest) {
|
||||
const { searchParams } = req.nextUrl;
|
||||
const appLogId = searchParams.get('appLogId');
|
||||
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
if (!appLogId) throw new Error('appLogId cannot null');
|
||||
const id: number = parseInt(appLogId);
|
||||
|
||||
const updateResult = await updateAppLog(id, data);
|
||||
|
||||
return response(updateResult, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('AppLog - Update', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* DELETE - update AppLog
|
||||
***************************************
|
||||
*/
|
||||
export async function DELETE(req: NextRequest) {
|
||||
const { searchParams } = req.nextUrl;
|
||||
const appLogId = searchParams.get('appLogId');
|
||||
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
if (!appLogId) throw new Error('appLogId cannot null');
|
||||
const id: number = parseInt(appLogId);
|
||||
|
||||
const deleteResult = await deleteAppLog(id);
|
||||
|
||||
return response(deleteResult, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('AppLog - Update', error);
|
||||
}
|
||||
}
|
25
03_source/cms_backend/src/app/api/AppLog/test.http
Normal file
25
03_source/cms_backend/src/app/api/AppLog/test.http
Normal file
@@ -0,0 +1,25 @@
|
||||
###
|
||||
GET http://localhost:7272/api/AppLog
|
||||
|
||||
###
|
||||
GET http://localhost:7272/api/AppLog?appLogId=51f2f5dd-78be-4069-ba29-09d2a5026191
|
||||
|
||||
###
|
||||
POST http://localhost:7272/api/AppLog?appLogId=1
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"data":{"hello": "test"}
|
||||
}
|
||||
|
||||
###
|
||||
PUT http://localhost:7272/api/AppLog?appLogId=1
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"data": {"hello": "test"}
|
||||
}
|
||||
|
||||
|
||||
###
|
||||
DELETE http://localhost:7272/api/AppLog?appLogId=1
|
@@ -3,12 +3,13 @@ import type { NextRequest } from 'next/server';
|
||||
import { sign } from 'src/utils/jwt';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { _users, JWT_SECRET, JWT_EXPIRES_IN } from 'src/_mock/_auth';
|
||||
import { JWT_SECRET, JWT_EXPIRES_IN } from 'src/_mock/_auth';
|
||||
import { createAccessLog } from 'src/app/services/AccessLog.service';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
/**
|
||||
* This API is used for demo purpose only
|
||||
* You should use a real database
|
||||
@@ -17,29 +18,36 @@ export const runtime = 'edge';
|
||||
* You should not expose the JWT_SECRET in the client side
|
||||
*/
|
||||
|
||||
const ERR_USER_NOT_FOUND = 'There is no user corresponding to the email address.';
|
||||
const ERR_WRONG_PASSWORD = 'Wrong password';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const debug = { 'req.headers': Object.fromEntries(req.headers.entries()) };
|
||||
|
||||
try {
|
||||
const { email, password } = await req.json();
|
||||
|
||||
const currentUser = _users.find((user) => user.email === email);
|
||||
|
||||
const currentUser = await prisma.user.findFirst({ where: { email } });
|
||||
if (!currentUser) {
|
||||
return response(
|
||||
{ message: 'There is no user corresponding to the email address.' },
|
||||
STATUS.UNAUTHORIZED
|
||||
);
|
||||
await createAccessLog('', `user tried login with email ${email}`, { debug });
|
||||
return response({ message: ERR_USER_NOT_FOUND }, STATUS.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (currentUser?.password !== password) {
|
||||
return response({ message: 'Wrong password' }, STATUS.UNAUTHORIZED);
|
||||
await createAccessLog(currentUser.id, 'user logged with wrong password', { debug });
|
||||
return response({ message: ERR_WRONG_PASSWORD }, STATUS.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
const accessToken = await sign({ userId: currentUser?.id }, JWT_SECRET, {
|
||||
expiresIn: JWT_EXPIRES_IN,
|
||||
});
|
||||
|
||||
return response({ user: currentUser, accessToken }, 200);
|
||||
await createAccessLog(currentUser.id, 'access granted', { debug });
|
||||
|
||||
return response({ user: currentUser, accessToken }, STATUS.OK);
|
||||
} catch (error) {
|
||||
await createAccessLog('', 'attempted login but failed', { debug, error });
|
||||
|
||||
return handleError('Auth - Sign in', error);
|
||||
}
|
||||
}
|
||||
|
29
03_source/cms_backend/src/app/api/auth/sign-in/test.http
Normal file
29
03_source/cms_backend/src/app/api/auth/sign-in/test.http
Normal file
@@ -0,0 +1,29 @@
|
||||
###
|
||||
# username and password ok
|
||||
POST http://localhost:7272/api/auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"email": "demo@minimals.cc",
|
||||
"password": "@2Minimal"
|
||||
}
|
||||
|
||||
###
|
||||
# There is no user corresponding to the email address.
|
||||
POST http://localhost:7272/api/auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"email": "demo@minimals1.cc",
|
||||
"password": "@2Minimal"
|
||||
}
|
||||
|
||||
###
|
||||
# Wrong password
|
||||
POST http://localhost:7272/api/auth/sign-in
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"email": "demo@minimals.cc",
|
||||
"password": "@2Min111imal"
|
||||
}
|
@@ -24,10 +24,7 @@ export async function POST(req: NextRequest) {
|
||||
const userExists = _users.find((user) => user.email === email);
|
||||
|
||||
if (userExists) {
|
||||
return response(
|
||||
{ message: 'There already exists an account with the given email address.' },
|
||||
STATUS.CONFLICT
|
||||
);
|
||||
return response({ message: 'There already exists an account with the given email address.' }, STATUS.CONFLICT);
|
||||
}
|
||||
|
||||
const newUser = {
|
||||
|
Reference in New Issue
Block a user