1. 背景

公司的人工智能服务平台(TAP)需要添加新的功能:对数据集创建数据标注任务,标注完成时将文件回传至数据集。

经过一系列调研,最终我们选择了开源的Label U项目,并决定通过iframe接入的方式来使用Label U,然后根据业务需求修改Label U的源码。

2.思路

因为是通过iframe嵌入Label U,所以我们只需要在开发环境部署一个Label U的容器能提供访问ip和端口即可。而我们接入的Label U肯定是直接有登录状态的,这个可以通过直接将token信息放在iframe的url上传入到Label U获取使用或者让Laebl U直接从TAP的localstorage中获取token信息。由于运维对外只暴露一个ip 和端口(即TAP的ip和端口),所以我们团队选择了利用nginx代理转发接入Label U,从而token直接由TAP的localstorage中获取。

难点:iframe嵌入的系统想要获取外部系统localstorage的数据必须要同源,而我们TAP项目和Label U项目是不同的ip,所以我们需要借助nginx进行代理转发保证两个项目都是同源的。

3.实现

配置TAP接入页面的路由

client_labelu为部署配置的变量,最终会在部署时的映射文件被替换为 /client_labelu

路径`${client_labelu}/tasks`只写`${client_labelu}`不行,后面至少加个/ 或者跟路径才能正常打开

const Layout = () => import('@/layout/index.vue');
const IframeLayout = () => import('@/layout/iframe.vue');
const client_labelu = config.client_labelu;

const routes = {
  path: '/data_annotation',
  component: Layout,
  redirect: '/data_annotation/list',
  name: 'AiDevelopmentDataAnnotation',
  meta: {
    title: '数据标注',
    app: 'portal',
  },
  children: [
    {
      path: 'list',
      name: 'AiDevelopmentDataAnnotationList',
      component: IframeLayout,
      meta: {
        title: '数据标注',
        sidebar: false,
        breadcrumb: false,
        activeMenu: '/data_annotation',
        link: `${client_labelu}/tasks`,
      },
    },
  ],
};

export default routes;

iframe组件代码

<script setup name="IframeLayout">
  const { proxy } = getCurrentInstance();
  const route = useRoute();

  const iframe = ref({
    loading: true,
    src: '',
  });

  onMounted(() => {
    iframe.value.src = route.meta.link;
    proxy.$refs.iframeRef.onload = () => {
      iframe.value.loading = false;
    };
  });
</script>

<template>
  <div v-loading.lock="iframe.loading" class="iframe-wrapper m-box">
    <iframe ref="iframeRef" :src="iframe.src" frameborder="0" class="iframe" />
  </div>
</template>

<style lang="scss" scoped>
  .iframe-wrapper,
  .iframe {
    position: absolute;
    z-index: 1;
    width: 100%;
    height: 100%;
    &.iframe {
      height: 100%;
    }
  }
</style>

以下为添加Label U项目的基础路由前缀和静态资源引用前缀,因为是iframe嵌入的Label U,不给静态资源加前缀的话会直接请求到TAP的静态资源,之后nginx也需要添加相应的代理,这时候导致路由无法命中,加载页面404,因为原本的iframe的src路径为/client_labelu/tasks,代理后为http://172.21.6.123:31604/client_labelu/tasks,前端路由多了/client_labelu路径,所以需要为前端项目添加一个/client_labelu路径前缀才能命中路由。

修改Label U的APP.tsx文件,添加basename="/client_labelu”,类似于vue的history路由前缀

return(
  <ConfigProvider Locale={getAntdLocale()} componentSize="middle" theme={{ token: themeToken. token }}>
    <AntApp>
      <StaticAnt />
      <GlobalStyle />
      {/* @ts-ignore */}
      <IntlProvider locale={locale.split('_')[0]} messages={localeConfig[locale]}>
        <QueryProvider>
          <RouterContainer routes={routes} basename="/client_labelu" /> 
        </QueryProvider>
      </IntlProvider>
    </AntApp>
  </ConfigProvider>
)

修改vite打包配置,为静态资源引用加上/client_labelu前缀

export default defineConfig({
  base: '/client_labelu'
})

打包后的html资源引用如下,可以看到静态资源引用都带上了client_labelu前缀

<! DOCTYPE html>
<html Lang="en">

<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="x-frame-options" content="SAMEORIGIN" />
<script src="https://unpkg.com/analyze-wiz@1.2.0-beta.2/dist/analyze-wiz.umd.js"></script>

<title>labelu</title>
<script type="module" crossorigin src="/client_labelu/assets/index-b7f75afb.js"></script>
<link rel="stylesheet" href="/client_labelu/assets/index-e614b01a.css">
</head>

<body>
<noscript>You need to enable JavaScript to run this app .< /noscript>
<div id="root"></div>

</body>
<script type="text/javascript" src="/client_labelu/yaml.js"></script>
<script async src="/client_labelu/frontend_version.js?t=1738907593410"></script>
<script async src="/client_labelu/backend_version.js?t=1738907593411"></script>

</htm1>

修改dockerfile文件,将打包后的dist文件夹放到docker容器的/usr/share/nginx/html路径的client_labelu文件夹下

FROM 172.20.15.4/library/nginx:1.24
MAINTAINER trendytech

ADD /apps/frontend/dist /usr/share/nginx/html/client_labelu

TAP nginx配置

# 代理Label U的前端服务
location /client_labelu {
  proxy_pass http://172.21.6.123:31604/client_labelu;
  add_header Access-Control-Allow-Origin * always;
}
# 效果同上
location /client_labelu {
  proxy_pass http://172.21.6.123:31604;
  add_header Access-Control-Allow-Origin * always;
}

# 将TAP中的Label U调用的后端接口统一转发到网关
location /labels {
  proxy_pass http://minerva-gateway:8080;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
}

Label U nginx配置

location / {
  root /usr/share/nginx/html;
  index /client_labelu/index.html;
  try_files $uri $uri/ /client_labelu/index.html;
  add_header Access-Control-Allow-Origin * always;
}