Merge branch 'dev' into 'main'
fix(video): develop See merge request uirepo/labelui!2
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
NEXT_PUBLIC_BASE_PATH=""
|
NEXT_PUBLIC_BASE_PATH=""
|
||||||
|
|
||||||
|
NEXT_PUBLIC_ENV=development
|
||||||
|
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
# NEXT_PUBLIC_REDIS_URL="172.16.112.128"
|
# NEXT_PUBLIC_REDIS_URL="172.16.112.128"
|
||||||
NEXT_PUBLIC_REDIS_URL="192.168.193.237"
|
NEXT_PUBLIC_REDIS_URL="192.168.193.237"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
NEXT_PUBLIC_BASE_PATH=""
|
NEXT_PUBLIC_BASE_PATH=""
|
||||||
|
|
||||||
|
NEXT_PUBLIC_ENV=production
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
NEXT_PUBLIC_REDIS_URL="172.16.105.154"
|
NEXT_PUBLIC_REDIS_URL="172.16.105.154"
|
||||||
NEXT_PUBLIC_REDIS_PORT=8015
|
NEXT_PUBLIC_REDIS_PORT=8015
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
NEXT_PUBLIC_BASE_PATH=""
|
NEXT_PUBLIC_BASE_PATH=""
|
||||||
|
|
||||||
|
NEXT_PUBLIC_ENV=staging
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
NEXT_PUBLIC_REDIS_URL="172.16.112.128"
|
NEXT_PUBLIC_REDIS_URL="172.16.112.128"
|
||||||
NEXT_PUBLIC_REDIS_PORT=9765
|
NEXT_PUBLIC_REDIS_PORT=9765
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
Container,
|
Container,
|
||||||
FileInput,
|
FileInput,
|
||||||
|
Flex,
|
||||||
Group,
|
Group,
|
||||||
Image,
|
Image,
|
||||||
Loader,
|
Loader,
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import { appendCapped } from "./cap"
|
import { appendCapped } from "./cap"
|
||||||
import type { WorkerResponse } from "./ffmpeg.worker"
|
import type { WorkerResponse } from "./ffmpeg.worker"
|
||||||
|
import { getServerImage } from "@/components/label/api/label"
|
||||||
|
|
||||||
export default function VideoFrameExtractor() {
|
export default function VideoFrameExtractor() {
|
||||||
const [loaded, setLoaded] = useState(false)
|
const [loaded, setLoaded] = useState(false)
|
||||||
@@ -155,6 +157,69 @@ export default function VideoFrameExtractor() {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将视频 Base64 转换为 File 对象
|
||||||
|
* @param {string} base64String - 视频 Base64 字符串
|
||||||
|
* @param {string} fileName - 文件名 (例如 'video.mp4')
|
||||||
|
* @returns {File}
|
||||||
|
*/
|
||||||
|
function videoBase64ToFile(base64String: string, fileName: string) {
|
||||||
|
// 1. 提取 MIME 类型和数据
|
||||||
|
const parts = base64String.split(",")
|
||||||
|
const mime = parts[0].match(/:(.*?);/)?.[1] // 可能是 video/mp4, video/webm 等
|
||||||
|
|
||||||
|
// 2. 解码
|
||||||
|
const bstr = atob(parts[1])
|
||||||
|
let n = bstr.length
|
||||||
|
const u8arr = new Uint8Array(n)
|
||||||
|
|
||||||
|
// 3. 填充字节数组
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 返回 File 对象
|
||||||
|
return new File([u8arr], fileName, { type: mime })
|
||||||
|
}
|
||||||
|
|
||||||
|
const getServerVideo = async () => {
|
||||||
|
const params = {
|
||||||
|
data_names: ["6874b86e341d1d0e4c75d0d5.h264"],
|
||||||
|
data_type: 0,
|
||||||
|
project_id: 9,
|
||||||
|
}
|
||||||
|
let text
|
||||||
|
const response = await getServerImage(params)
|
||||||
|
text = `data:video/h264;base64,${response}`
|
||||||
|
|
||||||
|
const fileRes = videoBase64ToFile(text, "6874b86e341d1d0e4c75d0d5.h264")
|
||||||
|
|
||||||
|
// const videoFile = new File([response], "6874b86e341d1d0e4c75d0d5.264", {
|
||||||
|
// type: "video/264",
|
||||||
|
// })
|
||||||
|
setFile(fileRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadFile(file: File | null) {
|
||||||
|
if (!file) return
|
||||||
|
// 创建一个临时的 DOM URL 指向内存中的文件
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
|
||||||
|
// 创建一个隐藏的 a 标签
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.style.display = "none"
|
||||||
|
a.href = url
|
||||||
|
a.download = file.name // 设置下载后的文件名
|
||||||
|
|
||||||
|
// 将标签添加到文档中并触发点击
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
|
||||||
|
// 清理工作:移除标签并释放内存 URL
|
||||||
|
document.body.removeChild(a)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
|
||||||
const processVideo = () => {
|
const processVideo = () => {
|
||||||
if (!file || !loaded || !workerRef.current) return
|
if (!file || !loaded || !workerRef.current) return
|
||||||
|
|
||||||
@@ -215,11 +280,21 @@ export default function VideoFrameExtractor() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Card withBorder shadow="sm" p="lg" radius="md">
|
<Card withBorder shadow="sm" p="lg" radius="md">
|
||||||
|
<Flex gap={"md"}>
|
||||||
|
<Button onClick={getServerVideo}>getServerVideo</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
downloadFile(file)
|
||||||
|
}}>
|
||||||
|
downloadFile
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<FileInput
|
<FileInput
|
||||||
label="Select Video File"
|
label="Select Video File"
|
||||||
placeholder="Click to select MP4, WebM..."
|
placeholder="Click to select MP4, WebM..."
|
||||||
accept="video/*"
|
// accept="video/*"
|
||||||
leftSection={<IconMovie size={16} />}
|
leftSection={<IconMovie size={16} />}
|
||||||
value={file}
|
value={file}
|
||||||
onChange={setFile}
|
onChange={setFile}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ ctx.onmessage = async (event: MessageEvent<WorkerMessage>) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const mountDir = "/input_mount"
|
const mountDir = "/input_mount"
|
||||||
const inputPath = `${mountDir}/${file.name}`
|
let inputPath = `${mountDir}/${file.name}`
|
||||||
|
|
||||||
const mountInput = async () => {
|
const mountInput = async () => {
|
||||||
await ffmpeg.createDir(mountDir)
|
await ffmpeg.createDir(mountDir)
|
||||||
@@ -69,12 +69,64 @@ ctx.onmessage = async (event: MessageEvent<WorkerMessage>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const unmountInput = async () => {
|
const unmountInput = async () => {
|
||||||
await ffmpeg.unmount(mountDir).catch(() => {})
|
await ffmpeg.unmount(mountDir).catch((err) => {
|
||||||
await ffmpeg.deleteDir(mountDir).catch(() => {})
|
console.log("unmounterr", err)
|
||||||
|
})
|
||||||
|
await ffmpeg.deleteDir(mountDir).catch((err) => {
|
||||||
|
console.log("deleteDir", err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await mountInput()
|
await mountInput()
|
||||||
|
|
||||||
|
const isRawH264 =
|
||||||
|
file.name.toLowerCase().endsWith(".264") ||
|
||||||
|
file.name.toLowerCase().endsWith(".h264")
|
||||||
|
|
||||||
|
if (isRawH264) {
|
||||||
|
ctx.postMessage({
|
||||||
|
type: "LOG",
|
||||||
|
message: "[fix] 检测到 H.264 裸流,正在封装为 MP4 以修复索引...",
|
||||||
|
})
|
||||||
|
|
||||||
|
const remuxedPath = "fixed_container.mp4"
|
||||||
|
|
||||||
|
try {
|
||||||
|
// -f h264: 强制指定输入格式为 h264 裸流
|
||||||
|
// -i ... : 输入
|
||||||
|
// -c copy: 直接复制流,不转码(速度极快)
|
||||||
|
// -f mp4 : 输出为 MP4 容器
|
||||||
|
const ret = await ffmpeg.exec([
|
||||||
|
"-f",
|
||||||
|
"h264",
|
||||||
|
"-i",
|
||||||
|
inputPath,
|
||||||
|
"-c",
|
||||||
|
"copy",
|
||||||
|
remuxedPath,
|
||||||
|
])
|
||||||
|
|
||||||
|
if (ret === 0) {
|
||||||
|
ctx.postMessage({
|
||||||
|
type: "LOG",
|
||||||
|
message: "[fix] 封装成功,切换输入源",
|
||||||
|
})
|
||||||
|
// 关键:将后续操作的输入路径指向新生成的 MP4
|
||||||
|
inputPath = remuxedPath
|
||||||
|
} else {
|
||||||
|
ctx.postMessage({
|
||||||
|
type: "LOG",
|
||||||
|
message: "[fix] 封装失败,尝试继续使用原始文件...",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ctx.postMessage({
|
||||||
|
type: "LOG",
|
||||||
|
message: `[fix] 预处理出错: ${String(e)}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const normalizeError = (e: unknown) => {
|
const normalizeError = (e: unknown) => {
|
||||||
if (typeof e === "string") return e
|
if (typeof e === "string") return e
|
||||||
@@ -145,7 +197,9 @@ ctx.onmessage = async (event: MessageEvent<WorkerMessage>) => {
|
|||||||
message: `[probe] ffprobe failed: ${normalizeError(e)}`,
|
message: `[probe] ffprobe failed: ${normalizeError(e)}`,
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
await ffmpeg.deleteFile(durationProbePath).catch(() => {})
|
await ffmpeg.deleteFile(durationProbePath).catch((err) => {
|
||||||
|
console.log("deleteFile error", err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!durationSec) {
|
if (!durationSec) {
|
||||||
@@ -388,7 +442,9 @@ ctx.onmessage = async (event: MessageEvent<WorkerMessage>) => {
|
|||||||
)}`,
|
)}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
await ffmpeg.deleteFile(outputName).catch(() => {})
|
await ffmpeg.deleteFile(outputName).catch((err) => {
|
||||||
|
console.log("deleteFile error", err)
|
||||||
|
})
|
||||||
index += 1
|
index += 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -397,7 +453,9 @@ ctx.onmessage = async (event: MessageEvent<WorkerMessage>) => {
|
|||||||
const raw = (await ffmpeg.readFile(outputName)) as Uint8Array
|
const raw = (await ffmpeg.readFile(outputName)) as Uint8Array
|
||||||
const copy = raw.slice()
|
const copy = raw.slice()
|
||||||
sentBytes += copy.byteLength
|
sentBytes += copy.byteLength
|
||||||
await ffmpeg.deleteFile(outputName).catch(() => {})
|
await ffmpeg.deleteFile(outputName).catch((err) => {
|
||||||
|
console.log("deleteFile error", err)
|
||||||
|
})
|
||||||
|
|
||||||
ctx.postMessage(
|
ctx.postMessage(
|
||||||
{
|
{
|
||||||
@@ -484,13 +542,10 @@ ctx.onmessage = async (event: MessageEvent<WorkerMessage>) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e2) {
|
} catch (e2) {
|
||||||
|
let flag = typeof e2 === "string"
|
||||||
ctx.postMessage({
|
ctx.postMessage({
|
||||||
type: "LOG",
|
type: "LOG",
|
||||||
message: `[fatal] reload failed: ${
|
message: `[fatal] reload failed: ${flag ? e2 : (e2 as any)?.message || String(e2)}`,
|
||||||
typeof e2 === "string"
|
|
||||||
? e2
|
|
||||||
: (e2 as any)?.message || String(e2)
|
|
||||||
}`,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
// src/workers/ffmpeg.worker.ts
|
// app/component/label/video/libav.worker.ts
|
||||||
import type { LibAVWrapper, WorkerMessage, WorkerResponse } from "./types/libav"
|
import type {
|
||||||
|
LibAVWrapper,
|
||||||
|
WorkerMessage,
|
||||||
|
WorkerResponse,
|
||||||
|
} from "@/app/component/label/video/types/libav"
|
||||||
|
|
||||||
const ctx: Worker = self as any
|
const ctx: Worker = self as any
|
||||||
let libav: any = null // Use any to access full API
|
let libav: any = null // Use any to access full API
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { ChangeEvent, useEffect, useRef, useState } from "react"
|
import { ChangeEvent, useEffect, useRef, useState } from "react"
|
||||||
import type { WorkerMessage, WorkerResponse } from "./types/libav"
|
import type {
|
||||||
|
WorkerMessage,
|
||||||
|
WorkerResponse,
|
||||||
|
} from "@/app/component/label/video/types/libav"
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const workerRef = useRef<Worker | null>(null)
|
const workerRef = useRef<Worker | null>(null)
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ export default function ProjectAuditPage() {
|
|||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFilters((s) => ({
|
setFilters((s) => ({
|
||||||
...s,
|
...s,
|
||||||
create_at_start: e.currentTarget.value,
|
create_at_start: e.target.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -510,12 +510,12 @@ export default function ProjectAuditPage() {
|
|||||||
radius="xs"
|
radius="xs"
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.create_at_end}
|
value={filters.create_at_end}
|
||||||
onChange={(e) =>
|
onChange={(e) => {
|
||||||
setFilters((s) => ({
|
setFilters((s) => ({
|
||||||
...s,
|
...s,
|
||||||
create_at_end: e.currentTarget.value,
|
create_at_end: e.target.value,
|
||||||
}))
|
}))
|
||||||
}
|
}}
|
||||||
/>
|
/>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
6
build.sh
6
build.sh
@@ -71,15 +71,15 @@ pnpm run $BUILD_FLAG
|
|||||||
if [[ $? != 0 ]]; then echo -e "${RED}build error${NC}"; exit 100; fi
|
if [[ $? != 0 ]]; then echo -e "${RED}build error${NC}"; exit 100; fi
|
||||||
|
|
||||||
# Docker构建和推送
|
# Docker构建和推送
|
||||||
MAIN_NAME=digitalspace_webside
|
MAIN_NAME=digitalspace_labelui
|
||||||
DOCKER_FILE=$SHELL_FOLDER/Dockerfile
|
DOCKER_FILE=$SHELL_FOLDER/Dockerfile
|
||||||
|
|
||||||
if [[ $OUTPUT_TYPE == export ]]; then
|
if [[ $OUTPUT_TYPE == export ]]; then
|
||||||
DOCKER_FILE=$SHELL_FOLDER/docker/nginx.dockerfile
|
DOCKER_FILE=$SHELL_FOLDER/docker/nginx.dockerfile
|
||||||
MAIN_NAME=digitalspace_webside_static
|
MAIN_NAME=digitalspace_labelui_static
|
||||||
elif [[ $OUTPUT_TYPE == standalone ]]; then
|
elif [[ $OUTPUT_TYPE == standalone ]]; then
|
||||||
DOCKER_FILE=$SHELL_FOLDER/docker/node.dockerfile
|
DOCKER_FILE=$SHELL_FOLDER/docker/node.dockerfile
|
||||||
MAIN_NAME=digitalspace_webside_ssr
|
MAIN_NAME=digitalspace_labelui_ssr
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}Building docker image...${NC}"
|
echo -e "${GREEN}Building docker image...${NC}"
|
||||||
|
|||||||
4
components/label/api/const.ts
Normal file
4
components/label/api/const.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const BASE_LABEL_API =
|
||||||
|
process.env.NEXT_PUBLIC_ENV === "production"
|
||||||
|
? "https://label.cowarobot.com"
|
||||||
|
: "http://172.16.112.8:9110"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import httpFetch from "@/api/fetch"
|
import httpFetch from "@/api/fetch"
|
||||||
import { usePermissionStore } from "../../store/auth"
|
import { usePermissionStore } from "../../store/auth"
|
||||||
import { BASE_LABEL_API } from "../label"
|
|
||||||
import { Daily } from "./typing"
|
import { Daily } from "./typing"
|
||||||
|
import { BASE_LABEL_API } from "../const"
|
||||||
|
|
||||||
export const getDailyWorkList = (data: Daily.Request) => {
|
export const getDailyWorkList = (data: Daily.Request) => {
|
||||||
return httpFetch<Daily.Response>({
|
return httpFetch<Daily.Response>({
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
RequestLabelResult,
|
RequestLabelResult,
|
||||||
ServerResponse,
|
ServerResponse,
|
||||||
} from "./typing"
|
} from "./typing"
|
||||||
|
import { BASE_LABEL_API } from "../const"
|
||||||
export const BASE_LABEL_API = "https://label.cowarobot.com"
|
|
||||||
|
|
||||||
// 获取标注结果
|
// 获取标注结果
|
||||||
export const getLabelResult = (task_id: number) => {
|
export const getLabelResult = (task_id: number) => {
|
||||||
@@ -58,7 +57,9 @@ export const getServerImage = async (data: {
|
|||||||
} else {
|
} else {
|
||||||
if (res.data_list && res.data_list.length) {
|
if (res.data_list && res.data_list.length) {
|
||||||
const { image_data } = res.data_list[0]
|
const { image_data } = res.data_list[0]
|
||||||
resolve({ image_data })
|
resolve({
|
||||||
|
image_data,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import httpFetch from "@/api/fetch"
|
import httpFetch from "@/api/fetch"
|
||||||
import { usePermissionStore } from "../../store/auth"
|
import { usePermissionStore } from "../../store/auth"
|
||||||
import { BASE_LABEL_API } from "../label"
|
|
||||||
import { Project, ProjectDetail, ReviewUsers } from "./typing"
|
import { Project, ProjectDetail, ReviewUsers } from "./typing"
|
||||||
|
import { BASE_LABEL_API } from "../const"
|
||||||
|
|
||||||
// 获取项目列表
|
// 获取项目列表
|
||||||
export const getProjectList = (data: Project.ListRequest) => {
|
export const getProjectList = (data: Project.ListRequest) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import httpFetch from "@/api/fetch"
|
import httpFetch from "@/api/fetch"
|
||||||
import { usePermissionStore } from "../../store/auth"
|
import { usePermissionStore } from "../../store/auth"
|
||||||
import { BASE_LABEL_API } from "../label"
|
|
||||||
import { Scheme } from "./typing"
|
import { Scheme } from "./typing"
|
||||||
|
import { BASE_LABEL_API } from "../const"
|
||||||
|
|
||||||
// 获取标注方案列表
|
// 获取标注方案列表
|
||||||
export const getLabelSchemeList = (params: Scheme.ListRequest) => {
|
export const getLabelSchemeList = (params: Scheme.ListRequest) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import httpFetch from "@/api/fetch"
|
import httpFetch from "@/api/fetch"
|
||||||
import { usePermissionStore } from "../../store/auth"
|
import { usePermissionStore } from "../../store/auth"
|
||||||
import { BASE_LABEL_API } from "../label"
|
|
||||||
import { Task } from "./typing"
|
import { Task } from "./typing"
|
||||||
|
import { BASE_LABEL_API } from "../const"
|
||||||
|
|
||||||
// 获取任务列表
|
// 获取任务列表
|
||||||
export const getTaskList = (data: Task.ListRequest) => {
|
export const getTaskList = (data: Task.ListRequest) => {
|
||||||
|
|||||||
@@ -2,10 +2,15 @@ import httpFetch from "@/api/fetch"
|
|||||||
import { ResultData } from "@/api/typing"
|
import { ResultData } from "@/api/typing"
|
||||||
import { Login } from "./types"
|
import { Login } from "./types"
|
||||||
|
|
||||||
|
const BASE_PATH =
|
||||||
|
process.env.NEXT_PUBLIC_ENV === "production"
|
||||||
|
? "https://basis.soft.cowarobot.com"
|
||||||
|
: "https://basis.softtest.cowarobot.cn"
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
export const getVerifyCode = (params: { phone: string }) => {
|
export const getVerifyCode = (params: { phone: string }) => {
|
||||||
return httpFetch<ResultData<{}>>({
|
return httpFetch<ResultData<{}>>({
|
||||||
url: `/api/v1/basis/login/get_verify_code`,
|
url: BASE_PATH + `/api/v1/basis/login/get_verify_code`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(params),
|
body: JSON.stringify(params),
|
||||||
})
|
})
|
||||||
@@ -14,7 +19,7 @@ export const getVerifyCode = (params: { phone: string }) => {
|
|||||||
// 账户登录
|
// 账户登录
|
||||||
export const passwdLogin = (params: Login.ReqPasswdLogin) => {
|
export const passwdLogin = (params: Login.ReqPasswdLogin) => {
|
||||||
return httpFetch<ResultData<Login.ResLogin>>({
|
return httpFetch<ResultData<Login.ResLogin>>({
|
||||||
url: `/api/v1/basis/login/passwd`,
|
url: BASE_PATH + `/api/v1/basis/login/passwd`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(params),
|
body: JSON.stringify(params),
|
||||||
isLogin: true,
|
isLogin: true,
|
||||||
@@ -24,7 +29,7 @@ export const passwdLogin = (params: Login.ReqPasswdLogin) => {
|
|||||||
// 验证码登录
|
// 验证码登录
|
||||||
export const verificationLogin = (params: Login.ReqVerificationLogin) => {
|
export const verificationLogin = (params: Login.ReqVerificationLogin) => {
|
||||||
return httpFetch<ResultData<Login.ResLogin>>({
|
return httpFetch<ResultData<Login.ResLogin>>({
|
||||||
url: `/api/v1/basis/login/verification`,
|
url: BASE_PATH + `/api/v1/basis/login/verification`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(params),
|
body: JSON.stringify(params),
|
||||||
isLogin: true,
|
isLogin: true,
|
||||||
@@ -34,7 +39,7 @@ export const verificationLogin = (params: Login.ReqVerificationLogin) => {
|
|||||||
// 飞书登录
|
// 飞书登录
|
||||||
export const fetchFeishuLogin = (params: Login.ReqFeishuLogin) => {
|
export const fetchFeishuLogin = (params: Login.ReqFeishuLogin) => {
|
||||||
return httpFetch<ResultData<Login.ResLogin>>({
|
return httpFetch<ResultData<Login.ResLogin>>({
|
||||||
url: `/api/v1/basis/login/feishu`,
|
url: BASE_PATH + `/api/v1/basis/login/feishu`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(params),
|
body: JSON.stringify(params),
|
||||||
isLogin: true,
|
isLogin: true,
|
||||||
@@ -44,7 +49,7 @@ export const fetchFeishuLogin = (params: Login.ReqFeishuLogin) => {
|
|||||||
// 刷新token
|
// 刷新token
|
||||||
export const refreshKey = (params: { token: string }) => {
|
export const refreshKey = (params: { token: string }) => {
|
||||||
return httpFetch<ResultData<Login.ResRefreshKey>>({
|
return httpFetch<ResultData<Login.ResRefreshKey>>({
|
||||||
url: `/api/v1/basis/key/refresh`,
|
url: BASE_PATH + `/api/v1/basis/key/refresh`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(params),
|
body: JSON.stringify(params),
|
||||||
isRefresh: true,
|
isRefresh: true,
|
||||||
@@ -59,3 +64,11 @@ export const deleteCookie = () => {
|
|||||||
isDeleteCookie: true,
|
isDeleteCookie: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /api/v1/label_server/user/permission
|
||||||
|
export const getUserPermission = () => {
|
||||||
|
return httpFetch<any>({
|
||||||
|
url: `/api/v1/label_server/user/permission`,
|
||||||
|
method: "GET",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useLoginStore } from "@/components/login/store"
|
|||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { APP, PLATFORM, TENANT } from "@/components/login/libs/common"
|
import { APP, PLATFORM, TENANT } from "@/components/login/libs/common"
|
||||||
import { APP_ID, APP_SECRECT } from "./util"
|
import { APP_ID, APP_SECRECT } from "./util"
|
||||||
import { fetchFeishuLogin } from "../api"
|
import { fetchFeishuLogin, getUserPermission } from "../api"
|
||||||
|
|
||||||
const HeaderIcon = DigitalIcon
|
const HeaderIcon = DigitalIcon
|
||||||
|
|
||||||
@@ -60,6 +60,8 @@ const FeishuAutoLogin = ({ useUserStore }: { useUserStore: any }) => {
|
|||||||
access_token: info.access_token || "",
|
access_token: info.access_token || "",
|
||||||
refresh_token: info.refresh_token || "",
|
refresh_token: info.refresh_token || "",
|
||||||
})
|
})
|
||||||
|
const permission = await getUserPermission()
|
||||||
|
console.log(permission)
|
||||||
window.location.href = "/"
|
window.location.href = "/"
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
TENANT,
|
TENANT,
|
||||||
getQueryVariable,
|
getQueryVariable,
|
||||||
} from "@/components/login/libs/common"
|
} from "@/components/login/libs/common"
|
||||||
import { fetchFeishuLogin } from "../api"
|
import { fetchFeishuLogin, getUserPermission } from "../api"
|
||||||
|
|
||||||
const QrLogin = ({ useUserStore }: { useUserStore: any }) => {
|
const QrLogin = ({ useUserStore }: { useUserStore: any }) => {
|
||||||
const fingerprint = useLoginStore.getState().fingerprint
|
const fingerprint = useLoginStore.getState().fingerprint
|
||||||
@@ -57,6 +57,8 @@ const QrLogin = ({ useUserStore }: { useUserStore: any }) => {
|
|||||||
access_token: info.access_token || "",
|
access_token: info.access_token || "",
|
||||||
refresh_token: info.refresh_token || "",
|
refresh_token: info.refresh_token || "",
|
||||||
})
|
})
|
||||||
|
const permission = await getUserPermission()
|
||||||
|
console.log(permission)
|
||||||
window.location.href = "/"
|
window.location.href = "/"
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|||||||
@@ -26,7 +26,12 @@ import {
|
|||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { useDisclosure } from "@mantine/hooks"
|
import { useDisclosure } from "@mantine/hooks"
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import { getVerifyCode, passwdLogin, verificationLogin } from "./api"
|
import {
|
||||||
|
getUserPermission,
|
||||||
|
getVerifyCode,
|
||||||
|
passwdLogin,
|
||||||
|
verificationLogin,
|
||||||
|
} from "./api"
|
||||||
import QrLogin from "./feishu/qr-login"
|
import QrLogin from "./feishu/qr-login"
|
||||||
import { APP, PLATFORM, TENANT } from "./libs/common"
|
import { APP, PLATFORM, TENANT } from "./libs/common"
|
||||||
import { DigitalIcon } from "./resource/icons/digital"
|
import { DigitalIcon } from "./resource/icons/digital"
|
||||||
@@ -204,6 +209,8 @@ export default function LoginPage({ useUserStore }: { useUserStore: any }) {
|
|||||||
access_token: info.access_token || "",
|
access_token: info.access_token || "",
|
||||||
refresh_token: info.refresh_token || "",
|
refresh_token: info.refresh_token || "",
|
||||||
})
|
})
|
||||||
|
const permission = await getUserPermission()
|
||||||
|
console.log(permission)
|
||||||
window.location.href = "/"
|
window.location.href = "/"
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -295,6 +302,8 @@ export default function LoginPage({ useUserStore }: { useUserStore: any }) {
|
|||||||
access_token: info.access_token || "",
|
access_token: info.access_token || "",
|
||||||
refresh_token: info.refresh_token || "",
|
refresh_token: info.refresh_token || "",
|
||||||
})
|
})
|
||||||
|
const permission = await getUserPermission()
|
||||||
|
console.log(permission)
|
||||||
window.location.href = "/"
|
window.location.href = "/"
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : "登录失败,请重试"
|
const message = err instanceof Error ? err.message : "登录失败,请重试"
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ export const getQueryVariable = (variable: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const APP = "basis"
|
export const APP = "basis"
|
||||||
export const PLATFORM = "basis"
|
export const PLATFORM = "标注平台"
|
||||||
export const TENANT = "cowarobot"
|
export const TENANT = "cowarobot"
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ http {
|
|||||||
gzip_comp_level 6;
|
gzip_comp_level 6;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 5521;
|
listen 5531;
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
|
|
||||||
#access_log /var/log/nginx/host.access.log main;
|
#access_log /var/log/nginx/host.access.log main;
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ ADD ./docker/nginx.conf /etc/nginx/nginx.conf
|
|||||||
|
|
||||||
RUN chmod -R 777 /usr/share/nginx/html
|
RUN chmod -R 777 /usr/share/nginx/html
|
||||||
|
|
||||||
ENV PORT 5521
|
ENV PORT 5531
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ COPY ./.next/standalone ./standalone
|
|||||||
COPY ./public /app/standalone/public
|
COPY ./public /app/standalone/public
|
||||||
COPY ./.next/static /app/standalone/.next/static
|
COPY ./.next/static /app/standalone/.next/static
|
||||||
|
|
||||||
ENV PORT 5521
|
ENV PORT 5531
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
EXPOSE 5521
|
EXPOSE 5531
|
||||||
|
|
||||||
CMD ["node", "./standalone/server.js"]
|
CMD ["node", "./standalone/server.js"]
|
||||||
14
package.json
14
package.json
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "webside",
|
"name": "labelui",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone next dev --turbopack -p 5521",
|
"dev": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone next dev --turbopack -p 5531",
|
||||||
"dev:export": "NEXT_PUBLIC_URL=https://basis.softtest.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=export next dev --turbopack -p 5521",
|
"dev:export": "NEXT_PUBLIC_URL=http://172.16.112.8:9110 NEXT_PUBLIC_OUTPUT_TYPE=export next dev --turbopack -p 5531",
|
||||||
"dev:standalone": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone next dev next dev --turbopack -p 5521",
|
"dev:standalone": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone next dev next dev --turbopack -p 5531",
|
||||||
"build:standalone-stage": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone env-cmd -f .env.staging next build --turbopack",
|
"build:standalone-stage": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone env-cmd -f .env.staging next build --turbopack",
|
||||||
"prebuild:export-stage": "node scripts/disable-api-route.mjs",
|
"prebuild:export-stage": "node scripts/disable-api-route.mjs",
|
||||||
"build:export-stage": "NEXT_PUBLIC_URL=https://basis.softtest.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=export env-cmd -f .env.staging next build --turbopack",
|
"build:export-stage": "NEXT_PUBLIC_URL=http://172.16.112.8:9110 NEXT_PUBLIC_OUTPUT_TYPE=export env-cmd -f .env.staging next build --turbopack",
|
||||||
"postbuild:export-stage": "node scripts/restore-api-route.mjs",
|
"postbuild:export-stage": "node scripts/restore-api-route.mjs",
|
||||||
"build:standalone-prod": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone env-cmd -f .env.production next build --turbopack",
|
"build:standalone-prod": "NEXT_PUBLIC_URL=https://frontside2.soft.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=standalone env-cmd -f .env.production next build --turbopack",
|
||||||
"prebuild:export-prod": "node scripts/disable-api-route.mjs",
|
"prebuild:export-prod": "node scripts/disable-api-route.mjs",
|
||||||
"build:export-prod": "NEXT_PUBLIC_URL=https://basis.softtest.cowarobot.com NEXT_PUBLIC_OUTPUT_TYPE=export env-cmd -f .env.production next build --turbopack",
|
"build:export-prod": "NEXT_PUBLIC_URL=http://172.16.112.8:9110 NEXT_PUBLIC_OUTPUT_TYPE=export env-cmd -f .env.production next build --turbopack",
|
||||||
"postbuild:export-prod": "node scripts/restore-api-route.mjs",
|
"postbuild:export-prod": "node scripts/restore-api-route.mjs",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
@@ -96,4 +96,4 @@
|
|||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b"
|
"packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b"
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
"productName": "webside",
|
"productName": "labelui",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"identifier": "com.webside.dev",
|
"identifier": "com.labelui.dev",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../out",
|
"frontendDist": "../out",
|
||||||
"devUrl": "http://localhost:5521",
|
"devUrl": "http://localhost:5531",
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
"beforeBuildCommand": "pnpm build:export-stage"
|
"beforeBuildCommand": "pnpm build:export-stage"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "webside",
|
"title": "labelui",
|
||||||
"width": 800,
|
"width": 800,
|
||||||
"height": 600,
|
"height": 600,
|
||||||
"resizable": true,
|
"resizable": true,
|
||||||
@@ -39,4 +39,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user