better auth client on cloudflare
·
转载于:https://mp.weixin.qq.com/s/XCBSh5h92MOWUgz2vLApzg
better-auth 官方Demo
- 官方登录Demo页:https://demo.better-auth.com/
- 官方example: https://github.com/better-auth/examples
- 使用nuxt-example作为例子
运行命令
pnpm install
pnpm dev
可能遇到的错误
better-sqlite3
如果better-sqlite3有问题,导致demo无法运行,考虑如下方法:升级为最新版或者从源代码编译
pnpm install better-sqlite3@latest
pnpm add better-sqlite3 --config.build-from-source=true
cn全局导入
export default defineNuxtConfig({
imports: {
presets: [
{
// 目标文件路径(支持别名)
from: '@/lib/utils',
// 需要全局自动导入的方法名
imports: ['cn'],
},
],
},
})
Button
- 错误描述
ERROR [request error] [unhandled] [GET] http://localhost:3000/ 13:41:24
ℹ Error: _ctx.buttonVariants is not a function
⁃ at _sfc_ssrRender (components/ui/button/Button.vue:39:106)
16 ┃
17 ┃ <template>
18 ┃ <Primitive
19 ┃ :as="as"
20 ┃ :as-child="asChild"
21 ┃ :class="cn(buttonVariants({ variant, size }), props.class)"
22 ┃ >
23 ┃ <slot />
24 ┃ </Primitive>
25 ┃ </template>
26 ┃
- 解决方法,按照如下方法修改
//将
import { type ButtonVariants } from ".";
//修改为
import {buttonVariants, type ButtonVariants} from ".";
Label
- 错误描述
ERROR [unhandledRejection] Maximum call stack size exceeded 13:50:33
at Reflect.get (<anonymous>)
at ReadonlyReactiveHandler.get (node_modules/.pnpm/@vue+reactivity@3.5.25/node_modules/@vue/reactivity/dist/reactivity.cjs.js:993:25)
at ComputedRefImpl.fn (components/ui/label/Label.vue:29:28)
at refreshComputed (node_modules/.pnpm/@vue+reactivity@3.5.25/node_modules/@vue/reactivity/dist/reactivity.cjs.js:395:28)
at get value (node_modules/.pnpm/@vue+reactivity@3.5.25/node_modules/@vue/reactivity/dist/reactivity.cjs.js:1695:5)
at unref (node_modules/.pnpm/@vue+reactivity@3.5.25/node_modules/@vue/reactivity/dist/reactivity.cjs.js:1522:29)
at Object.get (node_modules/.pnpm/@vue+reactivity@3.5.25/node_modules/@vue/reactivity/dist/reactivity.cjs.js:1528:64)
at _sfc_ssrRender (components/ui/label/Label.vue:41:116)
at renderComponentSubTree (node_modules/.pnpm/@vue+server-renderer@3.5.25_vue@3.5.25_typescript@5.6.3_/node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:737:9)
at renderComponentVNode (node_modules/.pnpm/@vue+server-renderer@3.5.25_vue@3.5.25_typescript@5.6.3_/node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:686:12)
- 解决方法
<script setup lang="ts">
import { type HTMLAttributes, computed } from "vue";
import { type LabelProps } from "radix-vue";
const props = defineProps<LabelProps & { class?: HTMLAttributes["class"] }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<Label v-bind="delegatedProps"
:class="
cn(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
props.class,
)
"
>
<slot />
</Label>
</template>
修改为
<script setup lang="ts">
import { type HTMLAttributes, computed } from "vue";
// 🎯 核心修复:必须把真实的组件实例导进来,并重命名为 RadixLabel 避免套娃
import { Label as RadixLabel, type LabelProps } from "radix-vue";
const props = defineProps<LabelProps & { class?: HTMLAttributes["class"] }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<!-- 🎯 核心修复:把这里的标签从 <Label> 改为 <RadixLabel> -->
<RadixLabel
v-bind="delegatedProps"
:class="
cn(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
props.class,
)
"
>
<slot />
</RadixLabel>
</template>
效果显示
- 登录

