Prechádzať zdrojové kódy

Merge remote-tracking branch 'upstream/main'

yourname 6 mesiacov pred
rodič
commit
21e196d52e

+ 4 - 0
HISTORY.md

@@ -3,6 +3,10 @@
 迁移管理页面,在正式环境中,需要验证env中配置的密码参数才能打开
 
 2025.05.15 0.1.6
+站内消息支持三种类型,admin发送,mobile订阅
+增加admin, mobile 消息io连接
+增加socketio 路由 支持
+增加socketio server 支持
 修正文件分类后端api路由查询表名为file_categories
 将react版本降为18.3.1
 

+ 70 - 14
client/admin/pages_messages.tsx

@@ -1,15 +1,20 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
 import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import { Button, Table, Space, Modal, Form, Input, Select, message } from 'antd';
+import { io, Socket } from 'socket.io-client';
 import type { TableProps } from 'antd';
 import dayjs from 'dayjs';
 import 'dayjs/locale/zh-cn';
 
 import { MessageAPI , UserAPI } from './api/index.ts';
 import type { UserMessage } from '../share/types.ts';
-import { MessageStatusNameMap , MessageStatus} from '../share/types.ts';
+import { MessageStatusNameMap , MessageStatus, MessageType } from '../share/types.ts';
+import { useAuth } from "./hooks_sys.tsx";
 
