supabase 实现聊天板(Chat Board)

supabase 实现聊天板(Chat Board)

前提条件:windows中部署supabase与测试

1) 初始化数据库(在 Studio 中运行 SQL)

在 Studio -> SQL 编辑器 中执行 dev/data.sql 里新增的聊天表与策略,或复制如下片段:

sql 复制代码
-- Messages 表和策略(已包含在 dev/data.sql)
create table if not exists public.messages (
  id bigserial primary key,
  room text not null default 'public',
  username text not null default '匿名',
  content text not null check (char_length(content) > 0),
  created_at timestamptz not null default now()
);

alter table public.messages enable row level security;

drop policy if exists "Anyone can read messages" on public.messages;
create policy "Anyone can read messages"
  on public.messages for select using (true);

drop policy if exists "Anyone can send messages" on public.messages;
create policy "Anyone can send messages"
  on public.messages for insert with check (true);

drop policy if exists "Admins can update messages" on public.messages;
create policy "Admins can update messages"
  on public.messages for update to service_role using (true);

drop policy if exists "Admins can delete messages" on public.messages;
create policy "Admins can delete messages"
  on public.messages for delete to service_role using (true);

alter publication supabase_realtime add table public.messages;

2) 快速测试(REST + Realtime)

  • 环境变量设置

    bash 复制代码
    $env:ANON_KEY = ""
    $env:KONG_HTTP_PORT = "8000"
  • 拉取最近消息:

    bash 复制代码
    curl.exe -H "apikey: $env:ANON_KEY" -H "Authorization: Bearer $env:ANON_KEY" "http://localhost:$env:KONG_HTTP_PORT/rest/v1/messages?select=*&order=created_at.desc&limit=50"
  • 发送消息:

    bash 复制代码
      curl.exe -X POST `
      -H "Content-Type: application/json" `
      -H "apikey: $env:ANON_KEY" `
      -H "Authorization: Bearer $env:ANON_KEY" `
      -d '{\"room\":\"public\",\"username\":\"张三\",\"content\":\"大家好!\"}' `
      "http://localhost:$($env:KONG_HTTP_PORT)/rest/v1/messages"

3) 最小前端片段(可直接嵌入)

将下列 HTML 保存为 chat.html 并在浏览器中打开(注意将 SUPABASE_URLANON_KEY 替换成你的值):

html 复制代码
<!doctype html>
<meta charset="utf-8" />
<title>Chat Board</title>
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<style>
  body { font-family: sans-serif; max-width: 720px; margin: 24px auto; }
  #messages { border: 1px solid #ddd; padding: 12px; height: 360px; overflow: auto; }
  #form { display: flex; gap: 8px; margin-top: 12px; }
  input, button { padding: 8px; }
  .row { margin: 6px 0; }
  .time { color: #888; font-size: 12px; margin-left: 8px; }
  .user { font-weight: bold; }
  .content { margin-left: 6px; }
  header { display: flex; justify-content: space-between; align-items: center; }
  select { padding: 4px; }
</style>
<header>
  <h1>Chat Board</h1>
  <label>Room:
    <select id="room">
      <option value="public">public</option>
      <option value="random">random</option>
    </select>
  </label>
  <label>User: <input id="username" value="匿名" /></label>
</header>
<div id="messages"></div>
<div id="form">
  <input id="input" placeholder="输入消息..." />
  <button id="send">发送</button>
  <button id="refresh">刷新</button>
  <button id="clear">清屏</button>
  <span id="status"></span>
  </div>
<script>
  const SUPABASE_URL = 'http://localhost:8000';
  const ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE';
  const sb = supabase.createClient(SUPABASE_URL, ANON_KEY);

  const $ = (id) => document.getElementById(id);
  const box = $('messages');
  const roomSel = $('room');
  const usernameInput = $('username');
  const status = $('status');
  let channel;

  const isAscii = (s) => /^[\x00-\x7F]+$/.test(s);
  function validateConfig() {
    if (!SUPABASE_URL || !ANON_KEY) {
      status.textContent = '配置缺失:请设置 SUPABASE_URL 和 ANON_KEY';
      return false;
    }
    if (!isAscii(ANON_KEY) || /替换为/.test(ANON_KEY)) {
      status.textContent = '配置错误:请替换为实际 ANON_KEY(ASCII)';
      alert('请将 ANON_KEY 替换为实际匿名密钥字符串(ASCII),否则请求会失败。');
      return false;
    }
    return true;
  }

  function row({ username, content, created_at }) {
    const div = document.createElement('div');
    div.className = 'row';
    const time = new Date(created_at).toLocaleString();
    div.innerHTML = `<span class="user">${username}</span><span class="content">${content}</span><span class="time">${time}</span>`;
    return div;
  }

  async function list() {
    status.textContent = '加载中...';
    const { data, error } = await sb
      .from('messages')
      .select('*')
      .eq('room', roomSel.value)
      .order('created_at', { descending: false })
      .limit(200);
    status.textContent = error ? '加载失败' : `加载 ${data?.length ?? 0} 条`;
    box.innerHTML = '';
    (data || []).forEach(m => box.appendChild(row(m)));
    box.scrollTop = box.scrollHeight;
  }

  async function send() {
    const content = $('input').value.trim();
    if (!content) return;
    const username = usernameInput.value.trim() || '匿名';
    const { error } = await sb.from('messages').insert({ room: roomSel.value, username, content });
    if (error) alert('发送失败: ' + error.message);
    $('input').value = '';
  }

  function subscribe() {
    if (channel) sb.removeChannel(channel);
    channel = sb.channel(`room:${roomSel.value}`)
      .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages', filter: `room=eq.${roomSel.value}` }, payload => {
        box.appendChild(row(payload.new));
        box.scrollTop = box.scrollHeight;
      })
      .subscribe((status) => console.log('subscribe', status));
  }

  $('send').onclick = send;
  $('refresh').onclick = list;
  $('clear').onclick = () => (box.innerHTML = '');
  roomSel.onchange = () => { list(); subscribe(); };

  (async () => { if (!validateConfig()) return; await list(); subscribe(); })();
  </script>
相关推荐
武子康16 小时前
大数据-236 离线数仓 - 会员指标验证、DataX 导出与广告业务 ODS/DWD/ADS 全流程
大数据·后端·apache hive
武子康2 天前
大数据-235 离线数仓 - 实战:Flume+HDFS+Hive 搭建 ODS/DWD/DWS/ADS 会员分析链路
大数据·后端·apache hive
DianSan_ERP2 天前
电商API接口全链路监控:构建坚不可摧的线上运维防线
大数据·运维·网络·人工智能·git·servlet
够快云库2 天前
能源行业非结构化数据治理实战:从数据沼泽到智能资产
大数据·人工智能·机器学习·企业文件安全
AI周红伟2 天前
周红伟:智能体全栈构建实操:OpenClaw部署+Agent Skills+Seedance+RAG从入门到实战
大数据·人工智能·大模型·智能体
B站计算机毕业设计超人2 天前
计算机毕业设计Django+Vue.js高考推荐系统 高考可视化 大数据毕业设计(源码+LW文档+PPT+详细讲解)
大数据·vue.js·hadoop·django·毕业设计·课程设计·推荐算法
计算机程序猿学长2 天前
大数据毕业设计-基于django的音乐网站数据分析管理系统的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)
大数据·django·课程设计
B站计算机毕业设计超人2 天前
计算机毕业设计Django+Vue.js音乐推荐系统 音乐可视化 大数据毕业设计 (源码+文档+PPT+讲解)
大数据·vue.js·hadoop·python·spark·django·课程设计
十月南城2 天前
数据湖技术对比——Iceberg、Hudi、Delta的表格格式与维护策略
大数据·数据库·数据仓库·hive·hadoop·spark
中烟创新2 天前
灯塔AI智能体获评“2025-2026中国数智科技年度十大创新力产品”
大数据·人工智能·科技