修改视频RTSP连接
parent
0a118e73bf
commit
e9b491dd08
@ -0,0 +1,21 @@
|
|||||||
|
CAMERA_HOST=192.168.1.68
|
||||||
|
CAMERA_PORT=554
|
||||||
|
CAMERA_USER=admin
|
||||||
|
CAMERA_PASSWORD=change-me
|
||||||
|
CAMERA_RTSP_PATH=/Stream/Live/101
|
||||||
|
|
||||||
|
WS_HOST=0.0.0.0
|
||||||
|
WS_PORT=8080
|
||||||
|
WS_PATH=/video
|
||||||
|
RTSP_TRANSPORT=tcp
|
||||||
|
VIDEO_TRANSCODE=0
|
||||||
|
FFMPEG_PATH=ffmpeg
|
||||||
|
|
||||||
|
CONTROL_HOST=0.0.0.0
|
||||||
|
CONTROL_PORT=8090
|
||||||
|
CONTROL_TRANSPORT=tcp
|
||||||
|
CONTROL_TCP_HOST=192.168.1.123
|
||||||
|
CONTROL_TCP_PORT=502
|
||||||
|
CONTROL_ADDRESS=1
|
||||||
|
CONTROL_SPEED=32
|
||||||
|
VITE_CONTROL_API_URL=http://localhost:8090/control
|
||||||
@ -0,0 +1,388 @@
|
|||||||
|
import dgram from 'node:dgram'
|
||||||
|
import {existsSync, readFileSync} from 'node:fs'
|
||||||
|
import http from 'node:http'
|
||||||
|
import net from 'node:net'
|
||||||
|
import {resolve} from 'node:path'
|
||||||
|
import {fileURLToPath} from 'node:url'
|
||||||
|
|
||||||
|
loadDotEnv()
|
||||||
|
|
||||||
|
const DEFAULT_ADDRESS = 0x01
|
||||||
|
const DEFAULT_SPEED = 0x20
|
||||||
|
|
||||||
|
const CONTROL_HOST = process.env.CONTROL_HOST || '0.0.0.0'
|
||||||
|
const CONTROL_PORT = Number(process.env.CONTROL_PORT || 8090)
|
||||||
|
const CONTROL_TRANSPORT = process.env.CONTROL_TRANSPORT || 'log'
|
||||||
|
const CONTROL_TCP_HOST = process.env.CONTROL_TCP_HOST || ''
|
||||||
|
const CONTROL_TCP_PORT = Number(process.env.CONTROL_TCP_PORT || 0)
|
||||||
|
const CONTROL_UDP_HOST = process.env.CONTROL_UDP_HOST || ''
|
||||||
|
const CONTROL_UDP_PORT = Number(process.env.CONTROL_UDP_PORT || 0)
|
||||||
|
const CONTROL_ADDRESS = parseByte(process.env.CONTROL_ADDRESS, DEFAULT_ADDRESS)
|
||||||
|
const CONTROL_SPEED = parseByte(process.env.CONTROL_SPEED, DEFAULT_SPEED)
|
||||||
|
|
||||||
|
function loadDotEnv() {
|
||||||
|
const envPath = resolve(process.cwd(), '.env')
|
||||||
|
|
||||||
|
if (!existsSync(envPath)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = readFileSync(envPath, 'utf8').split(/\r?\n/)
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
if (!trimmed || trimmed.startsWith('#')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const separatorIndex = trimmed.indexOf('=')
|
||||||
|
if (separatorIndex === -1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = trimmed.slice(0, separatorIndex).trim()
|
||||||
|
let value = trimmed.slice(separatorIndex + 1).trim()
|
||||||
|
if (
|
||||||
|
(value.startsWith('"') && value.endsWith('"')) ||
|
||||||
|
(value.startsWith("'") && value.endsWith("'"))
|
||||||
|
) {
|
||||||
|
value = value.slice(1, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env[key]) {
|
||||||
|
process.env[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseByte(value, fallback) {
|
||||||
|
if (!value) {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = String(value).trim().toLowerCase()
|
||||||
|
const parsed = normalized.startsWith('0x')
|
||||||
|
? Number.parseInt(normalized.slice(2), 16)
|
||||||
|
: Number.parseInt(normalized, 10)
|
||||||
|
|
||||||
|
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 0xff) {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampPelcoSpeed(value) {
|
||||||
|
return Math.max(0x00, Math.min(0x3f, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPelcoDCommand(command1, command2, data1 = 0x00, data2 = 0x00) {
|
||||||
|
const address = CONTROL_ADDRESS
|
||||||
|
const checksum = (address + command1 + command2 + data1 + data2) & 0xff
|
||||||
|
return Buffer.from([
|
||||||
|
0xff,
|
||||||
|
address,
|
||||||
|
command1,
|
||||||
|
command2,
|
||||||
|
data1,
|
||||||
|
data2,
|
||||||
|
checksum,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPresetCommand(operation, presetNo) {
|
||||||
|
const preset = parseByte(presetNo, 0x00)
|
||||||
|
|
||||||
|
if (preset < 0x01 || preset > 0xff) {
|
||||||
|
throw new Error(`Invalid preset number: ${presetNo}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const command2ByOperation = {
|
||||||
|
set: 0x03,
|
||||||
|
clear: 0x05,
|
||||||
|
call: 0x07,
|
||||||
|
}
|
||||||
|
const command2 = command2ByOperation[operation]
|
||||||
|
|
||||||
|
if (command2 === undefined) {
|
||||||
|
throw new Error(`Invalid preset operation: ${operation}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return createPelcoDCommand(0x00, command2, 0x00, preset)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CONTROL_COMMANDS = Object.freeze({
|
||||||
|
PTZ_UP: 'PTZ_UP',
|
||||||
|
PTZ_DOWN: 'PTZ_DOWN',
|
||||||
|
PTZ_LEFT: 'PTZ_LEFT',
|
||||||
|
PTZ_RIGHT: 'PTZ_RIGHT',
|
||||||
|
PTZ_STOP: 'PTZ_STOP',
|
||||||
|
ZOOM_TELE: 'ZOOM_TELE',
|
||||||
|
ZOOM_WIDE: 'ZOOM_WIDE',
|
||||||
|
FOCUS_FAR: 'FOCUS_FAR',
|
||||||
|
FOCUS_NEAR: 'FOCUS_NEAR',
|
||||||
|
IRIS_OPEN: 'IRIS_OPEN',
|
||||||
|
IRIS_CLOSE: 'IRIS_CLOSE',
|
||||||
|
AUX_2_ON: 'AUX_2_ON',
|
||||||
|
AUX_2_OFF: 'AUX_2_OFF',
|
||||||
|
PRESET_1_CALL: 'PRESET_1_CALL',
|
||||||
|
LASER_ON: 'LASER_ON',
|
||||||
|
LASER_AUTO: 'LASER_AUTO',
|
||||||
|
LASER_OFF: 'LASER_OFF',
|
||||||
|
AUTO_FOCUS_ON: 'AUTO_FOCUS_ON',
|
||||||
|
AUTO_FOCUS_OFF: 'AUTO_FOCUS_OFF',
|
||||||
|
DEFOG_ON: 'DEFOG_ON',
|
||||||
|
DEFOG_OFF: 'DEFOG_OFF',
|
||||||
|
DEFROST_ON: 'DEFROST_ON',
|
||||||
|
DEFROST_OFF: 'DEFROST_OFF',
|
||||||
|
REBOOT: 'REBOOT',
|
||||||
|
})
|
||||||
|
|
||||||
|
export function buildControlCommand(action) {
|
||||||
|
const speed = clampPelcoSpeed(CONTROL_SPEED)
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case CONTROL_COMMANDS.PTZ_UP:
|
||||||
|
return createPelcoDCommand(0x00, 0x08, 0x00, speed)
|
||||||
|
case CONTROL_COMMANDS.PTZ_DOWN:
|
||||||
|
return createPelcoDCommand(0x00, 0x10, 0x00, speed)
|
||||||
|
case CONTROL_COMMANDS.PTZ_LEFT:
|
||||||
|
return createPelcoDCommand(0x00, 0x04, speed, 0x00)
|
||||||
|
case CONTROL_COMMANDS.PTZ_RIGHT:
|
||||||
|
return createPelcoDCommand(0x00, 0x02, speed, 0x00)
|
||||||
|
case CONTROL_COMMANDS.PTZ_STOP:
|
||||||
|
return createPelcoDCommand(0x00, 0x00, 0x00, 0x00)
|
||||||
|
case CONTROL_COMMANDS.ZOOM_TELE:
|
||||||
|
return createPelcoDCommand(0x00, 0x20, 0x00, 0x00)
|
||||||
|
case CONTROL_COMMANDS.ZOOM_WIDE:
|
||||||
|
return createPelcoDCommand(0x00, 0x40, 0x00, 0x00)
|
||||||
|
case CONTROL_COMMANDS.FOCUS_FAR:
|
||||||
|
return createPelcoDCommand(0x00, 0x80, 0x00, 0x00)
|
||||||
|
case CONTROL_COMMANDS.FOCUS_NEAR:
|
||||||
|
return createPelcoDCommand(0x01, 0x00, 0x00, 0x00)
|
||||||
|
case CONTROL_COMMANDS.IRIS_OPEN:
|
||||||
|
return createPelcoDCommand(0x02, 0x00, 0x00, 0x00)
|
||||||
|
case CONTROL_COMMANDS.IRIS_CLOSE:
|
||||||
|
return createPelcoDCommand(0x04, 0x00, 0x00, 0x00)
|
||||||
|
case CONTROL_COMMANDS.AUX_2_ON:
|
||||||
|
return createPelcoDCommand(0x00, 0x09, 0x00, 0x02)
|
||||||
|
case CONTROL_COMMANDS.AUX_2_OFF:
|
||||||
|
return createPelcoDCommand(0x00, 0x0b, 0x00, 0x02)
|
||||||
|
case CONTROL_COMMANDS.PRESET_1_CALL:
|
||||||
|
return createPresetCommand('call', 1)
|
||||||
|
case CONTROL_COMMANDS.LASER_ON:
|
||||||
|
return createPresetCommand('call', 224)
|
||||||
|
case CONTROL_COMMANDS.LASER_AUTO:
|
||||||
|
return createPresetCommand('call', 225)
|
||||||
|
case CONTROL_COMMANDS.LASER_OFF:
|
||||||
|
return createPresetCommand('call', 226)
|
||||||
|
case CONTROL_COMMANDS.AUTO_FOCUS_ON:
|
||||||
|
return createPresetCommand('call', 135)
|
||||||
|
case CONTROL_COMMANDS.AUTO_FOCUS_OFF:
|
||||||
|
return createPresetCommand('call', 136)
|
||||||
|
case CONTROL_COMMANDS.DEFOG_ON:
|
||||||
|
return createPresetCommand('call', 252)
|
||||||
|
case CONTROL_COMMANDS.DEFOG_OFF:
|
||||||
|
return createPresetCommand('call', 253)
|
||||||
|
case CONTROL_COMMANDS.DEFROST_ON:
|
||||||
|
return createPresetCommand('call', 250)
|
||||||
|
case CONTROL_COMMANDS.DEFROST_OFF:
|
||||||
|
return createPresetCommand('call', 251)
|
||||||
|
case CONTROL_COMMANDS.REBOOT:
|
||||||
|
return createPresetCommand('call', 220)
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported control action: ${action}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeTcp(command) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!CONTROL_TCP_HOST || !CONTROL_TCP_PORT) {
|
||||||
|
reject(new Error('Missing CONTROL_TCP_HOST or CONTROL_TCP_PORT'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const socket = net.createConnection({
|
||||||
|
host: CONTROL_TCP_HOST,
|
||||||
|
port: CONTROL_TCP_PORT,
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.setTimeout(3000)
|
||||||
|
socket.on('connect', () => {
|
||||||
|
socket.end(command)
|
||||||
|
})
|
||||||
|
socket.on('close', resolve)
|
||||||
|
socket.on('timeout', () => {
|
||||||
|
socket.destroy(new Error('Control TCP connection timed out'))
|
||||||
|
})
|
||||||
|
socket.on('error', reject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeUdp(command) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!CONTROL_UDP_HOST || !CONTROL_UDP_PORT) {
|
||||||
|
reject(new Error('Missing CONTROL_UDP_HOST or CONTROL_UDP_PORT'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const socket = dgram.createSocket('udp4')
|
||||||
|
socket.send(command, CONTROL_UDP_PORT, CONTROL_UDP_HOST, (error) => {
|
||||||
|
socket.close()
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendControlAction(action) {
|
||||||
|
const command = buildControlCommand(action)
|
||||||
|
const hex = command.toString('hex').match(/../g).join(' ').toUpperCase()
|
||||||
|
|
||||||
|
if (CONTROL_TRANSPORT === 'tcp') {
|
||||||
|
await writeTcp(command)
|
||||||
|
} else if (CONTROL_TRANSPORT === 'udp') {
|
||||||
|
await writeUdp(command)
|
||||||
|
} else {
|
||||||
|
console.log(`[control] dry-run ${action}: ${hex}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
action,
|
||||||
|
hex,
|
||||||
|
transport: CONTROL_TRANSPORT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getControlStatus() {
|
||||||
|
return {
|
||||||
|
host: CONTROL_HOST,
|
||||||
|
port: CONTROL_PORT,
|
||||||
|
transport: CONTROL_TRANSPORT,
|
||||||
|
address: CONTROL_ADDRESS,
|
||||||
|
speed: clampPelcoSpeed(CONTROL_SPEED),
|
||||||
|
tcpConfigured: Boolean(CONTROL_TCP_HOST && CONTROL_TCP_PORT),
|
||||||
|
udpConfigured: Boolean(CONTROL_UDP_HOST && CONTROL_UDP_PORT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeJson(response, statusCode, payload) {
|
||||||
|
response.writeHead(statusCode, {
|
||||||
|
'access-control-allow-origin': '*',
|
||||||
|
'access-control-allow-methods': 'GET,POST,OPTIONS',
|
||||||
|
'access-control-allow-headers': 'content-type',
|
||||||
|
'content-type': 'application/json; charset=utf-8',
|
||||||
|
})
|
||||||
|
response.end(JSON.stringify(payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
function readRequestJson(request) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let body = ''
|
||||||
|
|
||||||
|
request.setEncoding('utf8')
|
||||||
|
request.on('data', (chunk) => {
|
||||||
|
body += chunk
|
||||||
|
|
||||||
|
if (body.length > 1024 * 1024) {
|
||||||
|
request.destroy(new Error('Request body is too large'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
request.on('end', () => {
|
||||||
|
if (!body.trim()) {
|
||||||
|
resolve({})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(body))
|
||||||
|
} catch {
|
||||||
|
reject(new Error('Invalid JSON body'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
request.on('error', reject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleControlRequest(request, response) {
|
||||||
|
try {
|
||||||
|
const payload = await readRequestJson(request)
|
||||||
|
const action = payload.action
|
||||||
|
|
||||||
|
if (!action || typeof action !== 'string') {
|
||||||
|
writeJson(response, 400, {
|
||||||
|
ok: false,
|
||||||
|
message: 'Missing action',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await sendControlAction(action)
|
||||||
|
writeJson(response, 200, {
|
||||||
|
ok: true,
|
||||||
|
...result,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
writeJson(response, 400, {
|
||||||
|
ok: false,
|
||||||
|
message: error.message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startControlServer() {
|
||||||
|
const server = http.createServer((request, response) => {
|
||||||
|
if (request.method === 'OPTIONS') {
|
||||||
|
writeJson(response, 204, {})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.method === 'GET' && request.url === '/health') {
|
||||||
|
writeJson(response, 200, {
|
||||||
|
ok: true,
|
||||||
|
control: getControlStatus(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.method === 'GET' && request.url === '/commands') {
|
||||||
|
writeJson(response, 200, {
|
||||||
|
ok: true,
|
||||||
|
commands: Object.values(CONTROL_COMMANDS),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.method === 'POST' && request.url === '/control') {
|
||||||
|
handleControlRequest(request, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeJson(response, 404, {
|
||||||
|
ok: false,
|
||||||
|
message: 'Not Found',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
server.on('error', (error) => {
|
||||||
|
console.error(`[control] failed to listen on ${CONTROL_HOST}:${CONTROL_PORT}: ${error.message}`)
|
||||||
|
process.exitCode = 1
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(CONTROL_PORT, CONTROL_HOST, () => {
|
||||||
|
console.log(`[control] http://${CONTROL_HOST}:${CONTROL_PORT}`)
|
||||||
|
console.log(`[control] transport=${CONTROL_TRANSPORT}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentFile = fileURLToPath(import.meta.url)
|
||||||
|
|
||||||
|
if (process.argv[1] === currentFile) {
|
||||||
|
startControlServer()
|
||||||
|
}
|
||||||
@ -0,0 +1,109 @@
|
|||||||
|
import request from '../utils/request'
|
||||||
|
|
||||||
|
const DEFAULT_SPEED = 50
|
||||||
|
|
||||||
|
export function stopPt() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/Stop',
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveUp(speed = DEFAULT_SPEED) {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/MoveUp',
|
||||||
|
method: 'post',
|
||||||
|
params: { speed },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveDown(speed = DEFAULT_SPEED) {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/MoveDown',
|
||||||
|
method: 'post',
|
||||||
|
params: { speed },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveLeft(speed = DEFAULT_SPEED) {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/MoveLeft',
|
||||||
|
method: 'post',
|
||||||
|
params: { speed },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveRight(speed = DEFAULT_SPEED) {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/MoveRight',
|
||||||
|
method: 'post',
|
||||||
|
params: { speed },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function zoomTele() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/ZoomTele',
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function zoomWide() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/ZoomWide',
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callPreset1() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/CallPreset',
|
||||||
|
method: 'post',
|
||||||
|
params: { presetNo: 1 },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function laserOn() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/LaserOn',
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function laserAuto() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/LaserAuto',
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function laserOff() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/LaserOff',
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function autoFocusOn() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/CallPreset',
|
||||||
|
method: 'post',
|
||||||
|
params: { presetNo: 135 },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defogOn() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/CallPreset',
|
||||||
|
method: 'post',
|
||||||
|
params: { presetNo: 252 },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defrostOn() {
|
||||||
|
return request({
|
||||||
|
url: '/api/Pt/CallPreset',
|
||||||
|
method: 'post',
|
||||||
|
params: { presetNo: 250 },
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: import.meta.env.VITE_APP_BASE_API || '/dev-api',
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
|
||||||
|
service.interceptors.request.use(
|
||||||
|
(config) => config,
|
||||||
|
(error) => Promise.reject(error)
|
||||||
|
)
|
||||||
|
|
||||||
|
service.interceptors.response.use(
|
||||||
|
(response) => response.data,
|
||||||
|
(error) => Promise.reject(error)
|
||||||
|
)
|
||||||
|
|
||||||
|
export function request(config) {
|
||||||
|
return service(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default request
|
||||||
Loading…
Reference in New Issue