-export  const MessagesPage = () => {
+export const MessagesPage = () => {
+  const { token } = useAuth();
+  const [socket, setSocket] = useState<Socket | null>(null);
+  const [isSocketConnected, setIsSocketConnected] = useState(false);
   const queryClient = useQueryClient();
   const [form] = Form.useForm();
   const [isModalVisible, setIsModalVisible] = useState(false);
@@ -59,8 +64,58 @@ export  const MessagesPage = () => {
   });
 
   // 发送消息
+  // 初始化Socket.IO连接
+  useEffect(() => {
+    if (!token) return;
+
+    const newSocket = io('/', {
+      path: '/socket.io',
+      transports: ['websocket'],
+      autoConnect: false,
+      query: {
+        socket_token: token
+      }
+    });
+
+    newSocket.on('connect', () => {
+      setIsSocketConnected(true);
+      message.success('实时消息连接已建立');
+    });
+
+    newSocket.on('disconnect', () => {
+      setIsSocketConnected(false);
+      message.warning('实时消息连接已断开');
+    });
+
+    newSocket.on('error', (err) => {
+      message.error(`实时消息错误: ${err}`);
+    });
+
+    newSocket.connect();
+    setSocket(newSocket);
+
+    return () => {
+      newSocket.disconnect();
+    };
+  }, [token]);
+
   const sendMessageMutation = useMutation({
-    mutationFn: (data: any) => MessageAPI.sendMessage(data),
+    mutationFn: async (data: any) => {
+      // 优先使用Socket.IO发送
+      if (isSocketConnected && socket) {
+        return new Promise((resolve, reject) => {
+          socket.emit('message:send', data, (response: any) => {
+            if (response.error) {
+              reject(new Error(response.error));
+            } else {
+              resolve(response.data);
+            }
+          });
+        });
+      }
+      // 回退到HTTP API
+      return MessageAPI.sendMessage(data);
+    },
     onSuccess: () => {
       queryClient.invalidateQueries({ queryKey: ['messages'] });
       queryClient.invalidateQueries({ queryKey: ['unreadCount'] });
@@ -167,9 +222,9 @@ export  const MessagesPage = () => {
               style={{ width: 120 }}
               allowClear
               options={[
-                { value: 'SYSTEM', label: '系统消息' },
-                { value: 'NOTICE', label: '公告' },
-                { value: 'PERSONAL', label: '个人消息' },
+                { value: MessageType.SYSTEM, label: '系统消息' },
+                { value: MessageType.ANNOUNCE, label: '公告' },
+                { value: MessageType.PRIVATE, label: '个人消息' },
               ]}
             />
           </Form.Item>
@@ -178,8 +233,8 @@ export  const MessagesPage = () => {
               style={{ width: 120 }}
               allowClear
               options={[
-                { value: 'UNREAD', label: '未读' },
-                { value: 'READ', label: '已读' },
+                { value: MessageStatus.UNREAD, label: '未读' },
+                { value: MessageStatus.READ, label: '已读' },
               ]}
             />
           </Form.Item>
@@ -235,9 +290,9 @@ export  const MessagesPage = () => {
           >
             <Select
               options={[
-                { value: 'SYSTEM', label: '系统消息' },
-                { value: 'NOTICE', label: '公告' },
-                { value: 'PERSONAL', label: '个人消息' },
+                { value: MessageType.SYSTEM, label: '系统消息' },
+                { value: MessageType.ANNOUNCE, label: '公告' },
+                { value: MessageType.PRIVATE, label: '个人消息' },
               ]}
             />
           </Form.Item>
@@ -266,10 +321,11 @@ export  const MessagesPage = () => {
           </Form.Item>
 
           <Form.Item>
-            <Button 
-              type="primary" 
+            <Button
+              type="primary"
               htmlType="submit"
               loading={sendMessageMutation.status === 'pending'}
+              icon={isSocketConnected ? <span style={{color:'green'}}>●</span> : <span style={{color:'red'}}>●</span>}
             >
               发送
             </Button>

+ 72 - 2
client/mobile/pages_messages.tsx

@@ -1,16 +1,20 @@
-import React from 'react';
+import React, { useEffect } from 'react';
 import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import dayjs from 'dayjs';
 import 'dayjs/locale/zh-cn';
 import { BellIcon } from '@heroicons/react/24/outline';
 import { MessageStatus } from '../share/types.ts';
-
+import { io, Socket } from 'socket.io-client';
 
 // 添加通知页面组件
 import { MessageAPI } from './api/index.ts';
+import { useAuth } from "./hooks.tsx";
 
 export const NotificationsPage = () => {
+  const { token , user} = useAuth();
   const queryClient = useQueryClient();
+  const [socket, setSocket] = React.useState<Socket | null>(null);
+  const [isSubscribed, setIsSubscribed] = React.useState(false);
 
   // 获取消息列表
   const { data: messages, isLoading } = useQuery({
@@ -18,6 +22,72 @@ export const NotificationsPage = () => {
     queryFn: () => MessageAPI.getMessages(),
   });
 
+  // 初始化Socket.IO连接
+  useEffect(() => {
+    if (!token || !user) return;
+
+    const newSocket = io('/', {
+      path: '/socket.io',
+      transports: ['websocket'],
+      withCredentials: true,
+      query: {
+        socket_token: token
+      }
+    });
+
+    setSocket(newSocket);
+
+    // 订阅消息频道
+    newSocket.on('connect', () => {
+      // 订阅个人频道
+      newSocket.emit('message:subscribe', `user_${user.id}`);
+      // 订阅系统频道
+      newSocket.emit('message:subscribe', 'system');
+      // 订阅公告频道
+      newSocket.emit('message:subscribe', 'announce');
+      setIsSubscribed(true);
+    });
+
+    // 处理实时消息
+    const handleNewMessage = (newMessage: any) => {
+      queryClient.setQueryData(['messages'], (oldData: any) => {
+        if (!oldData) return oldData;
+        return {
+          ...oldData,
+          data: [newMessage, ...oldData.data]
+        };
+      });
+
+      // 更新未读计数
+      queryClient.setQueryData(['unreadCount'], (oldData: any) => {
+        if (!oldData) return oldData;
+        return {
+          ...oldData,
+          count: oldData.count + 1
+        };
+      });
+    };
+
+    // 处理广播消息
+    newSocket.on('message:broadcasted', handleNewMessage);
+    // 处理频道推送消息
+    newSocket.on('message:received', handleNewMessage);
+
+    // 错误处理
+    newSocket.on('error', (error) => {
+      console.error('Socket error:', error);
+    });
+
+    return () => {
+      if (newSocket) {
+        newSocket.emit('message:unsubscribe', `user_${user.id}`);
+        newSocket.emit('message:unsubscribe', 'system');
+        newSocket.emit('message:unsubscribe', 'announce');
+        newSocket.disconnect();
+      }
+    };
+  }, [queryClient, token]);
+
   // 获取未读消息数量
   const { data: unreadCount } = useQuery({
     queryKey: ['unreadCount'],

+ 2 - 0
deno.json

@@ -33,6 +33,8 @@
     "react-hook-form": "https://esm.d8d.fun/react-hook-form@7.55.0?dev&deps=react@18.3.1,react-dom@18.3.1",
     "@heroicons/react/24/outline": "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?dev&deps=react@18.3.1,react-dom@18.3.1",
     "@heroicons/react/24/solid": "https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?dev&deps=react@18.3.1,react-dom@18.3.1",
+    "socket.io": "https://deno.land/x/socket_io@0.2.1/mod.ts",
+    "socket.io-client": "https://esm.d8d.fun/socket.io-client@4.8.1",
     "react-toastify": "https://esm.d8d.fun/react-toastify@11.0.5?dev&deps=react@18.3.1,react-dom@18.3.1",
     "aliyun-rtc-sdk":"https://esm.d8d.fun/aliyun-rtc-sdk@6.14.6?standalone",
     "decimal.js":"https://esm.d8d.fun/decimal.js@10.4.3",

+ 144 - 309
deno.lock

@@ -362,318 +362,153 @@
     "https://esm.d8d.fun/xmlhttprequest-ssl@~2.1.1?target=denonext": "https://esm.d8d.fun/xmlhttprequest-ssl@2.1.2?target=denonext"
   },
   "remote": {
-    "https://cdn.skypack.dev/-/bson@v4.5.4-F38JF4sWndM08oQ5WCDI/dist=es2020,mode=imports/optimized/bson.js": "47f919700b7c3b4a5b3a7843be762ae9406e7cd485311916f198ff5a2da0c751",
-    "https://cdn.skypack.dev/pin/bson@v4.5.4-F38JF4sWndM08oQ5WCDI/mode=imports/optimized/bson.js": "fc61370ac798f037e1e7298602b58065dc26496b2f6119c918402fa0e2d60a1c",
-    "https://deno.land/std@0.104.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
-    "https://deno.land/std@0.104.0/async/deadline.ts": "1d6ac7aeaee22f75eb86e4e105d6161118aad7b41ae2dd14f4cfd3bf97472b93",
-    "https://deno.land/std@0.104.0/async/debounce.ts": "b2f693e4baa16b62793fd618de6c003b63228db50ecfe3bd51fc5f6dc0bc264b",
-    "https://deno.land/std@0.104.0/async/deferred.ts": "ce81070ad3ba3294f3f34c032af884ccde1a20922b648f6eaee54bd8fd951a1e",
-    "https://deno.land/std@0.104.0/async/delay.ts": "9de1d8d07d1927767ab7f82434b883f3d8294fb19cad819691a2ad81a728cf3d",
-    "https://deno.land/std@0.104.0/async/mod.ts": "78425176fabea7bd1046ce3819fd69ce40da85c83e0f174d17e8e224a91f7d10",
-    "https://deno.land/std@0.104.0/async/mux_async_iterator.ts": "62abff3af9ff619e8f2adc96fc70d4ca020fa48a50c23c13f12d02ed2b760dbe",
-    "https://deno.land/std@0.104.0/async/pool.ts": "353ce4f91865da203a097aa6f33de8966340c91b6f4a055611c8c5d534afd12f",
-    "https://deno.land/std@0.104.0/async/tee.ts": "6b8f1322b6dd2396202cfbe9cde9cab158d1e962cfd9197b0a97c6657bee79ce",
-    "https://deno.land/std@0.104.0/bytes/bytes_list.ts": "a13287edb03f19d27ba4927dec6d6de3e5bd46254cd4aee6f7e5815810122673",
-    "https://deno.land/std@0.104.0/bytes/mod.ts": "1ae1ccfe98c4b979f12b015982c7444f81fcb921bea7aa215bf37d84f46e1e13",
-    "https://deno.land/std@0.104.0/encoding/base64.ts": "eecae390f1f1d1cae6f6c6d732ede5276bf4b9cd29b1d281678c054dc5cc009e",
-    "https://deno.land/std@0.104.0/encoding/hex.ts": "5bc7df19af498c315cdaba69e2fce1b2aef5fc57344e8c21c08991aa8505a260",
-    "https://deno.land/std@0.104.0/fmt/colors.ts": "d2f8355f00a74404668fc5a1e4a92983ce1a9b0a6ac1d40efbd681cb8f519586",
-    "https://deno.land/std@0.104.0/fs/exists.ts": "b0d2e31654819cc2a8d37df45d6b14686c0cc1d802e9ff09e902a63e98b85a00",
-    "https://deno.land/std@0.104.0/hash/_wasm/hash.ts": "313a4820227f1c45fa7204d9c28731b4f8ce97cdcc5f1e7e4efcdf2d70540d32",
-    "https://deno.land/std@0.104.0/hash/_wasm/wasm.js": "792f612fbb9998e267f9ae3f82ed72444305cb9c77b5bbf7ff6517fd3b606ed1",
-    "https://deno.land/std@0.104.0/hash/hasher.ts": "57a9ec05dd48a9eceed319ac53463d9873490feea3832d58679df6eec51c176b",
-    "https://deno.land/std@0.104.0/hash/mod.ts": "dd339a26b094032f38d71311b85745e8d19f2085364794c1877057e057902dd9",
-    "https://deno.land/std@0.104.0/io/buffer.ts": "3ead6bb11276ebcf093c403f74f67fd2205a515dbbb9061862c468ca56f37cd8",
-    "https://deno.land/std@0.104.0/io/bufio.ts": "6024117aa37f8d21a116654bd5ca5191d803f6492bbc744e3cee5054d0e900d1",
-    "https://deno.land/std@0.104.0/io/util.ts": "85c33d61b20fd706acc094fe80d4c8ae618b04abcf3a96ca2b47071842c1c8ac",
-    "https://deno.land/std@0.104.0/log/handlers.ts": "8c7221a2408b4097e186b018f3f1a18865d20b98761aa1dccaf1ee3d57298355",
-    "https://deno.land/std@0.104.0/log/levels.ts": "088a883039ece5fa0da5f74bc7688654045ea7cb01bf200b438191a28d728eae",
-    "https://deno.land/std@0.104.0/log/logger.ts": "6b2dd8cbe6f407100b9becfe61595d7681f8ce3692412fad843de84d617a038e",
-    "https://deno.land/std@0.104.0/log/mod.ts": "91711789b28803082b1bdfb123d2c9685a7e01767f2e79c0a82706063ad964d8",
-    "https://deno.land/std@0.104.0/testing/_diff.ts": "5d3693155f561d1a5443ac751ac70aab9f5d67b4819a621d4b96b8a1a1c89620",
-    "https://deno.land/std@0.104.0/testing/asserts.ts": "e4311d45d956459d4423bc267208fe154b5294989da2ed93257b6a85cae0427e",
-    "https://deno.land/std@0.114.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
-    "https://deno.land/std@0.114.0/async/deadline.ts": "1d6ac7aeaee22f75eb86e4e105d6161118aad7b41ae2dd14f4cfd3bf97472b93",
-    "https://deno.land/std@0.114.0/async/debounce.ts": "b2f693e4baa16b62793fd618de6c003b63228db50ecfe3bd51fc5f6dc0bc264b",
-    "https://deno.land/std@0.114.0/async/deferred.ts": "ab60d46ba561abb3b13c0c8085d05797a384b9f182935f051dc67136817acdee",
-    "https://deno.land/std@0.114.0/async/delay.ts": "f2d8ccaa8ebc26594bd8b0989edfd8a96257a714c1dee2fb54d986e5bdd840ac",
-    "https://deno.land/std@0.114.0/async/mod.ts": "78425176fabea7bd1046ce3819fd69ce40da85c83e0f174d17e8e224a91f7d10",
-    "https://deno.land/std@0.114.0/async/mux_async_iterator.ts": "62abff3af9ff619e8f2adc96fc70d4ca020fa48a50c23c13f12d02ed2b760dbe",
-    "https://deno.land/std@0.114.0/async/pool.ts": "353ce4f91865da203a097aa6f33de8966340c91b6f4a055611c8c5d534afd12f",
-    "https://deno.land/std@0.114.0/async/tee.ts": "3e9f2ef6b36e55188de16a667c702ace4ad0cf84e3720379160e062bf27348ad",
-    "https://deno.land/std@0.114.0/bytes/bytes_list.ts": "3bff6a09c72b2e0b1e92e29bd3b135053894196cca07a2bba842901073efe5cb",
-    "https://deno.land/std@0.114.0/bytes/equals.ts": "69f55fdbd45c71f920c1a621e6c0865dc780cd8ae34e0f5e55a9497b70c31c1b",
-    "https://deno.land/std@0.114.0/bytes/mod.ts": "fedb80b8da2e7ad8dd251148e65f92a04c73d6c5a430b7d197dc39588c8dda6f",
-    "https://deno.land/std@0.114.0/datetime/formatter.ts": "bf7befcd2c55c3060be199ebc10e40f9c33aef6141c20f7c781d03beef25a49e",
-    "https://deno.land/std@0.114.0/datetime/mod.ts": "ddbf54ca8144583cdf16f49b5a69c6b4594215d7b14fef8fecc5ff73911da9e3",
-    "https://deno.land/std@0.114.0/datetime/tokenizer.ts": "492bb6251e75e0c03d5a89a66bd2b03e08e9cbc298d51e002cf59378aaa32c48",
-    "https://deno.land/std@0.114.0/encoding/base64.ts": "0b58bd6477214838bf711eef43eac21e47ba9e5c81b2ce185fe25d9ecab3ebb3",
-    "https://deno.land/std@0.114.0/encoding/hex.ts": "5bc7df19af498c315cdaba69e2fce1b2aef5fc57344e8c21c08991aa8505a260",
-    "https://deno.land/std@0.114.0/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621",
-    "https://deno.land/std@0.114.0/hash/_wasm/hash.ts": "021e1d98319edaf4bd57cf114e858f16c483d21c1057ef3bec8e329e61207c80",
-    "https://deno.land/std@0.114.0/hash/_wasm/wasm.js": "7313f3e83e8e7addc6c5504db29dbb5f3e3fd75ab3d0b76b326bd1bea0f36a2e",
-    "https://deno.land/std@0.114.0/hash/hasher.ts": "57a9ec05dd48a9eceed319ac53463d9873490feea3832d58679df6eec51c176b",
-    "https://deno.land/std@0.114.0/hash/mod.ts": "e06f28bf9a212529cfb90f7f67f0840ad3eae1441d29210972e8224002382047",
-    "https://deno.land/std@0.114.0/hash/sha256.ts": "bd85257c68d1fdd9da8457284c4fbb04efa9f4f2229b5f41a638d5b71a3a8d5c",
-    "https://deno.land/std@0.114.0/io/buffer.ts": "a587093277c889ded709a933cd7d5b6285978275b2015a7cf9c8a3caad464236",
-    "https://deno.land/std@0.117.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
-    "https://deno.land/std@0.117.0/_wasm_crypto/crypto.js": "1c565287b35f6eb1aa58499d0f4fbac99a9c30eb9a735c512d193a6493499e84",
-    "https://deno.land/std@0.117.0/_wasm_crypto/crypto.wasm.js": "e93d38b215c05c552669e9565654599b13e7898ebd3f10ac91e39413efda4b84",
-    "https://deno.land/std@0.117.0/_wasm_crypto/mod.ts": "9afe300945fe7e5bcec231b52b0016d8442d026b823f619bb5939b2cf66ff21b",
-    "https://deno.land/std@0.117.0/async/deferred.ts": "ab60d46ba561abb3b13c0c8085d05797a384b9f182935f051dc67136817acdee",
-    "https://deno.land/std@0.117.0/bytes/bytes_list.ts": "3bff6a09c72b2e0b1e92e29bd3b135053894196cca07a2bba842901073efe5cb",
-    "https://deno.land/std@0.117.0/bytes/equals.ts": "69f55fdbd45c71f920c1a621e6c0865dc780cd8ae34e0f5e55a9497b70c31c1b",
-    "https://deno.land/std@0.117.0/bytes/mod.ts": "fedb80b8da2e7ad8dd251148e65f92a04c73d6c5a430b7d197dc39588c8dda6f",
-    "https://deno.land/std@0.117.0/crypto/mod.ts": "8e1ec0ff94a4f08e3c4f72de5b88781566481602614e62291aa7ae7444ba11f0",
-    "https://deno.land/std@0.117.0/encoding/base64.ts": "0b58bd6477214838bf711eef43eac21e47ba9e5c81b2ce185fe25d9ecab3ebb3",
-    "https://deno.land/std@0.117.0/encoding/hex.ts": "5bc7df19af498c315cdaba69e2fce1b2aef5fc57344e8c21c08991aa8505a260",
-    "https://deno.land/std@0.117.0/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621",
-    "https://deno.land/std@0.117.0/io/buffer.ts": "8f10342821b81990acf859cdccb4e4031c7c9187a0ffc3ed6b356ee29ecc6681",
-    "https://deno.land/std@0.117.0/io/files.ts": "8794bf6014e6f3183fc5433926dfef4a50eb8d3ec3b6736ebb4277e5f0ce3420",
-    "https://deno.land/std@0.117.0/io/mod.ts": "15804a7fe4d00c3a8c3ffa281845a2d0341414336d5c10e668d1a1c8ecec4114",
-    "https://deno.land/std@0.117.0/io/readers.ts": "17403919724fef2f343c88555606368868a5c752a1099ad801f6a381c170f62d",
-    "https://deno.land/std@0.117.0/io/streams.ts": "531a435ca1b3586e113151fbbd28f225720f4836f6002650b1ad4b18fb63ac1b",
-    "https://deno.land/std@0.117.0/io/util.ts": "96409103aa87b0eb0ddc494666a3514a6e969a1a3e7f69e100115ab7a1f532fb",
-    "https://deno.land/std@0.117.0/io/writers.ts": "10067b4e637c602c0ae6bb3b8285da76edeb988f43add4ec4b969c76e17d8a98",
-    "https://deno.land/std@0.117.0/streams/conversion.ts": "fe0059ed9d3c53eda4ba44eb71a6a9acb98c5fdb5ba1b6c6ab28004724c7641b",
-    "https://deno.land/std@0.117.0/testing/_diff.ts": "e6a10d2aca8d6c27a9c5b8a2dbbf64353874730af539707b5b39d4128140642d",
-    "https://deno.land/std@0.117.0/testing/asserts.ts": "a1fef0239a2c343b0baa49c77dcdd7412613c46f3aba2887c331a2d7ed1f645e",
+    "https://deno.land/std@0.150.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
+    "https://deno.land/std@0.150.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4",
+    "https://deno.land/std@0.150.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a",
+    "https://deno.land/std@0.150.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf",
+    "https://deno.land/std@0.150.0/fmt/colors.ts": "6f9340b7fb8cc25a993a99e5efc56fe81bb5af284ff412129dd06df06f53c0b4",
+    "https://deno.land/std@0.150.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d",
+    "https://deno.land/std@0.150.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b",
+    "https://deno.land/std@0.150.0/log/handlers.ts": "b88c24df61eaeee8581dbef3622f21aebfd061cd2fda49affc1711c0e54d57da",
+    "https://deno.land/std@0.150.0/log/levels.ts": "82c965b90f763b5313e7595d4ba78d5095a13646d18430ebaf547526131604d1",
+    "https://deno.land/std@0.150.0/log/logger.ts": "4d25581bc02dfbe3ad7e8bb480e1f221793a85be5e056185a0cea134f7a7fdf4",
+    "https://deno.land/std@0.150.0/log/mod.ts": "65d2702785714b8d41061426b5c279f11b3dcbc716f3eb5384372a430af63961",
     "https://deno.land/std@0.150.0/media_types/_util.ts": "ce9b4fc4ba1c447dafab619055e20fd88236ca6bdd7834a21f98bd193c3fbfa1",
     "https://deno.land/std@0.150.0/media_types/mod.ts": "2d4b6f32a087029272dc59e0a55ae3cc4d1b27b794ccf528e94b1925795b3118",
     "https://deno.land/std@0.150.0/media_types/vendor/mime-db.v1.52.0.ts": "724cee25fa40f1a52d3937d6b4fbbfdd7791ff55e1b7ac08d9319d5632c7f5af",
-    "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
-    "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
-    "https://deno.land/std@0.224.0/bytes/concat.ts": "86161274b5546a02bdb3154652418efe7af8c9310e8d54107a68aaa148e0f5ed",
-    "https://deno.land/std@0.224.0/crypto/_wasm/lib/deno_std_wasm_crypto.generated.mjs": "7cd490ae1553c97459bd02de4c3f0a552768a85621949b2366003f3cf84b99d7",
-    "https://deno.land/std@0.224.0/crypto/_wasm/mod.ts": "e89fbbc3c4722602ff975dd85f18273c7741ec766a9b68f6de4fd1d9876409f8",
-    "https://deno.land/std@0.224.0/crypto/crypto.ts": "e58d78f3db111a499261dbab037ec78cc89da0516a50e1f0205665980a3417e3",
-    "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
-    "https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8",
-    "https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2",
-    "https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c",
-    "https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c",
-    "https://deno.land/std@0.224.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
-    "https://deno.land/std@0.224.0/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b",
-    "https://deno.land/std@0.224.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf",
-    "https://deno.land/std@0.224.0/path/_common/glob_to_reg_exp.ts": "6cac16d5c2dc23af7d66348a7ce430e5de4e70b0eede074bdbcf4903f4374d8d",
-    "https://deno.land/std@0.224.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
-    "https://deno.land/std@0.224.0/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3",
-    "https://deno.land/std@0.224.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607",
-    "https://deno.land/std@0.224.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a",
-    "https://deno.land/std@0.224.0/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883",
-    "https://deno.land/std@0.224.0/path/_interface.ts": "8dfeb930ca4a772c458a8c7bbe1e33216fe91c253411338ad80c5b6fa93ddba0",
-    "https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15",
-    "https://deno.land/std@0.224.0/path/basename.ts": "7ee495c2d1ee516ffff48fb9a93267ba928b5a3486b550be73071bc14f8cc63e",
-    "https://deno.land/std@0.224.0/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643",
-    "https://deno.land/std@0.224.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36",
-    "https://deno.land/std@0.224.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c",
-    "https://deno.land/std@0.224.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441",
-    "https://deno.land/std@0.224.0/path/format.ts": "6ce1779b0980296cf2bc20d66436b12792102b831fd281ab9eb08fa8a3e6f6ac",
-    "https://deno.land/std@0.224.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069",
-    "https://deno.land/std@0.224.0/path/glob_to_regexp.ts": "7f30f0a21439cadfdae1be1bf370880b415e676097fda584a63ce319053b5972",
-    "https://deno.land/std@0.224.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7",
-    "https://deno.land/std@0.224.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141",
-    "https://deno.land/std@0.224.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a",
-    "https://deno.land/std@0.224.0/path/join_globs.ts": "5b3bf248b93247194f94fa6947b612ab9d3abd571ca8386cf7789038545e54a0",
-    "https://deno.land/std@0.224.0/path/mod.ts": "f6bd79cb08be0e604201bc9de41ac9248582699d1b2ee0ab6bc9190d472cf9cd",
-    "https://deno.land/std@0.224.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352",
-    "https://deno.land/std@0.224.0/path/normalize_glob.ts": "cc89a77a7d3b1d01053b9dcd59462b75482b11e9068ae6c754b5cf5d794b374f",
-    "https://deno.land/std@0.224.0/path/parse.ts": "77ad91dcb235a66c6f504df83087ce2a5471e67d79c402014f6e847389108d5a",
-    "https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d",
-    "https://deno.land/std@0.224.0/path/posix/basename.ts": "d2fa5fbbb1c5a3ab8b9326458a8d4ceac77580961b3739cd5bfd1d3541a3e5f0",
-    "https://deno.land/std@0.224.0/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
-    "https://deno.land/std@0.224.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1",
-    "https://deno.land/std@0.224.0/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00",
-    "https://deno.land/std@0.224.0/path/posix/extname.ts": "e398c1d9d1908d3756a7ed94199fcd169e79466dd88feffd2f47ce0abf9d61d2",
-    "https://deno.land/std@0.224.0/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1",
-    "https://deno.land/std@0.224.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40",
-    "https://deno.land/std@0.224.0/path/posix/glob_to_regexp.ts": "76f012fcdb22c04b633f536c0b9644d100861bea36e9da56a94b9c589a742e8f",
-    "https://deno.land/std@0.224.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede",
-    "https://deno.land/std@0.224.0/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
-    "https://deno.land/std@0.224.0/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63",
-    "https://deno.land/std@0.224.0/path/posix/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25",
-    "https://deno.land/std@0.224.0/path/posix/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604",
-    "https://deno.land/std@0.224.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91",
-    "https://deno.land/std@0.224.0/path/posix/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6",
-    "https://deno.land/std@0.224.0/path/posix/parse.ts": "09dfad0cae530f93627202f28c1befa78ea6e751f92f478ca2cc3b56be2cbb6a",
-    "https://deno.land/std@0.224.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c",
-    "https://deno.land/std@0.224.0/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf",
-    "https://deno.land/std@0.224.0/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf",
-    "https://deno.land/std@0.224.0/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0",
-    "https://deno.land/std@0.224.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add",
-    "https://deno.land/std@0.224.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d",
-    "https://deno.land/std@0.224.0/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b",
-    "https://deno.land/std@0.224.0/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40",
-    "https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808",
-    "https://deno.land/std@0.224.0/path/windows/basename.ts": "6bbc57bac9df2cec43288c8c5334919418d784243a00bc10de67d392ab36d660",
-    "https://deno.land/std@0.224.0/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
-    "https://deno.land/std@0.224.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5",
-    "https://deno.land/std@0.224.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9",
-    "https://deno.land/std@0.224.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef",
-    "https://deno.land/std@0.224.0/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6",
-    "https://deno.land/std@0.224.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01",
-    "https://deno.land/std@0.224.0/path/windows/glob_to_regexp.ts": "e45f1f89bf3fc36f94ab7b3b9d0026729829fabc486c77f414caebef3b7304f8",
-    "https://deno.land/std@0.224.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a",
-    "https://deno.land/std@0.224.0/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
-    "https://deno.land/std@0.224.0/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf",
-    "https://deno.land/std@0.224.0/path/windows/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25",
-    "https://deno.land/std@0.224.0/path/windows/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604",
-    "https://deno.land/std@0.224.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780",
-    "https://deno.land/std@0.224.0/path/windows/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6",
-    "https://deno.land/std@0.224.0/path/windows/parse.ts": "08804327b0484d18ab4d6781742bf374976de662f8642e62a67e93346e759707",
-    "https://deno.land/std@0.224.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7",
-    "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972",
-    "https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e",
-    "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c",
-    "https://deno.land/std@0.224.0/uuid/_common.ts": "05c787c5735776c4e48e30294878332c39cb7738f50b209df4eb9f2b0facce4d",
-    "https://deno.land/std@0.224.0/uuid/constants.ts": "eb6c96871e968adf3355507d7ae79adce71525fd6c1ca55c51d32ace0196d64e",
-    "https://deno.land/std@0.224.0/uuid/mod.ts": "cefc8e2f77d9e493739c8dc4ec141b12b855414bf757e778bf9b00f783506b76",
-    "https://deno.land/std@0.224.0/uuid/v1.ts": "cc45e7eb1d463d7d38b21a3c6e4de55ff98598ca442309321575fe841b323a54",
-    "https://deno.land/std@0.224.0/uuid/v3.ts": "689f2d64a9460a75877a2eed94662d9cb31bedb890d72fce0d161ef47d66cc26",
-    "https://deno.land/std@0.224.0/uuid/v4.ts": "1319a2eeff7259adda416ec5f7997ded80d3165ef0787012793fc8621c18c493",
-    "https://deno.land/std@0.224.0/uuid/v5.ts": "75f76d9e53583572fe3d4893168530986222d439b1545b56d4493c6d5d1cd81d",
-    "https://deno.land/std@0.77.0/fmt/colors.ts": "c5665c66f1a67228f21c5989bbb04b36d369b98dd7ceac06f5e26856c81c2531",
-    "https://deno.land/x/bytes_formater@v1.4.0/deps.ts": "4f98f74e21145423b873a5ca6ead66dc3e674fa81e230a0a395f9b86aafeceea",
-    "https://deno.land/x/bytes_formater@v1.4.0/format.ts": "657c41b9f180c3ed0f934dcf75f77b09b6a610be98bb07525bffe2acfd5af4d5",
-    "https://deno.land/x/bytes_formater@v1.4.0/mod.ts": "c6bf35303f53d74e9134eb13f666fb388fb4c62c6b12b17542bbadade250a864",
-    "https://deno.land/x/case@v2.1.0/camelCase.ts": "0808961e69e1883c6e94faf85e333196a464ceba46ddc76984d7b8ce9e26a43a",
-    "https://deno.land/x/case@v2.1.0/constantCase.ts": "e50eaa8a45cf68417902fda218f03a99e45e2a30f7fce2db325306c84e8c3f0a",
-    "https://deno.land/x/case@v2.1.0/dotCase.ts": "ef3977567057e6c2a4e1b5ca09ec34b9e0788c1a50246187c11f435a468be17e",
-    "https://deno.land/x/case@v2.1.0/headerCase.ts": "4440a251a196fb6d7090213c39e4c6c50ddecab90c14ec91495bee3563082d3c",
-    "https://deno.land/x/case@v2.1.0/lowerCase.ts": "86d5533f9587ed60003181591e40e648838c23f371edfa79d00288153d113b16",
-    "https://deno.land/x/case@v2.1.0/lowerFirstCase.ts": "74e8ebe10f3c54a9d8e81d21127a20fcfb34c446e9c49b2a335162babd652de9",
-    "https://deno.land/x/case@v2.1.0/mod.ts": "28b0b1329c7b18730799ac05627a433d9547c04b9bfb429116247c60edecd97b",
-    "https://deno.land/x/case@v2.1.0/normalCase.ts": "6a8b924da9ab0790d99233ae54bfcfc996d229cb91b2533639fe20972cc33dac",
-    "https://deno.land/x/case@v2.1.0/paramCase.ts": "cf3101c59fd4f16ee14fd09985adb7fa3dbfb194f102499986f7407995202394",
-    "https://deno.land/x/case@v2.1.0/pascalCase.ts": "f68936d584182c8f4b341238bd9c424b1a740bfba3ab2847234f57a4c205f1df",
-    "https://deno.land/x/case@v2.1.0/pathCase.ts": "76e5f437369f8981e17ecdb07870e1c9c8d2d0357f1bf3377e2b0eb132159ed8",
-    "https://deno.land/x/case@v2.1.0/sentenceCase.ts": "f3355985a6a41e088c8c9be80219e5e055a68ad9a987df084a57ee437a0871c5",
-    "https://deno.land/x/case@v2.1.0/snakeCase.ts": "ee2ab4e2c931d30bb79190d090c21eb5c00d1de1b7a9a3e7f33e035ae431333b",
-    "https://deno.land/x/case@v2.1.0/swapCase.ts": "d9b5ee5b8e1ee3d202cbce32a504dde976b9002ed94b4527054a004179905dbb",
-    "https://deno.land/x/case@v2.1.0/titleCase.ts": "36d3fc73df58712240a74b04d84191ac22dd2866bef3838b02157f8e46cb0ecb",
-    "https://deno.land/x/case@v2.1.0/types.ts": "8e2bd6edaa27c0d1972c0d5b76698564740f37b4d3787d58d1fb5f48de611e61",
-    "https://deno.land/x/case@v2.1.0/upperCase.ts": "e6a1a3dea30e17013aa212ec237b35e2dd5c5c0b1501778c07db66ff0bbe4372",
-    "https://deno.land/x/case@v2.1.0/upperFirstCase.ts": "2b083db95744105a4f4accefe66dcd09390634514abf757fc580a7ebad58bf4f",
-    "https://deno.land/x/case@v2.1.0/vendor/camelCaseRegexp.ts": "7d9ff02aad4ab6429eeab7c7353f7bcdd6cc5909a8bd3dda97918c8bbb7621ae",
-    "https://deno.land/x/case@v2.1.0/vendor/camelCaseUpperRegexp.ts": "292de54a698370f90adcdf95727993d09888b7f33d17f72f8e54ba75f7791787",
-    "https://deno.land/x/case@v2.1.0/vendor/nonWordRegexp.ts": "c1a052629a694144b48c66b0175a22a83f4d61cb40f4e45293fc5d6b123f927e",
-    "https://deno.land/x/colorlog@v1.0/mod.ts": "1a547b3d64bb7cb021d5782d68ef55ace10730ed12a802e0663e4658610a71a2",
-    "https://deno.land/x/denodb@v1.4.0/deps.ts": "623e8e9aa315097b7bb6e49694e0a5b13dc3b0bedaf583b225b25e7573db133c",
-    "https://deno.land/x/denodb@v1.4.0/lib/connectors/connector.ts": "9cce5289c67c0f1e9855ae51f7d2b0f636e54df03c783b4d858d3da93918da6d",
-    "https://deno.land/x/denodb@v1.4.0/lib/connectors/factory.ts": "debc9e7321d787cd6bfeab3ad795801506811b8d60c4c9a48ded957ea22ffc8c",
-    "https://deno.land/x/denodb@v1.4.0/lib/connectors/mongodb-connector.ts": "d6a98fc76cc08df54e41fa8636b02f861cb20613c68dfc6c596a09bec6fdae00",
-    "https://deno.land/x/denodb@v1.4.0/lib/connectors/mysql-connector.ts": "ef6d4c03c20f306de422f58ad798c2d60673d11cbba8742516d0bf6ddda89d9d",
-    "https://deno.land/x/denodb@v1.4.0/lib/connectors/postgres-connector.ts": "c4d5816e79a7e13b276c71de8c4a833b3d86e715160fa785ffd4d7738c1cce71",
-    "https://deno.land/x/denodb@v1.4.0/lib/connectors/sqlite3-connector.ts": "3c71b9110140cfc7ed1b62202773e3cc6a9901853ff8f325ffa74273eedbdad9",
-    "https://deno.land/x/denodb@v1.4.0/lib/data-types.ts": "696da99750fa9e1f30d6741dabe7c0a333d96392f32052c1f89ee3f0c44896db",
-    "https://deno.land/x/denodb@v1.4.0/lib/database.ts": "a78d556140066d534e47845fd2700912f39a9bbc369a9f3969c29c003a448439",
-    "https://deno.land/x/denodb@v1.4.0/lib/helpers/fields.ts": "fdbf9bcc84d618b88fc4bd164bc828c618eab9ffcc43298d3a8bd425d5c0c8e2",
-    "https://deno.land/x/denodb@v1.4.0/lib/helpers/log.ts": "9cadee73efea4a7cbfc54fc30bd868ffd652e6188f59e13199966af049d8684a",
-    "https://deno.land/x/denodb@v1.4.0/lib/helpers/results.ts": "fb6817a9e27a760b22c499c2928a2fe843c2fa9ceffb3abe2bde88acbdbb1acd",
-    "https://deno.land/x/denodb@v1.4.0/lib/model-pivot.ts": "f6ca2b421b3c55ac16226d799ae36563f68bbf0d3e04e36102713edb533251d1",
-    "https://deno.land/x/denodb@v1.4.0/lib/model.ts": "762500bb81c86bf4c0d167ceff1d1532f0a842d43e9b49c62ea997875289db3f",
-    "https://deno.land/x/denodb@v1.4.0/lib/query-builder.ts": "475351f1fa2ab6720843ca70ecb925fb4cbf8dab8a78000ecd02212053ed04fc",
-    "https://deno.land/x/denodb@v1.4.0/lib/relationships.ts": "a0a1d684127039cb50b1c2fe2492fdacf63079fd9b8599e2648ad1afb0143740",
-    "https://deno.land/x/denodb@v1.4.0/lib/translators/basic-translator.ts": "38aec23d986eed04c1ea684b385bd511273a761605b3d7d971db4e08b61437fb",
-    "https://deno.land/x/denodb@v1.4.0/lib/translators/sql-translator.ts": "8dbe05021f03cf8a7fed2619a29546c4851134e2ea9d7670ee72b95492a9ba0b",
-    "https://deno.land/x/denodb@v1.4.0/lib/translators/translator.ts": "6c8b0fa23eb5c1c777bcc34af05b66637f207e9f5134643b3be33bcd657ae4e9",
-    "https://deno.land/x/denodb@v1.4.0/mod.ts": "ff3f31b25895670a5d3055e5c5fbfa4722ff2486f4f77b930477ab4d79bc5b2d",
-    "https://deno.land/x/mongo@v0.28.1/bson/bson.ts": "b9c2a1e4b4419bc7e788f0a44996812174393083278c3552e22dcf675693a563",
-    "https://deno.land/x/mongo@v0.28.1/deps.ts": "6c61093d6df600d3ea7520edc4903fe86dab0d5ccf452b41b4e6f8a1f014ea74",
-    "https://deno.land/x/mongo@v0.28.1/mod.ts": "665e574d7e51cb97be47de48d693b0f0d45bd9cad9674c5c56613f46533bc07b",
-    "https://deno.land/x/mongo@v0.28.1/src/auth/base.ts": "3a66fc307c6548bcef0e82cef391c8731f15cb04125094f978c0ca536287db3a",
-    "https://deno.land/x/mongo@v0.28.1/src/auth/mod.ts": "b161611bd5be9e9d1b4497227c8ea93ad4daadcd98038c9e5a707bc5da7a25ca",
-    "https://deno.land/x/mongo@v0.28.1/src/auth/pbkdf2.ts": "1f1db192fd37869d118ab34780b64567ebcf0ad83a03ef28b3d740cae9adb47a",
-    "https://deno.land/x/mongo@v0.28.1/src/auth/scram.ts": "4ec28f58a210b87231e9e55a676628fb1b5a853bfb22c2e36b3ae348b368f671",
-    "https://deno.land/x/mongo@v0.28.1/src/auth/x509.ts": "6ce3a17a3b961e1f278b24e859f562c42647efea5072e69030e0e5870ac70b9c",
-    "https://deno.land/x/mongo@v0.28.1/src/client.ts": "823ba7d7c17135eeb7bcf4f4783b788f26df40bd2a2ce0e26ea99ffd19c18a5b",
-    "https://deno.land/x/mongo@v0.28.1/src/cluster.ts": "40be6bcb22337b2f5c87adcdadf6c841975fd4202eec00fcf4a029a58800c2d1",
-    "https://deno.land/x/mongo@v0.28.1/src/collection/collection.ts": "e3b12fa6ebb490acc9761cb63acc30a266821008581b25f92de46c3e42235ea2",
-    "https://deno.land/x/mongo@v0.28.1/src/collection/commands/aggregate.ts": "9ed10eacbdcd61c020d91b831d4bffa75afd3c4c272fa69e1aaf77bc339d526f",
-    "https://deno.land/x/mongo@v0.28.1/src/collection/commands/find.ts": "247e00d69efef7c6f33a82cb2055cc27e00e7e9dd5232a5cb9d6e65a64c1bbbe",
-    "https://deno.land/x/mongo@v0.28.1/src/collection/commands/list_indexes.ts": "d32a5120305d0547497026b4f5ab2ecc2f40ff7f44c405b99c2a0287765333f5",
-    "https://deno.land/x/mongo@v0.28.1/src/collection/commands/update.ts": "4c5cc7bfd9e9d4d7d120595615045037cfda520be4a722a0c38f9543ff485e8b",
-    "https://deno.land/x/mongo@v0.28.1/src/collection/mod.ts": "bd791a0b9b46be4365f88c54893584eadec2f8cd799db9eb05e0b5b4c8d72b8e",
-    "https://deno.land/x/mongo@v0.28.1/src/database.ts": "e1035b010445ceb73436042d99b5ce39afaff4293e9b5e7333b1aa74fda9a107",
-    "https://deno.land/x/mongo@v0.28.1/src/error.ts": "9e24b26b25119373bb5c13930ccf807a4bdcfe6633dcd94f04c6df7636d70d34",
-    "https://deno.land/x/mongo@v0.28.1/src/gridfs/bucket.ts": "e988aede2a051987ad14acbcdcb1ae2815148c487d480b0e7a4499709fee9da3",
-    "https://deno.land/x/mongo@v0.28.1/src/gridfs/indexes.ts": "a0ea4369bf48d119038ac9e8a2fb0bcf974847fe880f74e40dea60e76315343f",
-    "https://deno.land/x/mongo@v0.28.1/src/gridfs/upload.ts": "20acb3aa195134e4cf7bc24337cb00029f7c94323347b003ce831f23b5b2f1b1",
-    "https://deno.land/x/mongo@v0.28.1/src/protocol/cursor.ts": "9692ab4045514811860fd53faa3aba04a412be6e347b22001b62693dca097ad2",
-    "https://deno.land/x/mongo@v0.28.1/src/protocol/handshake.ts": "a5bef517984f56eca54ca4b82d81da83a9327daf60ef47d14fe4764cc611c9a4",
-    "https://deno.land/x/mongo@v0.28.1/src/protocol/header.ts": "0f28db842f886e57b7013606c1391affab2e2960a1a4568d2502e7b788117716",
-    "https://deno.land/x/mongo@v0.28.1/src/protocol/message.ts": "65a3f6b7eb6b6d623a858ddcff175c7dca86ee0df36ca7aec8dcb0fbf5794c15",
-    "https://deno.land/x/mongo@v0.28.1/src/protocol/mod.ts": "4e24d563049c0a236234598ca786ca13778dc17fdb80ac543ac6c75d0c5094d7",
-    "https://deno.land/x/mongo@v0.28.1/src/protocol/protocol.ts": "31ef11c927496b11364719d52d7ff1a77568042f0b3af7bad8de0e31984e869f",
-    "https://deno.land/x/mongo@v0.28.1/src/types.ts": "9491a89586254ca5c8b96ce7cdece0a45447b99f2695f8e59abefb325a32519a",
-    "https://deno.land/x/mongo@v0.28.1/src/types/gridfs.ts": "2f8a7f4f7ac9f7087e9f88d8e752432014fcccb1255d015dd29af225d6d50a61",
-    "https://deno.land/x/mongo@v0.28.1/src/types/read_write_concern.ts": "d00f35eb85520e776741888685d08d479766a19e9a0a970b53f4594c9db00496",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/bson.ts": "ac9bdb797a179c71f5c4498234cdfbd49b87253fbfdd0be413859b22c5e60e96",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/ns.ts": "fb0c57b8dc4d31f8993112d267dec3c163d3e8862198d1cd03b2b51bcc3caad9",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/saslprep/deps.ts": "95ceb81b353110526dacf2a98854bc79d6e17d7f173af8806e91c05555d7b8c7",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/saslprep/load_code_points.ts": "f6a4ef2eb2345eac40ffbf1a30661cca803f399865f2a0fadafb71f57d4c97bf",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/saslprep/memory_pager.ts": "f55a79a13ec569c21630c3915a9af0c6fc0aa2b899121fa2a85a813c6dd4afba",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/saslprep/mod.ts": "0a8a39a0784d065a79c54ce63d7d7b103d0b94addc5b7bcf985517ba2442c8a1",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/saslprep/sparse_bitfield.ts": "07d6fe2ecd4ba5f711c44c1ae409bb9c1fe3a3cfc09e27434d231d4aae46dd2d",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/srv.ts": "09c207069ea6fec02ddcd77c797171e556771dd940eb190a95cf4c7138e87baa",
-    "https://deno.land/x/mongo@v0.28.1/src/utils/uri.ts": "a3d80622c8a8eed6bae422cfd817af8e1f62523fcc1e7c019b3d7b7cff0d0f7a",
-    "https://deno.land/x/mysql@v2.11.0/deps.ts": "68635959a41bb08bc87db007679fb8449febc55d48202dff20b93cc23ef5820d",
-    "https://deno.land/x/mysql@v2.11.0/mod.ts": "c751574b2b41bb0926f0eb4f29c70aa9a435dc039a370e1fb238dc495fea2dcf",
-    "https://deno.land/x/mysql@v2.11.0/src/auth.ts": "129ea08b180d3e90e567c3f71e60432bb266304c224e17ea39d604bbcc1160d8",
-    "https://deno.land/x/mysql@v2.11.0/src/auth_plugin/caching_sha2_password.ts": "aab89e272382e6f408406f860ae6e79628275f4511e27a565049033543c4bdec",
-    "https://deno.land/x/mysql@v2.11.0/src/auth_plugin/crypt.ts": "8798819cce1171d95cfee8edda15fe6a652068cad4dc91f81b6e91cf90a13617",
-    "https://deno.land/x/mysql@v2.11.0/src/auth_plugin/index.ts": "8617e520ad854e38470aeefd07becdb3397c4cde16c2397dd48d5c10fdd5ab09",
-    "https://deno.land/x/mysql@v2.11.0/src/buffer.ts": "59f7e08e196f1b7e58cf5c3cf8ae8f4d0d47d1ae31430076fc468d974d3b59e7",
-    "https://deno.land/x/mysql@v2.11.0/src/client.ts": "9a486419dfeb5f4d15d9fa56705e3cfbab6134bbbe08783a515e7e38f5cbca65",
-    "https://deno.land/x/mysql@v2.11.0/src/connection.ts": "0ca035bbba2865f93900d8817f9d3a080e4c01bca3a45dc8318276a14b1d9459",
-    "https://deno.land/x/mysql@v2.11.0/src/constant/capabilities.ts": "bf6b357b793da4d6e3f192a45d2368767902d7cb92affde2b393c3e08ed530f9",
-    "https://deno.land/x/mysql@v2.11.0/src/constant/charset.ts": "253d7233679c774df623d1f974ebb358f3678c18fd6a623e25983311d97d959b",
-    "https://deno.land/x/mysql@v2.11.0/src/constant/errors.ts": "923bab27d524e43199fa21fdfcbe025580ca76d8b32254ad9505765c502f238a",
-    "https://deno.land/x/mysql@v2.11.0/src/constant/mysql_types.ts": "79c50de8eb5919b897e81e2ff2366ee1ffdbb4297f711e15003bdb787bbc8e6c",
-    "https://deno.land/x/mysql@v2.11.0/src/constant/packet.ts": "a1e7e00ce30c551c5f95c05d233b8d83f8e1fc865de97be3b317058e173630a9",
-    "https://deno.land/x/mysql@v2.11.0/src/deferred.ts": "35d087619d919961e849e382c33b2bfea15b4119f55eca2d9c9047f30512a2cb",
-    "https://deno.land/x/mysql@v2.11.0/src/logger.ts": "9fe85e361d3972f3105e33930dd4a069456c625b5b0cd7efc322418964edc470",
-    "https://deno.land/x/mysql@v2.11.0/src/packets/builders/auth.ts": "d9752c7e95aae3f3ace81df03c19907ed8a9dfe9c19399796e80a24cb83ab2ed",
-    "https://deno.land/x/mysql@v2.11.0/src/packets/builders/query.ts": "caf426a72ebe545ff5bab14c8b7b5e412dd8827c091322959cdf4e9aa89ef900",
-    "https://deno.land/x/mysql@v2.11.0/src/packets/packet.ts": "d7800cc142226f7dfd3c5f647f03cd3ef308f9d8551b4edb2e1bfb9c758d33b6",
-    "https://deno.land/x/mysql@v2.11.0/src/packets/parsers/authswitch.ts": "aa34f21336c4907b3ae968108fcdad8f1c43a303088efd83d972e6c7b258c166",
-    "https://deno.land/x/mysql@v2.11.0/src/packets/parsers/err.ts": "4110c4ddc2ae8358d6661fa2522f8eda2e603900d1e433e3684765ed50e88ed8",
-    "https://deno.land/x/mysql@v2.11.0/src/packets/parsers/handshake.ts": "88f7ee34e9e0ef089bc5fdefacaccf256ef002b2f7a8ad684e35327682039e73",
-    "https://deno.land/x/mysql@v2.11.0/src/packets/parsers/result.ts": "8ab16f1adae67415eefcc17803b0eb828c1f4c6a24c55f25949f418e862d3ec8",
-    "https://deno.land/x/mysql@v2.11.0/src/pool.ts": "978ba2813b3886d68be007678360ad43c54dab14b1aea1c07fcdb41222fcc432",
-    "https://deno.land/x/mysql@v2.11.0/src/util.ts": "83d38e87cc3901da00ac44bfcd53c0e8d24525262f5c7647c912dccf3ed2dbb5",
-    "https://deno.land/x/postgres@v0.14.2/client.ts": "06b636869904b090c998f79620ae0a0ea062c204728e236c76cb45e31ca9dee5",
-    "https://deno.land/x/postgres@v0.14.2/client/error.ts": "60b9a575b694a92657654165f6737acbe9fb4bbc1ecf1359a12b413129bb6cdf",
-    "https://deno.land/x/postgres@v0.14.2/connection/auth.ts": "f397ca9bcd8cf40644085bb030f232ae691749d77ad10693f2e33e07b977bc47",
-    "https://deno.land/x/postgres@v0.14.2/connection/connection.ts": "3f0959e1d3e76f6feb03502779fe981dfaa72edaffe6a9bcafeeecabd0fe48d1",
-    "https://deno.land/x/postgres@v0.14.2/connection/connection_params.ts": "e5621de3b9563734455602f01ce7b20add950ee882b81866d3d3b43db8daa6fb",
-    "https://deno.land/x/postgres@v0.14.2/connection/message.ts": "f9257948b7f87d58bfbfe3fc6e2e08f0de3ef885655904d56a5f73655cc22c5a",
-    "https://deno.land/x/postgres@v0.14.2/connection/message_code.ts": "466719008b298770c366c5c63f6cf8285b7f76514dadb4b11e7d9756a8a1ddbf",
-    "https://deno.land/x/postgres@v0.14.2/connection/packet.ts": "050aeff1fc13c9349e89451a155ffcd0b1343dc313a51f84439e3e45f64b56c8",
-    "https://deno.land/x/postgres@v0.14.2/connection/scram.ts": "11316f3181a21b6bdd8e7c66442469a0c956bd6cf46b086c9199668b3c37654c",
-    "https://deno.land/x/postgres@v0.14.2/deps.ts": "dfc89682720edaecadc553d99103333c7f01398d00baced04a350d6288bdd7ee",
-    "https://deno.land/x/postgres@v0.14.2/mod.ts": "d30a3e499b827b645bc396e6c9cdab9814dfc99a3e5b2f9a55fbc83b68d7b079",
-    "https://deno.land/x/postgres@v0.14.2/pool.ts": "d1277c33156b15b39786fc4a5d2278d8d3b73416d4bca4a5b0bb5e2913d117ed",
-    "https://deno.land/x/postgres@v0.14.2/query/array_parser.ts": "f8a229d82c3801de8266fa2cc4afe12e94fef8d0c479e73655c86ed3667ef33f",
-    "https://deno.land/x/postgres@v0.14.2/query/decode.ts": "44a4a6cbcf494ed91a4fecae38a57dce63a7b519166f02c702791d9717371419",
-    "https://deno.land/x/postgres@v0.14.2/query/decoders.ts": "4f654e12c2a9f0a52c40cd2986d817e5dbec57cd4ad093ea6cbe996d797f6e37",
-    "https://deno.land/x/postgres@v0.14.2/query/encode.ts": "990a49b10b4fe2724a88bafe1c05c3df4cdba686f8baa2ec16b739ff0628b87d",
-    "https://deno.land/x/postgres@v0.14.2/query/oid.ts": "8c33e1325f34e4ca9f11a48b8066c8cfcace5f64bc1eb17ad7247af4936999e1",
-    "https://deno.land/x/postgres@v0.14.2/query/query.ts": "36c191c3ed4f089dd281bea641a0974569993a88e0049e05d8087cd9b0f87406",
-    "https://deno.land/x/postgres@v0.14.2/query/transaction.ts": "e2902f3afed91e2697692f1f0d1edf302be7e5692c2c2e291d5c7ae039daef34",
-    "https://deno.land/x/postgres@v0.14.2/query/types.ts": "a6dc8024867fe7ccb0ba4b4fa403ee5d474c7742174128c8e689c3b5e5eaa933",
-    "https://deno.land/x/postgres@v0.14.2/utils/deferred.ts": "3c203ba0bebae5a33edd95d59c633d3f8b887c9856ad406642ac5a3c47ba005a",
-    "https://deno.land/x/postgres@v0.14.2/utils/utils.ts": "0706bde6e189f28deb283df30193b1d4d0b3b737f1ff90e1170fcaecd7b8ffd5",
-    "https://deno.land/x/sql_builder@v1.9.1/util.ts": "b9855dc435972704cf82655019f4ec168ac83550ab4db596c5f6b6d201466384",
-    "https://deno.land/x/sqlite@v3.7.0/build/sqlite.js": "cc55fef9cd124b2acb624899a5fad413834f4701bcfc21ac275844b822466292",
-    "https://deno.land/x/sqlite@v3.7.0/build/vfs.js": "08533cc78fb29b9d9bd62f6bb93e5ef333407013fed185776808f11223ba0e70",
-    "https://deno.land/x/sqlite@v3.7.0/mod.ts": "e09fc79d8065fe222578114b109b1fd60077bff1bb75448532077f784f4d6a83",
-    "https://deno.land/x/sqlite@v3.7.0/src/constants.ts": "90f3be047ec0a89bcb5d6fc30db121685fc82cb00b1c476124ff47a4b0472aa9",
-    "https://deno.land/x/sqlite@v3.7.0/src/db.ts": "87e3d222d00dd2e2827816a47976c6359996ab39e69c7f87855ea414585839c6",
-    "https://deno.land/x/sqlite@v3.7.0/src/error.ts": "f7a15cb00d7c3797da1aefee3cf86d23e0ae92e73f0ba3165496c3816ab9503a",
-    "https://deno.land/x/sqlite@v3.7.0/src/function.ts": "e4c83b8ec64bf88bafad2407376b0c6a3b54e777593c70336fb40d43a79865f2",
-    "https://deno.land/x/sqlite@v3.7.0/src/query.ts": "69895232360b4254a8834e3bbea30bc3752cf691dba862f4a393e7a41a2e30eb",
-    "https://deno.land/x/sqlite@v3.7.0/src/wasm.ts": "e79d0baa6e42423257fb3c7cc98091c54399254867e0f34a09b5bdef37bd9487",
+    "https://deno.land/std@0.150.0/testing/_diff.ts": "029a00560b0d534bc0046f1bce4bd36b3b41ada3f2a3178c85686eb2ff5f1413",
+    "https://deno.land/std@0.150.0/testing/_format.ts": "0d8dc79eab15b67cdc532826213bbe05bccfd276ca473a50a3fc7bbfb7260642",
+    "https://deno.land/std@0.150.0/testing/_test_suite.ts": "ad453767aeb8c300878a6b7920e20370f4ce92a7b6c8e8a5d1ac2b7c14a09acb",
+    "https://deno.land/std@0.150.0/testing/asserts.ts": "0ee58a557ac764e762c62bb21f00e7d897e3919e71be38b2d574fb441d721005",
+    "https://deno.land/std@0.150.0/testing/bdd.ts": "182bb823e09bd75b76063ecf50722870101b7cfadf97a09fa29127279dc21128",
+    "https://deno.land/std@0.158.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
+    "https://deno.land/std@0.158.0/async/deferred.ts": "c01de44b9192359cebd3fe93273fcebf9e95110bf3360023917da9a2d1489fae",
+    "https://deno.land/std@0.158.0/async/delay.ts": "0419dfc993752849692d1f9647edf13407c7facc3509b099381be99ffbc9d699",
+    "https://deno.land/std@0.158.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4",
+    "https://deno.land/std@0.158.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a",
+    "https://deno.land/std@0.158.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf",
+    "https://deno.land/std@0.158.0/io/buffer.ts": "fae02290f52301c4e0188670e730cd902f9307fb732d79c4aa14ebdc82497289",
+    "https://deno.land/std@0.217.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
+    "https://deno.land/std@0.217.0/assert/_diff.ts": "dcc63d94ca289aec80644030cf88ccbf7acaa6fbd7b0f22add93616b36593840",
+    "https://deno.land/std@0.217.0/assert/_format.ts": "0ba808961bf678437fb486b56405b6fefad2cf87b5809667c781ddee8c32aff4",
+    "https://deno.land/std@0.217.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5",
+    "https://deno.land/std@0.217.0/assert/assert_almost_equals.ts": "8b96b7385cc117668b0720115eb6ee73d04c9bcb2f5d2344d674918c9113688f",
+    "https://deno.land/std@0.217.0/assert/assert_array_includes.ts": "1688d76317fd45b7e93ef9e2765f112fdf2b7c9821016cdfb380b9445374aed1",
+    "https://deno.land/std@0.217.0/assert/assert_equals.ts": "4497c56fe7d2993b0d447926702802fc0becb44e319079e8eca39b482ee01b4e",
+    "https://deno.land/std@0.217.0/assert/assert_exists.ts": "24a7bf965e634f909242cd09fbaf38bde6b791128ece08e33ab08586a7cc55c9",
+    "https://deno.land/std@0.217.0/assert/assert_false.ts": "6f382568e5128c0f855e5f7dbda8624c1ed9af4fcc33ef4a9afeeedcdce99769",
+    "https://deno.land/std@0.217.0/assert/assert_greater.ts": "4945cf5729f1a38874d7e589e0fe5cc5cd5abe5573ca2ddca9d3791aa891856c",
+    "https://deno.land/std@0.217.0/assert/assert_greater_or_equal.ts": "573ed8823283b8d94b7443eb69a849a3c369a8eb9666b2d1db50c33763a5d219",
+    "https://deno.land/std@0.217.0/assert/assert_instance_of.ts": "72dc1faff1e248692d873c89382fa1579dd7b53b56d52f37f9874a75b11ba444",
+    "https://deno.land/std@0.217.0/assert/assert_is_error.ts": "6596f2b5ba89ba2fe9b074f75e9318cda97a2381e59d476812e30077fbdb6ed2",
+    "https://deno.land/std@0.217.0/assert/assert_less.ts": "2b4b3fe7910f65f7be52212f19c3977ecb8ba5b2d6d0a296c83cde42920bb005",
+    "https://deno.land/std@0.217.0/assert/assert_less_or_equal.ts": "b93d212fe669fbde959e35b3437ac9a4468f2e6b77377e7b6ea2cfdd825d38a0",
+    "https://deno.land/std@0.217.0/assert/assert_match.ts": "ec2d9680ed3e7b9746ec57ec923a17eef6d476202f339ad91d22277d7f1d16e1",
+    "https://deno.land/std@0.217.0/assert/assert_not_equals.ts": "ac86413ab70ffb14fdfc41740ba579a983fe355ba0ce4a9ab685e6b8e7f6a250",
+    "https://deno.land/std@0.217.0/assert/assert_not_instance_of.ts": "8f720d92d83775c40b2542a8d76c60c2d4aeddaf8713c8d11df8984af2604931",
+    "https://deno.land/std@0.217.0/assert/assert_not_match.ts": "b4b7c77f146963e2b673c1ce4846473703409eb93f5ab0eb60f6e6f8aeffe39f",
+    "https://deno.land/std@0.217.0/assert/assert_not_strict_equals.ts": "da0b8ab60a45d5a9371088378e5313f624799470c3b54c76e8b8abeec40a77be",
+    "https://deno.land/std@0.217.0/assert/assert_object_match.ts": "e85e5eef62a56ce364c3afdd27978ccab979288a3e772e6855c270a7b118fa49",
+    "https://deno.land/std@0.217.0/assert/assert_rejects.ts": "e9e0c8d9c3e164c7ac962c37b3be50577c5a2010db107ed272c4c1afb1269f54",
+    "https://deno.land/std@0.217.0/assert/assert_strict_equals.ts": "0425a98f70badccb151644c902384c12771a93e65f8ff610244b8147b03a2366",
+    "https://deno.land/std@0.217.0/assert/assert_string_includes.ts": "dfb072a890167146f8e5bdd6fde887ce4657098e9f71f12716ef37f35fb6f4a7",
+    "https://deno.land/std@0.217.0/assert/assert_throws.ts": "edddd86b39606c342164b49ad88dd39a26e72a26655e07545d172f164b617fa7",
+    "https://deno.land/std@0.217.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8",
+    "https://deno.land/std@0.217.0/assert/equal.ts": "fae5e8a52a11d3ac694bbe1a53e13a7969e3f60791262312e91a3e741ae519e2",
+    "https://deno.land/std@0.217.0/assert/fail.ts": "f310e51992bac8e54f5fd8e44d098638434b2edb802383690e0d7a9be1979f1c",
+    "https://deno.land/std@0.217.0/assert/mod.ts": "325df8c0683ad83a873b9691aa66b812d6275fc9fec0b2d180ac68a2c5efed3b",
+    "https://deno.land/std@0.217.0/assert/unimplemented.ts": "47ca67d1c6dc53abd0bd729b71a31e0825fc452dbcd4fde4ca06789d5644e7fd",
+    "https://deno.land/std@0.217.0/assert/unreachable.ts": "38cfecb95d8b06906022d2f9474794fca4161a994f83354fd079cac9032b5145",
+    "https://deno.land/std@0.217.0/fmt/colors.ts": "d239d84620b921ea520125d778947881f62c50e78deef2657073840b8af9559a",
+    "https://deno.land/x/deno_dom@v0.1.45/build/deno-wasm/deno-wasm.js": "d6841a06342eb6a2798ef28de79ad69c0f2fa349fa04d3ca45e5fcfbf50a9340",
+    "https://deno.land/x/deno_dom@v0.1.45/deno-dom-wasm.ts": "a33d160421bbb6e3104285ea5ebf33352b7ad50d82ea8765e3cf65f972b25119",
+    "https://deno.land/x/deno_dom@v0.1.45/src/api.ts": "0ff5790f0a3eeecb4e00b7d8fbfa319b165962cf6d0182a65ba90f158d74f7d7",
+    "https://deno.land/x/deno_dom@v0.1.45/src/constructor-lock.ts": "59714df7e0571ec7bd338903b1f396202771a6d4d7f55a452936bd0de9deb186",
+    "https://deno.land/x/deno_dom@v0.1.45/src/deserialize.ts": "1cf4096678d8afed8ed28dbad690504c4d2c28149ba768b26eacd1416873425b",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/document-fragment.ts": "1c7352a3c816587ed7fad574b42636198f680f17abc3836fcfe7799b31e7718f",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/document.ts": "a182727dd9179e5712e31be66f4f72b766a5b714c765a75950babe6dd756b4ee",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/dom-parser.ts": "609097b426f8c2358f3e5d2bca55ed026cf26cdf86562e94130dfdb0f2537f92",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/element.ts": "d5371cd83ff2128353c1975465c368ef83d7441568626b386557deba51315111",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/elements/html-template-element.ts": "740b97a5378c9a14cccf3429299846eda240b613013e2d2d7f20b393897453c2",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/html-collection.ts": "829a965f419f8286d5f43a12e27886d10836d519ca2d5e74cb3f2e1d35f35746",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/node-list.ts": "9008303fe236e40e74f9f93e398bd173d2e9b09065932a0153dd0142c759397b",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/node.ts": "3069e6fc93ac4111a136ed68199d76673339842b9751610ba06f111ba7dc10a7",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/custom-api.ts": "852696bd58e534bc41bd3be9e2250b60b67cd95fd28ed16b1deff1d548531a71",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/nwsapi-types.ts": "c43b36c36acc5d32caabaa54fda8c9d239b2b0fcbce9a28efb93c84aa1021698",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/nwsapi.js": "985d7d8fc1eabbb88946b47a1c44c1b2d4aa79ff23c21424219f1528fa27a2ff",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/selectors.ts": "83eab57be2290fb48e3130533448c93c6c61239f2a2f3b85f1917f80ca0fdc75",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/sizzle-types.ts": "78149e2502409989ce861ed636b813b059e16bc267bb543e7c2b26ef43e4798b",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/sizzle.js": "c3aed60c1045a106d8e546ac2f85cc82e65f62d9af2f8f515210b9212286682a",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/utils-types.ts": "96db30e3e4a75b194201bb9fa30988215da7f91b380fca6a5143e51ece2a8436",
+    "https://deno.land/x/deno_dom@v0.1.45/src/dom/utils.ts": "4c6206516fb8f61f37a209c829e812c4f5a183e46d082934dd14c91bde939263",
+    "https://deno.land/x/deno_dom@v0.1.45/src/parser.ts": "e06b2300d693e6ae7564e53dfa5c9a9e97fdb8c044c39c52c8b93b5d60860be3",
+    "https://deno.land/x/deno_dom@v0.1.48/build/deno-wasm/deno-wasm.js": "d6841a06342eb6a2798ef28de79ad69c0f2fa349fa04d3ca45e5fcfbf50a9340",
+    "https://deno.land/x/deno_dom@v0.1.48/deno-dom-wasm.ts": "0669396686fb207f1354af33df6aabe2189b4eceafdb1bf7f3d6bbb2637b6b03",
+    "https://deno.land/x/deno_dom@v0.1.48/src/api.ts": "0ff5790f0a3eeecb4e00b7d8fbfa319b165962cf6d0182a65ba90f158d74f7d7",
+    "https://deno.land/x/deno_dom@v0.1.48/src/constructor-lock.ts": "0e7b297e8b9cf921a3b0d3a692ec5fb462c5afc47ec554292e20090b9e16b40a",
+    "https://deno.land/x/deno_dom@v0.1.48/src/deserialize.ts": "1cf4096678d8afed8ed28dbad690504c4d2c28149ba768b26eacd1416873425b",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/document-fragment.ts": "1c7352a3c816587ed7fad574b42636198f680f17abc3836fcfe7799b31e7718f",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/document.ts": "f8503c0ffe0d703535e84d174f1c30aa31eff15e1450777d7f2e8da81546c002",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/dom-parser.ts": "784ee0e766d4a01e14420f328053fd3a0016c6b40ee442edc3ae80f5d9777927",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/element.ts": "f662dbf28d2ac873ebbbe2d4ae53121d13879c2061416f6eae4e8cca58922e8b",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/elements/html-template-element.ts": "740b97a5378c9a14cccf3429299846eda240b613013e2d2d7f20b393897453c2",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/html-collection.ts": "eedc0b097612ef420d975df6924850a36a4829b35aafa4c92078609a15a52f08",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/node-list.ts": "d19fec8ed4979f43c8e117f9937b3da22acc2c8514cb1ef0074f54793cdfc8c9",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/node.ts": "c93e5f6b6c011cbad6f8728d65459782b911e097f9d0c8c99a51591f7c936449",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/selectors/custom-api.ts": "852696bd58e534bc41bd3be9e2250b60b67cd95fd28ed16b1deff1d548531a71",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/selectors/nwsapi-types.ts": "c43b36c36acc5d32caabaa54fda8c9d239b2b0fcbce9a28efb93c84aa1021698",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/selectors/nwsapi.js": "985d7d8fc1eabbb88946b47a1c44c1b2d4aa79ff23c21424219f1528fa27a2ff",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/selectors/selectors.ts": "83eab57be2290fb48e3130533448c93c6c61239f2a2f3b85f1917f80ca0fdc75",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/selectors/sizzle-types.ts": "78149e2502409989ce861ed636b813b059e16bc267bb543e7c2b26ef43e4798b",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/selectors/sizzle.js": "c3aed60c1045a106d8e546ac2f85cc82e65f62d9af2f8f515210b9212286682a",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/utils-types.ts": "96db30e3e4a75b194201bb9fa30988215da7f91b380fca6a5143e51ece2a8436",
+    "https://deno.land/x/deno_dom@v0.1.48/src/dom/utils.ts": "4c6206516fb8f61f37a209c829e812c4f5a183e46d082934dd14c91bde939263",
+    "https://deno.land/x/deno_dom@v0.1.48/src/parser.ts": "e06b2300d693e6ae7564e53dfa5c9a9e97fdb8c044c39c52c8b93b5d60860be3",
+    "https://deno.land/x/socket_io@0.2.1/deps.ts": "2c9c7fd0f00c9f8774a7cbf6bea2e73b274989aacb3ebfbd289a3c1bbe632bcb",
+    "https://deno.land/x/socket_io@0.2.1/mod.ts": "29050911ca6f9605623c672238bb209ca37ed23606c596d199e6e33de04168f9",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io-parser/base64-arraybuffer.ts": "57ccea6679609df5416159fcc8a47936ad28ad6fe32235ef78d9223a3a823407",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io-parser/mod.ts": "27d35094e2159ba49f6e74f11ed83b6208a6adb5a2d5ab3cbbdcdc9dc0e36ae7",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/lib/cors.ts": "e39b530dc3526ef85f288766ce592fa5cce2ec38b3fa19922041a7885b79b67c",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/lib/server.ts": "2faf79492858e532ad199c32b565bb04528fa9ea55d167e223dc9d019ef9315f",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/lib/socket.ts": "feb50d196decd7b1fc79af2706465cc7b4b8b18ebb7ca3dc8cedadb0a393103e",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/lib/transport.ts": "b09c589a099d539cd0b61f7b3be0b9d2d9ba7b8cbafdf4824425176ecf8d89c9",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/lib/transports/polling.ts": "5650189f6cd742ec0fd45f8c262105f07463b24e8183da6ffb2bf775baf21395",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/lib/transports/websocket.ts": "4a868e73d3b8b207d822d358f14723bd8d1cd80c6926be9c4bf6c4234e8a0d00",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/lib/util.ts": "9f396a141422c8a2e2ef4cbb31c8b7ec96665d8f1ca397888eaaa9ad28ca8c65",
+    "https://deno.land/x/socket_io@0.2.1/packages/engine.io/mod.ts": "3f7d85ebd3bee6e17838f4867927d808f35090a71e088fd4dd802e3255d44c4a",
+    "https://deno.land/x/socket_io@0.2.1/packages/event-emitter/mod.ts": "dcb2cb9c0b409060cf15a6306a8dbebea844aa3c58f782ed1d4bc3ccef7c2835",
+    "https://deno.land/x/socket_io@0.2.1/packages/msgpack/lib/decode.ts": "5906fa37474130b09fe308adb53c95e40d2484a015891be3249fb2f626c462bb",
+    "https://deno.land/x/socket_io@0.2.1/packages/msgpack/lib/encode.ts": "15dab78be56d539c03748c9d57086f7fd580eb8fbe2f8209c28750948c7d962e",
+    "https://deno.land/x/socket_io@0.2.1/packages/msgpack/mod.ts": "c7f4a9859af3e0b23794b400546d93475b19ba5110a02245112a0a994a31d309",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io-parser/mod.ts": "44479cf563b0ad80efedd1059cd40114bc5db199b45e623c2764e80f4a264f8c",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io-redis-adapter/mod.ts": "45d6b7f077f94fec385152bda7fda5ac3153c2ca3548cf4859891af673fa97cc",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/lib/adapter.ts": "594fbe748497ce346e8b783065cb19b284f27d702ff661d872971d14ccf6cf29",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/lib/broadcast-operator.ts": "d842eb933acc996a05ac701f6d83ffee49ee9c905c9adbdee70832776045bf63",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/lib/client.ts": "5e5d8e39d58cc5eb14f219e85ae6eef8fe6dd5f685f4c73496531aa48529c2c6",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/lib/namespace.ts": "d9ec734c8b45f4204c40382e1d3a8fb4d1b8bffab4bca4d2d69baece0703d4f8",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/lib/parent-namespace.ts": "9628cbf54e35bea01956825f557b4a82a37c8f1343423c0e7d952d475bf68ea0",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/lib/server.ts": "19b10d05e09e436fda0d8d205ba1ce06bb5877f516176b8148de8a3c26b37688",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/lib/socket.ts": "c448032d0f819d40d6dc30eb312c391c5d902eeeeb67bfd4f4f4c3499545eb7e",
+    "https://deno.land/x/socket_io@0.2.1/packages/socket.io/mod.ts": "dfd465bdcf23161af0c4d79fb8fc8912418c46a20d15e8b314cec6d9fb508196",
+    "https://deno.land/x/socket_io@0.2.1/test_deps.ts": "42e6bff240c54a2d7ade82154f8655650664a65ad9228623a0fdb3d0cde11ef0",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/backoff.ts": "33e4a6e245f8743fbae0ce583993a671a3ac2ecee433a3e7f0bd77b5dd541d84",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/command.ts": "802df3a1f49f6c49fe3e8fcf13fd0cc360b8a02369de0310a72d7f0c8e4ceaab",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/connection.ts": "b325d5b720af8132cd81d6d8b6a2e61936631b36d0dd08907d5421107bf50723",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/errors.ts": "bc8f7091cb9f36cdd31229660e0139350b02c26851e3ac69d592c066745feb27",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/executor.ts": "03e5f43df4e0c9c62b0e1be778811d45b6a1966ddf406e21ed5a227af70b7183",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/mod.ts": "20908f005f5c102525ce6aa9261648c95c5f61c6cf782b2cbb2fce88b1220f69",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/pipeline.ts": "80cc26a881149264d51dd019f1044c4ec9012399eca9f516057dc81c9b439370",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/protocol/_util.ts": "0525f7f444a96b92cd36423abdfe221f8d8de4a018dc5cb6750a428a5fc897c2",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/protocol/command.ts": "b1efd3b62fe5d1230e6d96b5c65ba7de1592a1eda2cc927161e5997a15f404ac",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/protocol/mod.ts": "f2601df31d8adc71785b5d19f6a7e43dfce94adbb6735c4dafc1fb129169d11a",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/protocol/reply.ts": "beac2061b03190bada179aef1a5d92b47a5104d9835e8c7468a55c24812ae9e4",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/protocol/types.ts": "40b0a568cb7fd4dc9107997062584d24e5c6ffa1f21acb6410aa19c92f89e9e1",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/pubsub.ts": "324b87dae0700e4cb350780ce3ae5bc02780f79f3de35e01366b894668b016c6",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/redis.ts": "a5c2cf8c72e7c92c9c8c6911f98227062649f6cba966938428c5414200f3aa54",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/stream.ts": "f116d73cfe04590ff9fa8a3c08be8ff85219d902ef2f6929b8c1e88d92a07810",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/vendor/https/deno.land/std/async/deferred.ts": "7391210927917113e04247ef013d800d54831f550e9a0b439244675c56058c55",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/vendor/https/deno.land/std/async/delay.ts": "c7e2604f7cb5ef00d595de8dd600604902d5be03a183b515b3d6d4bbb48e1700",
+    "https://deno.land/x/socket_io@0.2.1/vendor/deno.land/x/redis@v0.27.1/vendor/https/deno.land/std/io/buffer.ts": "8c5f84b7ecf71bc3e12aa299a9fae9e72e495db05281fcdd62006ecd3c5ed3f3",
     "https://deno.land/x/xhr@0.3.0/mod.ts": "094aacd627fd9635cd942053bf8032b5223b909858fa9dc8ffa583752ff63b20",
     "https://dev.jspm.io/debug@4": "10dd5ba3edd978795d30534e1a9e6210e241e1f860e301908f0475d3491a2e7a",
     "https://dev.jspm.io/inherits@2.0": "dac3435abfbb706adba76a0d19a09f17013b3ec975b4f6382e661e0439735dad",

+ 99 - 0
docs/message-system-architecture.md

@@ -0,0 +1,99 @@
+# 消息系统架构设计方案
+
+## 1. 架构图
+```mermaid
+flowchart LR
+    subgraph Admin端
+        A[发送消息] -->|类型转换| B(server/routes_io_messages.ts)
+    end
+    subgraph Server
+        B --> C{消息类型}
+        C -->|ANNOUNCE| D[存入DB+推announce]
+        C -->|PRIVATE| E[存入DB+推user_[id]]
+        C -->|SYSTEM| F[存入DB+推system]
+        D & E & F --> G[Socket推送]
+    end
+    subgraph Mobile端
+        G --> H[多频道订阅]
+        H --> I[按类型处理UI]
+    end
+```
+
+## 2. 关键数据结构
+
+### 消息类型枚举 (client/share/types.ts)
+```typescript
+export enum MessageType {
+  SYSTEM = 'system',    // 系统消息
+  ANNOUNCE = 'announce', // 公告
+  PRIVATE = 'private'   // 私信
+}
+
+export enum MessageStatus {
+  UNREAD = 0,   // 未读
+  READ = 1,     // 已读
+  DELETED = 2   // 已删除
+}
+```
+
+### 消息表结构
+```sql
+CREATE TABLE messages (
+  id SERIAL PRIMARY KEY,
+  title VARCHAR(255) NOT NULL,
+  content TEXT NOT NULL,
+  type ENUM('SYSTEM','ANNOUNCE','PRIVATE') NOT NULL,
+  sender_id INTEGER REFERENCES users(id),
+  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE user_messages (
+  user_id INTEGER REFERENCES users(id),
+  message_id INTEGER REFERENCES messages(id),
+  status ENUM('UNREAD','READ') DEFAULT 'UNREAD',
+  PRIMARY KEY (user_id, message_id)
+);
+```
+
+## 3. 事件流说明
+
+### Socket.IO 事件规范
+| 事件名称 | 方向 | 描述 |
+|---------|------|------|
+| message:subscribe | 客户端→服务端 | 订阅消息频道 |
+| message:unsubscribe | 客户端→服务端 | 取消订阅 |
+| message:send | 客户端→服务端 | 发送消息 |
+| message:received | 服务端→客户端 | 消息接收确认 |
+| message:broadcasted | 服务端→客户端 | 广播新消息 |
+
+### 频道订阅规范
+| 消息类型 | 目标频道 | 订阅方式 |
+|----------|----------|----------|
+| SYSTEM   | system   | socket.join('system') |
+| ANNOUNCE | announce | socket.join('announce') |
+| PRIVATE  | user_[id]| socket.join(`user_${userId}`) |
+
+### 实时推送流程
+1. Admin发送消息 → 服务端接收(message:send)
+2. 服务端处理:
+   - 存储消息到数据库
+   - 根据类型推送:
+     * SYSTEM: io.to('system').emit('message:broadcasted')
+     * ANNOUNCE: io.to('announce').emit('message:broadcasted')
+     * PRIVATE: io.to(`user_${targetId}`).emit('message:broadcasted')
+3. Mobile端:
+   - 初始化时订阅相关频道
+   - 按频道接收处理消息
+
+## 4. 接口定义
+
+### HTTP API
+- GET /api/messages - 获取消息列表
+- POST /api/messages - 发送消息
+- GET /api/messages/unread - 获取未读消息数
+- PUT /api/messages/:id/read - 标记消息为已读
+
+### 权限控制
+- 系统消息: 仅管理员可发送
+- 公告: 管理员和特定角色可发送
+- 私信: 所有用户可发送

+ 5 - 3
server/app.tsx

@@ -4,6 +4,7 @@ import React from 'hono/jsx'
 import type { Context as HonoContext } from 'hono'
 import { serveStatic } from 'hono/deno'
 import { APIClient } from '@d8d-appcontainer/api'
+import { Auth } from '@d8d-appcontainer/auth';
 import debug from "debug"
 import dayjs from 'dayjs';
 import utc from 'dayjs/plugin/utc';
@@ -54,16 +55,17 @@ interface EsmScriptConfig {
 
 // 定义模块参数接口
 interface ModuleParams {
-  apiClient: APIClient
+  apiClient: APIClient,
+  auth: Auth,
   app: Hono
   moduleDir: string
 }
 
-export default function({ apiClient, app, moduleDir }: ModuleParams) {
+export default function({ apiClient, app, moduleDir , auth}: ModuleParams) {
   const honoApp = app
   
   // 创建路由
-  const router = createRouter(apiClient, moduleDir)
+  const router = createRouter(apiClient, moduleDir, auth)
   honoApp.route('/', router)
  
   // 首页路由 - SSR

+ 3 - 33
server/middlewares.ts

@@ -45,11 +45,11 @@ export const withAuth = async (c: HonoContext<{ Variables: Variables }>, next: (
 export type WithAuth = typeof withAuth;
 
 // 环境变量设置中间件
-export const setEnvVariables = (apiClient: APIClient, moduleDir: string) => {
+export const setEnvVariables = (apiClient: APIClient, moduleDir: string, auth: Auth) => {
   return async (c: HonoContext<{ Variables: Variables }>, next: () => Promise<void>) => {
     c.set('apiClient', apiClient)
     c.set('moduleDir', moduleDir)
-    c.set('auth', await initAuth(apiClient))
+    c.set('auth', auth)
     c.set('systemSettings', await initSystemSettings(apiClient))
     await next()
   }
@@ -58,37 +58,7 @@ export const setEnvVariables = (apiClient: APIClient, moduleDir: string) => {
 // CORS中间件
 export const corsMiddleware = cors()
 
-// 初始化Auth实例
-const initAuth = async (apiClient: APIClient) => {
-  try {
-    log.auth('正在初始化Auth实例')
-    
-    const auth = new Auth(apiClient as any, {
-      jwtSecret: Deno.env.get("JWT_SECRET") || 'your-jwt-secret-key',
-      initialUsers: [],
-      storagePrefix: '',
-      userTable: 'users',
-      fieldNames: {
-        id: 'id',
-        username: 'username',
-        password: 'password',
-        phone: 'phone',
-        email: 'email',
-        is_disabled: 'is_disabled',
-        is_deleted: 'is_deleted'
-      },
-      tokenExpiry: 24 * 60 * 60,
-      refreshTokenExpiry: 7 * 24 * 60 * 60
-    })
-    
-    log.auth('Auth实例初始化完成')
-    return auth
-    
-  } catch (error) {
-    log.auth('Auth初始化失败:', error)
-    throw error
-  }
-}
+
 
 // 初始化系统设置
 const initSystemSettings = async (apiClient: APIClient) => {

+ 3 - 2
server/router.ts

@@ -2,6 +2,7 @@
 import { Hono } from 'hono'
 import { corsMiddleware, withAuth, setEnvVariables } from './middlewares.ts'
 import type { APIClient } from '@d8d-appcontainer/api'
+import { Auth } from '@d8d-appcontainer/auth';
 
 // 导入路由模块
 import { createAuthRoutes } from "./routes_auth.ts"
@@ -20,7 +21,7 @@ import { createHomeRoutes } from "./routes_home.ts"
 import { createClassRoomRoutes } from "./routes_classroom.ts"
 import { createStockRoutes } from "./routes_stock.ts";
 
-export function createRouter(apiClient: APIClient, moduleDir: string) {
+export function createRouter(apiClient: APIClient, moduleDir: string , auth: Auth) {
   const router = new Hono()
 
   // 添加CORS中间件
@@ -30,7 +31,7 @@ export function createRouter(apiClient: APIClient, moduleDir: string) {
   const api = new Hono()
   
   // 设置环境变量
-  api.use('*', setEnvVariables(apiClient, moduleDir))
+  api.use('*', setEnvVariables(apiClient, moduleDir, auth))
 
   // 注册所有路由
   api.route('/auth', createAuthRoutes(withAuth))

+ 73 - 0
server/router_io.ts

@@ -0,0 +1,73 @@
+import { Socket, Server } from "socket.io";
+import { Auth } from '@d8d-appcontainer/auth';
+import type { User as AuthUser } from '@d8d-appcontainer/auth';
+import { APIClient } from '@d8d-appcontainer/api';
+import { setupMessageEvents } from './routes_io_messages.ts';
+import debug from "debug";
+
+const log = debug('socketio:auth');
+
+interface SetupSocketIOProps {
+  io: Server, auth: Auth, apiClient: APIClient
+}
+
+export interface SocketWithUser extends Socket {
+  user?: AuthUser;
+}
+
+// 定义自定义上下文类型
+export interface Variables {
+  socket: SocketWithUser
+  auth: Auth
+  user: AuthUser
+  apiClient: APIClient
+  // moduleDir: string
+  // systemSettings?: SystemSettingRecord
+}
+
+export function setupSocketIO({ io, auth, apiClient }:SetupSocketIOProps) {
+  // Socket.IO认证中间件
+  io.use(async (socket: SocketWithUser) => {
+    try {
+      const token = socket.handshake.query.get('socket_token');
+      if (!token) {
+        log(`未提供token,拒绝连接: ${socket.id}`);
+        throw new Error('未授权')
+      }
+
+      const userData = await auth.verifyToken(token);
+      if (!userData) {
+        log(`无效token,拒绝连接: ${socket.id}`);
+        throw new Error('无效凭证')
+      }
+
+      socket.user = userData;
+      log(`认证成功: ${socket.id} 用户: ${userData.username}`);
+    } catch (error) {
+      log(`认证错误: ${socket.id}`, error);
+    }
+  });
+
+  io.on("connection", (socket: SocketWithUser) => {
+    if (!socket.user) {
+      socket.disconnect(true);
+      return;
+    }
+
+    console.log(`socket ${socket.id} 已连接,用户: ${socket.user.username}`);
+
+    socket.on("disconnect", (reason) => {
+      console.log(`socket ${socket.id} 断开连接,原因: ${reason}`);
+    });
+
+    const context: Variables = {
+      socket,
+      auth,
+      apiClient,
+      user: socket.user,
+    }
+
+    // 初始化消息路由
+    setupMessageEvents(context);
+  });
+}

+ 324 - 0
server/routes_io_messages.ts

@@ -0,0 +1,324 @@
+import { SocketWithUser , Variables} from './router_io.ts';
+import { MessageType, MessageStatus } from '../client/share/types.ts'
+import { APIClient } from "@d8d-appcontainer/api";
+
+interface MessageSendData {
+  title: string;
+  content: string;
+  type: MessageType;
+  receiver_ids: number[];
+}
+
+interface MessageListData {
+  page?: number;
+  pageSize?: number;
+  type?: MessageType;
+  status?: MessageStatus;
+}
+
+export function setupMessageEvents({ socket , apiClient }:Variables) {
+  // 订阅频道
+  socket.on('message:subscribe', (channel: string) => {
+    try {
+      socket.join(channel);
+      socket.emit('message:subscribed', {
+        message: `成功订阅频道: ${channel}`,
+        channel
+      });
+    } catch (error) {
+      console.error('订阅频道失败:', error);
+      socket.emit('error', '订阅频道失败');
+    }
+  });
+
+  // 取消订阅
+  socket.on('message:unsubscribe', (channel: string) => {
+    try {
+      socket.leave(channel);
+      socket.emit('message:unsubscribed', {
+        message: `已取消订阅频道: ${channel}`,
+        channel
+      });
+    } catch (error) {
+      console.error('取消订阅失败:', error);
+      socket.emit('error', '取消订阅失败');
+    }
+  });
+
+  // 广播消息
+  socket.on('message:broadcast', async (data: {
+    channel?: string;
+    title: string;
+    content: string;
+    type: MessageType;
+  }) => {
+    try {
+      const { channel, title, content, type } = data;
+      const user = socket.user;
+      if (!user) {
+        socket.emit('error', '未授权访问');
+        return;
+      }
+
+      // 创建广播消息
+      const [messageId] = await apiClient.database.table('messages').insert({
+        title,
+        content,
+        type,
+        sender_id: user.id,
+        sender_name: user.username,
+        is_broadcast: 1,
+        created_at: apiClient.database.fn.now(),
+        updated_at: apiClient.database.fn.now()
+      });
+
+      // 广播到所有客户端或特定频道
+      const broadcastTarget = channel ? socket.to(channel) : socket.broadcast;
+      broadcastTarget.emit('message:broadcasted', {
+        id: messageId,
+        title,
+        content,
+        type,
+        sender_id: user.id,
+        sender_name: user.username,
+        created_at: new Date().toISOString()
+      });
+
+      socket.emit('message:broadcasted', {
+        message: '广播消息发送成功',
+        data: { id: messageId }
+      });
+    } catch (error) {
+      console.error('广播消息失败:', error);
+      socket.emit('error', '广播消息失败');
+    }
+  });
+
+  // 发送消息
+  socket.on('message:send', async (data: MessageSendData) => {
+    try {
+      const { title, content, type, receiver_ids } = data;
+
+      if (!title || !content || !type || !receiver_ids?.length) {
+        socket.emit('error', '缺少必要参数');
+        return;
+      }
+
+      const user = socket.user;
+      if (!user) {
+        socket.emit('error', '未授权访问');
+        return;
+      }
+      
+      // 创建消息
+      const [messageId] = await apiClient.database.table('messages').insert({
+        title,
+        content,
+        type,
+        sender_id: user.id,
+        sender_name: user.username,
+        created_at: apiClient.database.fn.now(),
+        updated_at: apiClient.database.fn.now()
+      });
+
+      // 关联用户消息
+      const userMessages = receiver_ids.map((userId: number) => ({
+        user_id: userId,
+        message_id: messageId,
+        status: MessageStatus.UNREAD,
+        created_at: apiClient.database.fn.now(),
+        updated_at: apiClient.database.fn.now()
+      }));
+
+      await apiClient.database.table('user_messages').insert(userMessages);
+
+      // 根据消息类型推送到不同频道
+      const messageData = {
+        id: messageId,
+        title,
+        content,
+        type,
+        sender_id: user.id,
+        sender_name: user.username,
+        created_at: new Date().toISOString()
+      };
+
+      if (type === MessageType.SYSTEM) {
+        socket.to('system').emit('message:received', messageData);
+      } else if (type === MessageType.ANNOUNCE) {
+        socket.to('announce').emit('message:received', messageData);
+      } else if (type === MessageType.PRIVATE) {
+        receiver_ids.forEach(userId => {
+          socket.to(`user_${userId}`).emit('message:received', messageData);
+        });
+      }
+
+      socket.emit('message:sent', {
+        message: '消息发送成功',
+        data: { id: messageId }
+      });
+    } catch (error) {
+      console.error('发送消息失败:', error);
+      socket.emit('error', '发送消息失败');
+    }
+  });
+  
+  // 获取消息列表
+  socket.on('message:list', async (data: MessageListData) => {
+    try {
+      const { page = 1, pageSize = 20, type, status } = data;
+      const user = socket.user;
+      if (!user) {
+        socket.emit('error', '未授权访问');
+        return;
+      }
+      
+      const query = apiClient.database.table('user_messages as um')
+        .select('m.*', 'um.status as user_status', 'um.read_at', 'um.id as user_message_id')
+        .leftJoin('messages as m', 'um.message_id', 'm.id')
+        .where('um.user_id', user.id)
+        .where('um.is_deleted', 0)
+        .orderBy('m.created_at', 'desc')
+        .limit(pageSize)
+        .offset((page - 1) * pageSize);
+
+      if (type) query.where('m.type', type);
+      if (status) query.where('um.status', status);
+
+      const countQuery = query.clone();
+      const messages = await query;
+      
+      // 获取总数用于分页
+      const total = await countQuery.count();
+      const totalCount = Number(total);
+      const totalPages = Math.ceil(totalCount / pageSize);
+
+      socket.emit('message:list', {
+        data: messages,
+        pagination: {
+          total: totalCount,
+          current: page,
+          pageSize,
+          totalPages
+        }
+      });
+    } catch (error) {
+      console.error('获取消息列表失败:', error);
+      socket.emit('error', '获取消息列表失败');
+    }
+  });
+
+  // 获取消息详情
+  socket.on('message:detail', async (messageId: number) => {
+    try {
+      const user = socket.user;
+      if (!user) {
+        socket.emit('error', '未授权访问');
+        return;
+      }
+      
+      const message = await apiClient.database.table('user_messages as um')
+        .select('m.*', 'um.status as user_status', 'um.read_at')
+        .leftJoin('messages as m', 'um.message_id', 'm.id')
+        .where('um.user_id', user.id)
+        .where('um.message_id', messageId)
+        .first();
+
+      if (!message) {
+        socket.emit('error', '消息不存在或无权访问');
+        return;
+      }
+
+      // 标记为已读
+      if (message.user_status === MessageStatus.UNREAD) {
+        await apiClient.database.table('user_messages')
+          .where('user_id', user.id)
+          .where('message_id', messageId)
+          .update({
+            status: MessageStatus.READ,
+            read_at: apiClient.database.fn.now(),
+            updated_at: apiClient.database.fn.now()
+          });
+      }
+
+      socket.emit('message:detail', {
+        message: '获取消息成功',
+        data: message
+      });
+    } catch (error) {
+      console.error('获取消息详情失败:', error);
+      socket.emit('error', '获取消息详情失败');
+    }
+  });
+
+  // 删除消息
+  socket.on('message:delete', async (messageId: number) => {
+    try {
+      const user = socket.user;
+      if (!user) {
+        socket.emit('error', '未授权访问');
+        return;
+      }
+      
+      await apiClient.database.table('user_messages')
+        .where('user_id', user.id)
+        .where('message_id', messageId)
+        .update({
+          is_deleted: 1,
+          updated_at: apiClient.database.fn.now()
+        });
+
+      socket.emit('message:deleted', { message: '消息已删除' });
+    } catch (error) {
+      console.error('删除消息失败:', error);
+      socket.emit('error', '删除消息失败');
+    }
+  });
+
+  // 获取未读消息数
+  socket.on('message:count', async () => {
+    try {
+      const user = socket.user;
+      if (!user) {
+        socket.emit('error', '未授权访问');
+        return;
+      }
+      
+      const count = await apiClient.database.table('user_messages')
+        .where('user_id', user.id)
+        .where('status', MessageStatus.UNREAD)
+        .where('is_deleted', 0)
+        .count();
+
+      socket.emit('message:count', { count: Number(count) });
+    } catch (error) {
+      console.error('获取未读消息数失败:', error);
+      socket.emit('error', '获取未读消息数失败');
+    }
+  });
+
+  // 标记消息为已读
+  socket.on('message:read', async (messageId: number) => {
+    try {
+      const user = socket.user;
+      if (!user) {
+        socket.emit('error', '未授权访问');
+        return;
+      }
+      
+      await apiClient.database.table('user_messages')
+        .where('user_id', user.id)
+        .where('message_id', messageId)
+        .update({
+          status: MessageStatus.READ,
+          read_at: apiClient.database.fn.now(),
+          updated_at: apiClient.database.fn.now()
+        });
+
+      socket.emit('message:read', { message: '消息已标记为已读' });
+    } catch (error) {
+      console.error('标记消息为已读失败:', error);
+      socket.emit('error', '标记消息为已读失败');
+    }
+  });
+}

+ 74 - 26
server/run_app.ts

@@ -1,8 +1,12 @@
 // 导入所需模块
 import { Hono } from 'hono'
 import { APIClient } from '@d8d-appcontainer/api'
+import { Auth } from '@d8d-appcontainer/auth';
 import debug from "debug"
 import { cors } from 'hono/cors'
+import { Server } from "socket.io"
+import httpServer from './app.tsx'
+import { setupSocketIO } from './router_io.ts'
 
 // 初始化debug实例
 const log = {
@@ -36,6 +40,46 @@ const getApiClient = async (workspaceKey: string, serverUrl?: string) => {
     throw error
   }
 }
+// 初始化Auth实例
+const initAuth = async (apiClient: APIClient) => {
+  try {
+    log.auth('正在初始化Auth实例')
+    
+    const auth = new Auth(apiClient as any, {
+      jwtSecret: Deno.env.get("JWT_SECRET") || 'your-jwt-secret-key',
+      initialUsers: [],
+      storagePrefix: '',
+      userTable: 'users',
+      fieldNames: {
+        id: 'id',
+        username: 'username',
+        password: 'password',
+        phone: 'phone',
+        email: 'email',
+        is_disabled: 'is_disabled',
+        is_deleted: 'is_deleted'
+      },
+      tokenExpiry: 24 * 60 * 60,
+      refreshTokenExpiry: 7 * 24 * 60 * 60
+    })
+    
+    log.auth('Auth实例初始化完成')
+    return auth
+    
+  } catch (error) {
+    log.auth('Auth初始化失败:', error)
+    throw error
+  }
+}
+
+// 初始化API Client
+// 注意:WORKSPACE_KEY 需要在 多八多(www.d8d.fun) 平台注册并开通工作空间后获取
+const workspaceKey = Deno.env.get('WORKSPACE_KEY') || ''
+if (!workspaceKey) {
+  console.warn('未设置WORKSPACE_KEY,请前往 多八多(www.d8d.fun) 注册并开通工作空间以获取密钥')
+}
+const apiClient = await getApiClient(workspaceKey)
+const auth = await initAuth(apiClient);
 
 // 创建Hono应用实例
 const app = new Hono()
@@ -43,39 +87,43 @@ const app = new Hono()
 // 注册CORS中间件
 app.use('/*', cors())
 
+// 创建Socket.IO实例
+const io = new Server({
+  cors: {
+    origin: "*",
+    methods: ["GET", "POST"],
+    credentials: true
+  }
+})
+
+setupSocketIO({io, auth, apiClient});
+
 // 动态加载并运行模板
-const runTemplate = async () => {
+const runTemplate = () => {
   try {
     // 创建基础app实例
     const moduleApp = new Hono()
-    
-    // 初始化API Client
-    // 注意:WORKSPACE_KEY 需要在 多八多(www.d8d.fun) 平台注册并开通工作空间后获取
-    const workspaceKey = Deno.env.get('WORKSPACE_KEY') || ''
-    if (!workspaceKey) {
-      console.warn('未设置WORKSPACE_KEY,请前往 多八多(www.d8d.fun) 注册并开通工作空间以获取密钥')
-    }
-    const apiClient = await getApiClient(workspaceKey)
-    
-    // 导入模板主模块
-    const templateModule = await import('./app.tsx')
-    
-    if (templateModule.default) {
-      // 传入必要参数并初始化应用
-      const appInstance = templateModule.default({
-        apiClient: apiClient,
-        app: moduleApp,
-        moduleDir: './'
-      })
-      
-      // 启动服务器
-      Deno.serve({ port: 8080 }, appInstance.fetch)
-      console.log('应用已启动,监听端口: 8080')
-    }
+
+    // 传入必要参数并初始化应用
+    const appInstance = httpServer({
+      apiClient: apiClient,
+      app: moduleApp,
+      moduleDir: './',
+      auth
+    }) 
+    // 启动服务器
+    Deno.serve({ 
+      handler: io.handler(async (req) => {
+        return await appInstance.fetch(req) || new Response(null, { status: 404 });
+      }),
+      port: 8080 
+    })
+
+    console.log('应用已启动,监听端口: 8080')
   } catch (error) {
     console.error('模板加载失败:', error)
   }
 }
 
 // 执行模板
-runTemplate()
+runTemplate()