- 注册

- 重置密码

vue3 demo
- 参考 https://better-auth.com/docs/concepts/client
- Component从上面官方的nuxt项目中借鉴
创建项目
创建
lixiaobo@lixiaobodeMacBook-Pro test % npm create vite@latest better-auth-cloudflare-ui-vue-demo --template vue-ts
> npx
> create-vite better-auth-cloudflare-ui-vue-demo vue-ts
│
◇ Select a framework:
│ Vue
│
◇ Select a variant:
│ TypeScript
│
◇ Install with npm and start now?
│ No
│
◇ Scaffolding project in /Users/lixiaobo/cloudflare/test/better-auth-cloudflare-ui-vue-demo...
│
└ Done. Now run:
cd better-auth-cloudflare-ui-vue-demo
npm install
npm run dev
添加依赖
# 安装 Tailwind v4 的 Vite 原生插件、核心库与动画增强包
npm install -D @tailwindcss/vite@latest tailwindcss@latest tailwindcss-animate
# 安装 Better-Auth、Shadcn 核心运行依赖与自动导入
npm install better-auth radix-vue clsx tailwind-merge class-variance-authority lucide-vue-next @vueuse/core
npm install -D unplugin-auto-import unplugin-vue-components
npm install -D @types/node
npm install vue-router
tailwindcss配置
在vite.config.ts中配置tailwindcss插件
export default defineConfig({
plugins: [
vue(),
tailwindcss()
],
})
shadcn-vue配置
- 配置
tsconfig.json中的compilerOptions,添加
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
- 配置
vite.config.ts,添加resolve
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
}
}
- 导入tailwind
创建assets/css/tailwind.css文件,并添加语句@import 'tailwindcss';,否则后续init的时候找不到tailwindcss - 初始化
lixiaobo@lixiaobodeMacBook-Pro better-auth-cloudflare-ui-vue-demo % npx shadcn-vue@latest init
✔ Preflight checks.
✔ Verifying framework. Found Vite.
✔ Validating Tailwind CSS config. Found v4.
✔ Validating import alias.
✔ **Which** **component library** **would you like to use?** › Reka UI
✔ **Which** **icon library** **would you like to use?** › Lucide
✔ **Which** **font** **would you like to use?** › Inter
✔ **Which color would you like to use as the** **base color****?** › Zinc
✔ Writing components.json.
✔ Checking registry.
✔ Updating CSS variables in src/assets/css/tailwind.css
added 1 package, and audited 148 packages in 11s
37 packages are looking for funding
run `npm fund` for details
found **0** vulnerabilities
added 1 package, and audited 149 packages in 2s
38 packages are looking for funding
run `npm fund` for details
found **0** vulnerabilities
✔ Installing dependencies.
✔ Created 1 file:
- src/lib/utils.ts 10:02:24
Success! Project initialization completed. 10:02:24
You may now add components.
- 尝试导入组件
lixiaobo@lixiaobodeMacBook-Pro better-auth-cloudflare-ui-vue-demo % npx shadcn-vue@latest add card input label button
✔ Checking registry.
✔ Updating CSS variables in src/assets/css/tailwind.css
added 2 packages, and audited 151 packages in 3s
39 packages are looking for funding
run `npm fund` for details
found **0** vulnerabilities
✔ Installing dependencies.
✔ Created 14 files:
- src/components/ui/card/Card.vue 10:08:31
- src/components/ui/card/CardAction.vue 10:08:31
- src/components/ui/card/CardContent.vue 10:08:31
- src/components/ui/card/CardDescription.vue 10:08:31
- src/components/ui/card/CardFooter.vue 10:08:31
- src/components/ui/card/CardHeader.vue 10:08:31
- src/components/ui/card/CardTitle.vue 10:08:31
- src/components/ui/card/index.ts 10:08:31
- src/components/ui/input/Input.vue 10:08:31
- src/components/ui/input/index.ts 10:08:31
- src/components/ui/label/Label.vue 10:08:31
- src/components/ui/label/index.ts 10:08:31
- src/components/ui/button/Button.vue 10:08:31
- src/components/ui/button/index.ts 10:08:31
- 配置自动导入,无须import
Components({
dirs: [
'src/components/ui',
'src/components'
],
extensions: ['vue'],
deep: true, // 开启深度递归扫描,支持 ui/button/ 这种子目录层级
directoryAsNamespace: false, // 保持组件名为纯净的 <Button>
dts: 'src/components.d.ts', // 自动在 src 下生成组件的 TS 类型声明文件
resolvers: [
RekaUIResolver()
]
})
- utils问题
TS2307: Cannot find module @/lib/utils or its corresponding type declarations.
解决上面的问题的方法就是在tsconfig.json里面配置
"include": [
"src/**/*.vue",
]
因为,默认情况下ts不解析vue文件
配置better-auth
创建src/lib/auth-client.ts
import { createAuthClient } from "better-auth/vue"
export const authClient = createAuthClient({
baseURL: "http://localhost:8787" // The base URL of your auth server
})
export const {
signIn,
signOut,
signUp,
useSession,
requestPasswordReset,
resetPassword,
} = authClient;
创建views
- sign-in.vue
<script setup lang="ts">
// 🌟 彻底去掉了错误的 .js 后缀
import { signIn } from "@/lib/auth-client";
import { ref } from "vue";
const email = ref("");
const password = ref("");
const handleSignIn = async () => {
await signIn.email(
{
email: email.value,
password: password.value,
callbackURL: "/",
},
{
onError(context) {
alert(context.error.message);
},
},
);
};
</script>
<template>
<div class="h-screen flex justify-center items-center bg-background">
<Card class="mx-auto w-full max-w-sm">
<CardHeader> <CardTitle class="text-2xl font-bold"> Login </CardTitle>
<CardDescription> Enter your email below to login to your account </CardDescription>
</CardHeader> <CardContent> <div class="grid gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" v-model="email" required />
</div>
<div class="grid gap-2">
<!-- 🛠️ 终极彻底修复:全面抛弃常规 Flex,改用双列 Grid(两列等宽)并强行将左右元素各自对齐 -->
<!-- 这在 Tailwind v4 中拥有最高级别的布局权重,没有任何组件样式能够拦截它 --> <div class="grid grid-cols-2 w-full items-center">
<Label for="password" class="justify-self-start text-sm font-medium">Password</Label>
<a href="/forget-password" class="justify-self-end text-sm underline text-muted-foreground hover:text-foreground text-right whitespace-nowrap">
Forgot your password?
</a>
</div> <Input id="password" type="password" placeholder="password" v-model="password" required />
</div>
<Button type="submit" class="w-full" @click="handleSignIn">
Login
</Button>
<Button variant="outline" class="w-full" @click="async () => { await signIn.social({ provider: 'google', callbackURL: '/' }) }">
Login with Google
</Button>
</div>
<div class="mt-4 text-center text-sm text-muted-foreground">
Don't have an account?
<a href="/sign-up" class="underline text-foreground">
Sign up
</a>
</div> </CardContent> </Card> </div></template>
- sing-up.vue
<script lang="ts" setup>
import { signUp } from "@/lib/auth-client";
import {ref} from "vue";
import {useRouter} from "vue-router";
const firstName = ref("");
const lastName = ref("");
const email = ref("");
const password = ref("");
const handleSignUp = async () => {
const user = {
firstName: firstName.value,
lastName: lastName.value,
email: email.value,
password: password.value,
};
await signUp.email({
email: user.email,
password: user.password,
name: `${user.firstName} ${user.lastName}`,
callbackURL: "/",
fetchOptions: {
onError(context) {
alert(context.error.message);
},
onSuccess() {
useRouter().push("/dashboard");
},
},
});
};
</script>
<template>
<div class="h-screen flex justify-center items-center">
<Card class="mx-auto max-w-sm">
<CardHeader> <CardTitle class="text-xl">Sign Up</CardTitle>
<CardDescription> Enter your information to create an account
</CardDescription>
</CardHeader> <CardContent> <div class="grid gap-4">
<div class="grid grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="first-name">First name</Label>
<Input id="first-name" placeholder="Max" required v-model="firstName" />
</div> <div class="grid gap-2">
<Label for="last-name">Last name</Label>
<Input id="last-name" placeholder="Robinson" required v-model="lastName" />
</div> </div> <div class="grid gap-2">
<Label for="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" required v-model="email" />
</div> <div class="grid gap-2">
<Label for="password">Password</Label>
<Input id="password" type="password" v-model="password" />
</div> <Button type="button" class="w-full" @click="handleSignUp">Create an account</Button>
</div> <div class="mt-4 text-center text-sm">
Already have an account?
<a href="/sign-in" class="underline"> Sign in </a>
</div> </CardContent> </Card> </div></template>
- forget-password.vue
<script lang="ts" setup>
// 🌟 1. API 全量规范自动挂载(如果你配置了 AutoImport,这里甚至可以不写 import)
import { requestPasswordReset } from "@/lib/auth-client";
import { ref } from "vue";
const email = ref("");
const handleRequestPasswordReset = async () => {
if (!email.value) {
alert("Please enter your email address");
return;
}
await requestPasswordReset(
{
email: email.value,
// 🌟 Better Auth 业务提示:指向你的重置输入密码页面,系统会自动在该路径后追加 ?token=xxx 票据
redirectTo: "/reset-password",
},
{
onSuccess() {
alert("Password reset link sent to your email! (Please check your terminal/email box)");
window.location.href = "/sign-in";
},
onError(context) {
alert(context.error.message);
},
},
);
};
</script>
<template>
<div class="h-screen flex justify-center items-center bg-background">
<!-- 🌟 2. 宽度锁定,对冲任何可能在屏幕缩放时产生的突兀断行 -->
<Card class="mx-auto w-full max-w-sm shadow-md border border-border">
<CardHeader class="pb-4">
<CardTitle class="text-2xl font-bold tracking-tight">Reset Password</CardTitle>
<CardDescription class="text-sm text-muted-foreground">
Enter your email below to reset your password
</CardDescription>
</CardHeader>
<CardContent class="p-6 pt-0">
<!-- 🛠️ 核心修复:全面改用纵向管道 Flex 布局加物理垂直隔离(space-y-4) -->
<!-- 这在 Tailwind v4 下拥有最高级别的静态占位解析权重,绝不产生黏连塌陷 --> <div class="flex flex-col space-y-4 w-full">
<!-- 邮箱输入区域小盒子 -->
<div class="flex flex-col space-y-1.5 w-full">
<Label for="email" class="text-sm font-medium">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" required v-model="email" class="w-full" />
</div>
<!-- 发送请求按钮 -->
<Button type="button" class="w-full mt-2 cursor-pointer" @click="handleRequestPasswordReset">
Send Reset Link
</Button>
<!-- 🛠️ 界面细节升级:完美向下隔离,将返回登录链接优雅地沉在最底部并配上响应式换色 -->
<div class="pt-2 text-center text-sm text-muted-foreground">
<a href="/sign-in" class="underline text-foreground hover:opacity-80 transition-opacity">
Back to Sign In
</a>
</div>
</div> </CardContent> </Card> </div></template>
- reset-password.vue
<script lang="ts" setup>
import { resetPassword } from "@/lib/auth-client.js";
import {ref} from "vue";
const confirmPassword = ref("");
const password = ref("");
const handleResetPassword = async () => {
if (confirmPassword.value !== password.value) {
alert("Please enter same passwords");
return;
}
await resetPassword({
newPassword: password.value,
fetchOptions: {
onSuccess(context) {
window.location.href = "/sign-in";
},
onError(context) {
alert(context.error.message);
},
},
});
};
</script>
<template>
<div class="h-screen flex justify-center items-center">
<Card class="mx-auto max-w-sm">
<CardHeader> <CardTitle class="text-2xl">Reset Password</CardTitle>
<CardDescription>Enter your new password below</CardDescription>
</CardHeader> <CardContent> <div class="grid gap-4">
<div class="grid gap-2">
<Label for="password">New Password</Label>
<Input id="password" type="password" required v-model="password" placeholder="New Password" />
</div> <div class="grid gap-2">
<Label for="password">Confirm Password</Label>
<Input id="password" type="password" required placeholder="Confirm Password"
v-model="confirmPassword" />
</div> <Button type="button" class="w-full" @click="handleResetPassword">Reset</Button>
</div> </CardContent> </Card> </div></template>
- dashboard.vue
<script setup lang="ts">
import { useSession, signOut } from "@/lib/auth-client";
const session = useSession();
</script>
<template>
<div class="min-h-[80vh] flex items-center justify-center overflow-hidden no-visible-scrollbar px-6 md:px-0">
<Card class="w-[350px]">
<CardHeader> <CardTitle> User
</CardTitle>
</CardHeader> <CardContent> <div class="flex gap-2 items-center">
<Avatar> <AvatarImage :src="session?.data?.user.image || ''" alt="User profile" />
<AvatarFallback>{{ session?.data?.user.name[0] }}</AvatarFallback>
</Avatar> <div> <p class="text-sm">
{{ session?.data?.user?.name }}
</p>
<p class="text-xs">
{{ session?.data?.user?.email }}
</p>
</div> </div> </CardContent> <CardFooter> <Button @click="async () => {
await signOut()
// router.push('/')
}" variant="secondary">
Sing Out
</Button>
</CardFooter> </Card> </div></template>
- index.vue
<script setup lang="ts">
import { ref } from "vue";
// 🛠️ 改造 1:在纯 Vue 3 中,必须通过标准的组件引入或全量路由包导入
// 如果你用的是 Vue Router,需要导入 RouterLink
import { RouterLink } from "vue-router";
// 🛠️ 改造 2:纯 Vue 3 没有 Nuxt 的全局自动导入,必须手动显式引入你的 Auth 客户端方法
import { useSession } from "@/lib/auth-client";
// 🛠️ 改造 3:必须手动显式引入你的 UI 组件(例如 Shadcn Vue 或你本地的 Button)
import { Button } from "@/components/ui/button";
const features = [
"Email & Password",
"Organization | Teams",
"Passkeys",
"Multi Factor",
"Password Reset",
"Email Verification",
"Roles & Permissions",
"Rate Limiting",
"Session Management",
];
const session = useSession();
console.log(session);
</script>
<template>
<div class="min-h-[80vh] flex items-center justify-center overflow-hidden no-visible-scrollbar px-6 md:px-0">
<main class="flex flex-col gap-4 row-start-2 items-center justify-center">
<div class="flex flex-col gap-1">
<h3 class="font-bold text-4xl text-black dark:text-white text-center">
Better Auth.
</h3>
<p class="text-center break-words text-sm md:text-base">
Official demo to showcase
<!-- 🛠️ 改造 4:修复了 React 的 className 错误,改回标准 Vue 的 class -->
<a href="https://better-auth.com" target="_blank" class="italic underline">
better-auth
</a>
features and capabilities.
<br />
</p> <div class="flex flex-col gap-3 pt-2 flex-wrap">
<div class="border-y py-2 border-dotted bg-secondary/60 opacity-80">
<div class="text-xs flex items-center gap-2 justify-center text-muted-foreground">
<span class="text-center">
All features on this demo are Implemented with Better Auth without any custom backend code
</span>
</div> </div> <div class="flex gap-2 justify-center flex-wrap">
<div v-for="feature in features" :key="feature">
<span class="border-b pb-1 text-muted-foreground text-xs cursor-pointer hover:text-foreground duration-150 ease-in-out transition-all hover:border-foreground flex items-center gap-1">
{{ feature }}
</span>
</div> </div> </div>
<div class="flex items-center gap-2 mt-2 mx-auto">
<!-- 🛠️ 改造 5:将 Nuxt 专属的 <NuxtLink> 替换为 Vue Router 标准的 <RouterLink> -->
<RouterLink to="/sign-in" v-if="!session.data">
<Button variant="outline" class="rounded-none">
Sign In
</Button>
</RouterLink>
<RouterLink to="/dashboard" v-if="session.data">
<Button variant="outline" class="rounded-none">
Dashboard
</Button>
</RouterLink> </div> </div> </main> </div></template>
创建路由
- 创建env.d.ts,防止路由里面无法识别
@/views等路径
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
- 创建文件src/router/index.ts
import {createRouter, createWebHistory} from 'vue-router'
const routes = [
{
path: "/",
name: "home",
component: ()=> import('@/views/index.vue'),
},
{ path: '/sign-in', name: "sign-in", component: () => import('@/views/sign-in.vue') },
{ path: '/sign-up', name: "sign-up", component: () => import('@/views/sign-up.vue') },
{ path: '/forget-password', name: "forget-password", component: () => import('@/views/forget-password.vue') },
{ path: '/reset-password', name: "reset-password", component: () => import('@/views/reset-password.vue') },
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
页面效果
- index

- signin

- singup

- forget

cors
为了本地测试能够通过,需要添加如下配置
- 在cloudflare worker的auth.ts添加
trustedOrigins: [
"http://localhost:5173"
],
- 在cloudflare worker的index.ts添加
app.use('/api/auth/*', cors({
// 直接将前端发过来的请求源(c.req.header('Origin'))原封不动地动态塞回去。
// 这样管它是 localhost 还是带有 75aa0f36 随机哈希的预览域名,Hono 都会在 OPTIONS 预检和真正 POST 时秒发“特别通行证”!
origin: (origin) => {
return origin; // 100% 动态信任并回传当前前端发起的域名
},
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization'],
exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
maxAge: 600,
credentials: true, // 🌟 必须设为 true,否则 Better Auth 的 Session 无法写入浏览器 Cookie
}))
发布到cloudflare
配置auth-client.ts
export const authClient = createAuthClient({
baseURL: "https://better-auth-cloudflare-demo.shakingwaves.workers.dev/api/auth", // The base URL of your auth server
fetchOptions: {
credentials: "include"
}
})
编译
lixiaobo@lixiaobodeMacBook-Pro better-auth-cloudflare-ui-vue-demo % npm run build
> better-auth-cloudflare-ui-vue-demo@0.0.0 build
> vue-tsc -b && vite build
vite v8.1.4 building client environment for production...
✓ 697 modules transformed.
computing gzip size...
dist/index.html **0.57 kB** │ gzip: 0.35 kB
dist/assets/index-CAWqWHIQ.css **24.54 kB** │ gzip: 5.24 kB
dist/assets/forget-password-kXYPRVc-.js **1.88 kB** │ gzip: 0.95 kB
dist/assets/reset-password-OetIz09M.js **1.92 kB** │ gzip: 0.84 kB
dist/assets/views-1t-piWyo.js **2.20 kB** │ gzip: 1.09 kB
dist/assets/sign-in-BPG8k_4g.js **2.22 kB** │ gzip: 1.01 kB
dist/assets/sign-up-DdEw2C2N.js **2.40 kB** │ gzip: 1.00 kB
dist/assets/CardTitle-CZ0451W0.js **5.89 kB** │ gzip: 2.30 kB
dist/assets/index-B_Sscwc3.js **32.99 kB** │ gzip: 13.18 kB
dist/assets/runtime-core.esm-bundler-3gagyTuT.js **56.55 kB** │ gzip: 22.23 kB
dist/assets/auth-client-DrlXBgJb.js **59.07 kB** │ gzip: 20.54 kB
✓ built in 393ms
发布
wrangler pages deploy dist --project-name=better-auth-cloudflare-ui-vue-demo
▲ [WARNING] **Proxy environment variables detected. We'll use your proxy for fetch requests.**
⛅️ wrangler 4.106.0 (update available 4.110.0)
───────────────────────────────────────────────
✨ Success! Uploaded 9 files (4 already uploaded) (4.76 sec)
✨ Uploading _redirects
🌎 Deploying...
✨ Deployment complete! Take a peek over at https://6a9c138d.better-auth-cloudflare-ui-vue-demo.pages.dev
注册演示

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)