feat: new module create topic
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
import { Elysia } from 'elysia'
|
import { Elysia } from 'elysia'
|
||||||
import deviceRouter from '~/modules/device/router'
|
import deviceRouter from '~/modules/device/router'
|
||||||
import authRouter from '~/modules/auth/router'
|
import authRouter from '~/modules/auth/router'
|
||||||
|
import topicsRouter from '~/modules/topics/router'
|
||||||
|
|
||||||
export const router = new Elysia({
|
export const router = new Elysia({
|
||||||
name: 'codebase.router',
|
name: 'codebase.router',
|
||||||
})
|
})
|
||||||
.use(authRouter)
|
.use(authRouter)
|
||||||
|
.use(topicsRouter)
|
||||||
.use(deviceRouter)
|
.use(deviceRouter)
|
||||||
.get('/', () => ({
|
.get('/', () => ({
|
||||||
message: 'This service is running as expected.',
|
message: 'This service is running as expected.',
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { HTTPHeaders, StatusMap } from 'elysia'
|
import { Elysia } from 'elysia'
|
||||||
import { basicAuth as credential } from '~/config'
|
import { basicAuth as credential } from '~/config'
|
||||||
import { UnauthenticatedError } from '~/helpers/errors'
|
import { UnauthenticatedError } from '~/helpers/errors'
|
||||||
import { logger } from '~/plugins'
|
import { logger } from '~/plugins'
|
||||||
|
|
||||||
export const basicAuthMiddleware = (
|
export const basicAuthMacro = new Elysia()
|
||||||
headers: Record<string, string | undefined>,
|
.macro({
|
||||||
set: {
|
verifyBasicAuth: (enabled: boolean) => ({
|
||||||
headers: HTTPHeaders
|
beforeHandle({ set, headers }) {
|
||||||
status?: number | keyof StatusMap
|
if (!enabled) return
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const expectedAuth = Buffer.from(`${credential.username}:${credential.password}`).toString('base64')
|
const expectedAuth = Buffer.from(`${credential.username}:${credential.password}`).toString('base64')
|
||||||
|
|
||||||
if (headers.authorization !== `Basic ${expectedAuth}`) {
|
if (headers.authorization !== `Basic ${expectedAuth}`) {
|
||||||
logger.debug({
|
logger.debug({
|
||||||
'headers.authorization': headers.authorization,
|
'headers.authorization': headers.authorization,
|
||||||
@@ -19,4 +19,6 @@ export const basicAuthMiddleware = (
|
|||||||
set.headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
|
set.headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
|
||||||
throw new UnauthenticatedError('Unauthenticated.')
|
throw new UnauthenticatedError('Unauthenticated.')
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { basicAuthMiddleware } from './basicAuth'
|
export { basicAuthMacro } from './basicAuth'
|
||||||
export { bearerAuthMiddleware } from './bearerAuth'
|
export { bearerAuthMiddleware } from './bearerAuth'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Elysia, t } from 'elysia'
|
import { Elysia, t } from 'elysia'
|
||||||
import { createMerchantBody, createMerchantResponseSchema } from './schema'
|
import { createMerchantBody, createMerchantResponseSchema } from './schema'
|
||||||
import CreateMerchantService from './commands/service'
|
import CreateMerchantService from './commands/service'
|
||||||
import { basicAuthMiddleware } from '~/middlewares/basicAuth'
|
import { basicAuthMacro } from '~/middlewares/basicAuth'
|
||||||
import { DataAlreadyExistsError } from '~/helpers/errors'
|
import { DataAlreadyExistsError } from '~/helpers/errors'
|
||||||
import { apiKeyAuthMacro } from '~/middlewares/apiKeyAuth'
|
import { apiKeyAuthMacro } from '~/middlewares/apiKeyAuth'
|
||||||
|
|
||||||
@@ -10,9 +10,8 @@ export const router = new Elysia({
|
|||||||
detail: { tags: ['Auth'] },
|
detail: { tags: ['Auth'] },
|
||||||
prefix: '/auth',
|
prefix: '/auth',
|
||||||
})
|
})
|
||||||
.post('/get-key', async ({ body, headers, set }) => {
|
.use(basicAuthMacro)
|
||||||
basicAuthMiddleware(headers, set)
|
.post('/get-key', async ({ body, set }) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await CreateMerchantService.createMerchant(body)
|
const result = await CreateMerchantService.createMerchant(body)
|
||||||
return {
|
return {
|
||||||
@@ -39,6 +38,7 @@ export const router = new Elysia({
|
|||||||
response: createMerchantResponseSchema,
|
response: createMerchantResponseSchema,
|
||||||
detail: {
|
detail: {
|
||||||
security: [{ basicAuth: [] }],
|
security: [{ basicAuth: [] }],
|
||||||
|
verifyBasicAuth: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.use(apiKeyAuthMacro)
|
.use(apiKeyAuthMacro)
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { httpConfig } from '~/config'
|
||||||
|
import logger from '~/plugins/logger'
|
||||||
|
import { PatchTopic, PostDeletetopic } from '../schema'
|
||||||
|
|
||||||
|
export async function postTopic(payload: PostDeletetopic) {
|
||||||
|
try {
|
||||||
|
const request = await fetch(`${httpConfig.baseUrl}/addTopic`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
})
|
||||||
|
if (request.status != 200) {
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: await request.json(),
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e, 'ERROR')
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function delTopic(payload: PostDeletetopic) {
|
||||||
|
try {
|
||||||
|
const request = await fetch(`${httpConfig.baseUrl}/deleteTopic`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
})
|
||||||
|
if (request.status != 200) {
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: await request.json(),
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function patchTopic(payload: PatchTopic) {
|
||||||
|
try {
|
||||||
|
const request = await fetch(`${httpConfig.baseUrl}/editTopic`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
})
|
||||||
|
if (request.status != 200) {
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: await request.json(),
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { PatchTopic, PostDeleteTopicResponseSchema, PostDeletetopic } from '../schema'
|
||||||
|
import { delTopic, patchTopic, postTopic } from './command'
|
||||||
|
export default abstract class CommandService {
|
||||||
|
static async postTopic(body: PostDeletetopic): Promise<PostDeleteTopicResponseSchema> {
|
||||||
|
const postRequest = await postTopic(body)
|
||||||
|
if (postRequest.status != 200) {
|
||||||
|
return {
|
||||||
|
status: postRequest.status,
|
||||||
|
message: postRequest.message,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: 'Create topic success',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async delTopic(body: PostDeletetopic): Promise<PostDeleteTopicResponseSchema> {
|
||||||
|
const postRequest = await delTopic(body)
|
||||||
|
if (postRequest.status != 200) {
|
||||||
|
return {
|
||||||
|
status: postRequest.status,
|
||||||
|
message: postRequest.message,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: 'Delete topic success',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async patchTopic(body: PatchTopic): Promise<PostDeleteTopicResponseSchema> {
|
||||||
|
const postRequest = await patchTopic(body)
|
||||||
|
if (postRequest.status != 200) {
|
||||||
|
return {
|
||||||
|
status: postRequest.status,
|
||||||
|
message: postRequest.message,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: 'Update topic success',
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { httpConfig } from '~/config'
|
||||||
|
import logger from '~/plugins/logger'
|
||||||
|
import { ListTopics } from '../schema'
|
||||||
|
|
||||||
|
export async function getCommands(payload: ListTopics) {
|
||||||
|
try {
|
||||||
|
const qParams = new URLSearchParams(payload).toString()
|
||||||
|
const request = await fetch(`${httpConfig.baseUrl}/listCommand?${qParams}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (request.status != 200) {
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: await request.json(),
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStateReply(payload: ListTopics) {
|
||||||
|
try {
|
||||||
|
const qParams = new URLSearchParams(payload).toString()
|
||||||
|
const request = await fetch(`${httpConfig.baseUrl}/listState-reply?${qParams}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (request.status != 200) {
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: request.status,
|
||||||
|
message: request.statusText,
|
||||||
|
data: await request.json(),
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { ListTopics } from '../schema'
|
||||||
|
import { getCommands, getStateReply } from './query'
|
||||||
|
|
||||||
|
export default abstract class QueryService {
|
||||||
|
static async getCommands(payload: ListTopics) {
|
||||||
|
const result = await getCommands(payload)
|
||||||
|
if (result.status != 200) {
|
||||||
|
return {
|
||||||
|
status: result.status,
|
||||||
|
message: result.message,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: 'Get commands success',
|
||||||
|
data: result.data.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getStateReply(payload: ListTopics) {
|
||||||
|
const result = await getStateReply(payload)
|
||||||
|
if (result.status != 200) {
|
||||||
|
return {
|
||||||
|
status: result.status,
|
||||||
|
message: result.message,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: 'Get state-reply success',
|
||||||
|
data: result.data.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { Elysia } from 'elysia'
|
||||||
|
import CommandService from './commands/service'
|
||||||
|
import { listTopicResponseSchema, listTopics, postDeleteTopic, postDeleteTopicResponseSchema } from './schema'
|
||||||
|
import QueryService from './queries/service'
|
||||||
|
import { basicAuthMacro } from '../../middlewares/basicAuth'
|
||||||
|
|
||||||
|
export const router = new Elysia({
|
||||||
|
name: 'modules.topics',
|
||||||
|
detail: { tags: ['Topics'] },
|
||||||
|
prefix: '/topics/v1',
|
||||||
|
})
|
||||||
|
.use(basicAuthMacro)
|
||||||
|
.get('/state-reply', async ({ query, set }) => {
|
||||||
|
const result = await QueryService.getStateReply(query)
|
||||||
|
if (result.status != 200) {
|
||||||
|
set.status = result.status
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}, {
|
||||||
|
query: listTopics,
|
||||||
|
response: listTopicResponseSchema,
|
||||||
|
verifyBasicAuth: true,
|
||||||
|
})
|
||||||
|
.get('/commands', async ({ query, set }) => {
|
||||||
|
const result = await QueryService.getCommands(query)
|
||||||
|
if (result.status != 200) {
|
||||||
|
set.status = result.status
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}, {
|
||||||
|
query: listTopics,
|
||||||
|
response: listTopicResponseSchema,
|
||||||
|
verifyBasicAuth: true,
|
||||||
|
})
|
||||||
|
.post('/', async ({ body, set }) => {
|
||||||
|
const result = await CommandService.postTopic(body)
|
||||||
|
if (result.status != 200) {
|
||||||
|
set.status = result.status
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}, {
|
||||||
|
body: postDeleteTopic,
|
||||||
|
response: postDeleteTopicResponseSchema,
|
||||||
|
verifyBasicAuth: true,
|
||||||
|
})
|
||||||
|
.patch('/', () => {
|
||||||
|
return 'Topics'
|
||||||
|
})
|
||||||
|
.delete('/', async ({ body, set }) => {
|
||||||
|
const result = await CommandService.delTopic(body)
|
||||||
|
if (result.status != 200) {
|
||||||
|
set.status = result.status
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}, {
|
||||||
|
body: postDeleteTopic,
|
||||||
|
response: postDeleteTopicResponseSchema,
|
||||||
|
verifyBasicAuth: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const postDeleteTopic = z.object({
|
||||||
|
topic: z.string(),
|
||||||
|
type: z.enum(['state-reply', 'commands']),
|
||||||
|
})
|
||||||
|
export type PostDeletetopic = z.infer<typeof postDeleteTopic>
|
||||||
|
|
||||||
|
export const patchTopic = z.object({
|
||||||
|
oldTopic: z.string(),
|
||||||
|
newTopic: z.string(),
|
||||||
|
type: z.enum(['state-reply', 'commands']),
|
||||||
|
})
|
||||||
|
export type PatchTopic = z.infer<typeof patchTopic>
|
||||||
|
|
||||||
|
export const listTopics = z.object({
|
||||||
|
topic: z.string().optional(),
|
||||||
|
})
|
||||||
|
export type ListTopics = z.infer<typeof listTopics>
|
||||||
|
|
||||||
|
export const postDeleteTopicResponseSchema = z.object({
|
||||||
|
status: z.number(),
|
||||||
|
message: z.string(),
|
||||||
|
data: z.any().nullable(),
|
||||||
|
})
|
||||||
|
export type PostDeleteTopicResponseSchema = z.infer<typeof postDeleteTopicResponseSchema>
|
||||||
|
|
||||||
|
export const listTopicResponseSchema = z.object({
|
||||||
|
status: z.number(),
|
||||||
|
data: z.array(
|
||||||
|
z.object({
|
||||||
|
topic: z.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
|
).nullable(),
|
||||||
|
})
|
||||||
|
export type ListTopicResponseSchema = z.infer<typeof listTopicResponseSchema>
|
||||||
|
|
||||||
|
export const postDeleteTopicRequestSchema = {
|
||||||
|
body: postDeleteTopic,
|
||||||
|
response: {
|
||||||
|
200: postDeleteTopicResponseSchema,
|
||||||
|
400: postDeleteTopicResponseSchema,
|
||||||
|
401: postDeleteTopicResponseSchema,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listTopicRequestSchema = {
|
||||||
|
query: listTopics,
|
||||||
|
response: {
|
||||||
|
200: listTopicResponseSchema,
|
||||||
|
400: listTopicResponseSchema,
|
||||||
|
401: listTopicResponseSchema,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -19,11 +19,12 @@ export const swaggerPlugin = openapi({
|
|||||||
'tags': [
|
'tags': [
|
||||||
{ name: 'Auth', description: 'Auth API' },
|
{ name: 'Auth', description: 'Auth API' },
|
||||||
{ name: 'Device', description: 'Device API' },
|
{ name: 'Device', description: 'Device API' },
|
||||||
|
{ name: 'Topics', description: 'Management Topic' },
|
||||||
],
|
],
|
||||||
// --- Grouping di atas tag ---
|
// --- Grouping di atas tag ---
|
||||||
'x-tagGroups': [
|
'x-tagGroups': [
|
||||||
{ name: 'Auth', tags: ['Auth'] },
|
{ name: 'Auth', tags: ['Auth'] },
|
||||||
{ name: 'Device', tags: ['Device'] },
|
{ name: 'Device', tags: ['Device', 'Topics'] },
|
||||||
],
|
],
|
||||||
'components': {
|
'components': {
|
||||||
securitySchemes: {
|
securitySchemes: {
|
||||||
|
|||||||
Reference in New Issue
Block a user