Cal.com 推出使用 Next.js 和 Supabase 构建的专家市场

与普遍的看法相反,由于现代史上最大的在线牛肉营销活动,Cal.com 和 Supabase 实际上是非常好的朋友,他们共同的使命是构建开源软件。

因此,当 Cal.com 团队联系我们,希望我们能合作开发他们的新平台入门套件时,我们非常高兴。我们终于可以合作发布 Product Hunt,而不是相互竞争了。

什么是堆栈?

最初,该应用程序是为在 SQLite 上运行而构建的。然而,当需求增长到包括文件存储时,Cal.com 团队想起了他们的宿敌 Supabase,幸运的是,多亏了 Prisma 和 Supabase,在发布前三天将一切都切换到 Postgres 轻而易举。

Prisma 配置用于 Supabase 上的 Postgres #

使用 Prisma 时,您的应用程序将直接连接到 Supabase 上托管的 Postgres 数据库。为了有效地处理连接管理,尤其是在使用 Next.js 等无服务器应用程序时,Supabase 提供了一个名为Supavisor的连接池,以确保您的数据库在流量增加的情况下高效运行。

配置schema.prisma在您提供以下连接字符串的文件中指定:

schema.prisma

datasource db {
  provider  = "postgresql"
  url       = env("POSTGRES_PRISMA_URL")
  directUrl = env("POSTGRES_URL_NON_POOLING")
  schemas   = ["prisma"] // see multi-schema support below
}

.env这将从你的文件中加载相关的 Supabase 连接字符串

.env

POSTGRES_PRISMA_URL="postgres://postgres.YOUR-PROJECT-REF:[YOUR-PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres?pgbouncer=true&connection_limit=1" # Transaction Mode
POSTGRES_URL_NON_POOLING="postgres://postgres.YOUR-PROJECT-REF:[YOUR-PASSWORD]@aws-0-[REGION].pooler.supabase.com:5432/postgres"  # Session Mode

您可以在Supabase 仪表板的数据库设置中找到这些值。

有关将 Prisma 与 Supabase 结合使用的更多详细信息,请阅读官方文档

Prisma中的多模式支持

在 Supabase 中,模式通过自动生成的PostgRESTpublic API公开,这允许您使用Supabase 客户端库(例如supabase-js)从任何使用 HTTPS 的环境连接到您的数据库。

由于 Prisma 直接连接到您的数据库,因此建议将您的数据放在未通过 API 公开的单独模式上。

multischema我们可以通过在文件中启用支持来实现这一点schema.prisma

schema.prisma

generator client {
  provider = "prisma-client-js"
  previewFeatures = ["multiSchema"]
}

model Account {
  id                String  @id @default(cuid())
  // ...

  @@schema("prisma")
}

使用 React Dropzone 和 Supabase Storage 上传个人资料图片

Supabase Storage是一个与 S3 兼容的基于云的对象存储,可让您安全地存储文件。它与Supabase Auth方便地集成,让您可以轻松限制上传和下载的访问权限。

Cal.com 的平台入门套件在 Next.js 的Auth.js上运行其身份验证。幸运的是,Supabase Storage 非常灵活,允许您轻松在服务器端创建签名的上传 URL,然后从客户端上传资产 - 无论您选择使用哪种技术来处理应用程序的身份验证。

为了实现这一点,我们可以在 Next.js 中创建一个 API 路由来生成这些签名的 URL:

src/app/api/supabase/storage/route.ts

import { auth } from '@/auth'
import { env } from '@/env'
import { createClient } from '@supabase/supabase-js'

export const dynamic = 'force-dynamic' // defaults to auto
export async function GET(request: Request) {
  try {
    const session = await auth()
    if (!session || !session.user.id) {
      return new Response('Unauthorized', { status: 401 })
    }
    const {
      user: { id },
    } = session
    // Generate signed upload url to use on client.
    const supabaseAdmin = createClient(env.NEXT_PUBLIC_SUPABASE_URL, env.SUPABASE_SERVICE_ROLE_KEY)

    const { data, error } = await supabaseAdmin.storage
      .from('avatars')
      .createSignedUploadUrl(id, { upsert: true })
    console.log(error)
    if (error) throw error

    return new Response(JSON.stringify(data), {
      status: 200,
    })
  } catch (e) {
    console.error(e)
    return new Response('Internal Server Error', { status: 500 })
  }
}

createSignedUploadUrl方法返回一个token,然后我们可以在客户端使用它来上传React Dropzone选择的文件:

src/app/dashboard/settings/_components/ supabase-react-dropzone.tsx

'use client'

import { env } from '@/env'
import { createClient } from '@supabase/supabase-js'
import Image from 'next/image'
import React, { useState } from 'react'
import { useDropzone } from 'react-dropzone'

export default function SupabaseReactDropzone({ userId }: { userId?: string } = {}) {
  const supabaseBrowserClient = createClient(
    env.NEXT_PUBLIC_SUPABASE_URL,
    env.NEXT_PUBLIC_SUPABASE_ANON_KEY
  )
  const { acceptedFiles, fileRejections, getRootProps, getInputProps } = useDropzone({
    maxFiles: 1,
    accept: {
      'image/jpeg': [],
      'image/png': [],
    },
    onDropAccepted: async (acceptedFiles) => {
      setAvatar(null)
      console.log(acceptedFiles)
      const { path, token }: { path: string; token: string } = await fetch(
        '/api/supabase/storage'
      ).then((res) => res.json())

      const { data, error } = await supabaseBrowserClient.storage
        .from('avatars')
        .uploadToSignedUrl(path, token, acceptedFiles[0])
    },
  })

  return (
    <div className="mx-auto mt-4 grid w-full gap-2">
      <div {...getRootProps({ className: 'dropzone' })}>
        <input {...getInputProps()} />
        <p>Drag 'n' drop some files here, or click to select files</p>
        <em>(Only *.jpeg and *.png images will be accepted)</em>
      </div>
    </div>
  )
}

用于 Supabase Storage的自定义 Next.js 图像加载器

Supabase Storage 还可以通过创建自定义加载器方便地与 Next.js 图像范例集成:

src/lib/ supabase-image-loader.ts

import { env } from '@/env'

export default function supabaseLoader({ src, width, quality }) {
  return `${env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/${src}?width=${width}&quality=${quality || 75}`
}

现在我们只需要在next.config.js文件中注册自定义加载器:

next.config.js

images: {
  loader: "custom",
  loaderFile: "./src/lib/supabase-image-loader.ts",
},

我们只需在 Supabase Storage 中提供文件路径即可开始使用 Next.js 图像组件:

<Image
  alt="Expert image"
  className="aspect-square rounded-md object-cover"
  src="your-bucket-name/image.png"
  height="64"
  width="64"
/>
 

Supabase Vercel Integration 可实现一键部署

Supabase 还提供了Vercel 集成,使跨分支管理环境变量和部署预览变得轻而易举。当您将 Supabase 项目连接到 Vercel 项目时,集成将使您的环境变量保持同步。

当使用Vercel 部署按钮时,集成将自动为您创建一个新的 Supabase 项目,填充环境变量,甚至运行数据库迁移和种子脚本,这意味着您可以立即启动并运行完整的端到端应用程序!

为开源做贡献

Cal.com 和 Supabase 都致力于创建开源软件,因此这个新的平台入门套件当然也是开源的,让您在几分钟内就能启动自己的市场并方便地安排时间!当然,这也意味着非常欢迎您为入门套件贡献其他功能!您可以在 GitHub 上找到存储库

资源

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值