chore: move tailchat-website into tailchat

pull/49/head
moonrailgun 3 years ago
parent 7b6d00363e
commit 8025cad32e

@ -0,0 +1,53 @@
name: "Deployment Website"
on:
push:
branches:
- master
paths:
- "./website/**"
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
defaults:
run:
working-directory: website
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Cache pnpm modules
uses: actions/cache@v2
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- uses: pnpm/action-setup@v2.0.1
with:
version: 6.0.2
run_install: true
- name: Install Packages
run: pnpm install
- name: Build page
run: pnpm build
# - name: Deploy to gh-pages
# uses: peaceiris/actions-gh-pages@v3
# with:
# github_token: ${{ secrets.GITHUB_TOKEN }}
# publish_dir: ./website/build
- name: Deploy to Vercel
uses: amondnet/vercel-action@master
env:
VERSION: ${{ env.GITHUB_SHA }}
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID}}
vercel-project-id: ${{ secrets.PROJECT_ID}}
working-directory: ./
vercel-args: '--prod'

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Language" content="zh-CN" />
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style>
body {
text-align: center;
}
.video-container {
text-align: center;
padding: 40px 0;
}
.video-iframe {
width: 640px;
height: 360px;
}
@media screen and (max-width: 966px) {
.video-iframe {
width: 360px;
height: 240px;
}
}
</style>
</head>
<body>
<div>
<h1>
Tailchat Nightly
</h1>
<h3>
Tailchat 演示环境
</h3>
<p>如果有任何反馈欢迎直接在本群组 @moonrailgun 或者发送邮件到 <a href="mailto:moonrailgun@gmail.com">moonrailgun@gmail.com</a></p>
<p>
官方文档: <a href="https://tailchat.msgbyte.com/">https://tailchat.msgbyte.com/</a>
</p>
<p>
开源地址: <a href="https://github.com/msgbyte/tailchat">https://github.com/msgbyte/tailchat</a>
</p>
<div class="video-container">
<h2>演示视频</h2>
<iframe class="video-iframe" src="https://player.bilibili.com/player.html?aid=340398093&bvid=BV1394y1Z76n&cid=568332564&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
</div>
</div>
</body>
</html>

20
website/.gitignore vendored

@ -0,0 +1,20 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

@ -0,0 +1,2 @@
# https://npmmirror.com/
registry = https://registry.npmmirror.com

@ -0,0 +1,3 @@
# Tailchat Document
一个开放的即时通讯聊天应用

@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

@ -0,0 +1,161 @@
---
title: Tailchat —— 插件化的开源聊天平台
description: Tailchat 是一款易拓展、方便使用,并且完全免费开源的聊天平台
authors: moonrailgun
slug: intro
image: /img/logo.svg
keywords:
- tailchat
- 介绍
- 开源
- 开源聊天工具
tags: [介绍]
---
## 前言 First of all
大家好,我是[moonrailgun](https://github.com/moonrailgun),今天为大家介绍的是我个人开发的开源插件式聊天软件: [Tailchat](https://github.com/msgbyte/tailchat)
Tailchat 是一款易拓展、方便使用,并且完全免费开源的聊天平台。在部分思想上借鉴了 [Discord](https://discord.com/) 并在此致敬!
## 动机 Motivation
曾经痴迷于 TRPG, 并花了4年的业余时间做了人生第一款聊天软件 [TRPG Engine](https://github.com/TRPGEngine/Client)。但是受限于题材方面的问题,使用的人虽然一直有,但是并没有太多。
而且因为开发之初受到经验的限制,虽然当初经过了很多次重构,但是仍然背负上了很多的技术债务。而且因为当初没有做好分布式架构的设计,因此在面对实际的单机业务性能瓶颈时也没有非常好的解决方案,导致在高峰期只能让用户处于相对慢的系统响应中。
因此我将在 [TRPG Engine](https://github.com/TRPGEngine/Client) 的开发中遇到的问题收集起来,并重新整理了我的业务需求,开发出了新的继承者 [Tailchat](https://github.com/msgbyte/tailchat)。
因为最开始首先于小众的业务, [Tailchat](https://github.com/msgbyte/tailchat) 的设计之初就做好了易于拓展的准备。定位是除了核心的聊天场景以外,其他的业务需要都是由插件提供的。甚至是聊天的富文本渲染方式、消息的通知也是由插件提供的。作为使用者/开发者,能够方便得为 [Tailchat](https://github.com/msgbyte/tailchat) 拓展新的能力,或者将已有的能力集成到 [Tailchat](https://github.com/msgbyte/tailchat) 中
[Tailchat](https://github.com/msgbyte/tailchat) 名字来源于其logo是两只猫尾巴纠缠在一起的形象表示猫与猫之间最直接的沟通方式。
## 功能 Feature
### 登录页
![](/img/blog/intro/1.png)
登录页除了支持正常的 **登录**、**注册**,为降低使用门槛还允许先进行 **游客访问**,无需注册直接使用。同时还支持 **忘记密码** 功能。向通过用户发送邮件来帮助用户重置密码
> **NOTICE**: 忘记密码功能需要管理员配置SMTP服务
此处以访客为例,创建一个名为 `猫之使徒` 的访客:
![](/img/blog/intro/2.png)
进入主页面后如下显示:
![](/img/blog/intro/3.png)
可以通过点击最上面的认领账号来补充用户信息以方便下次登录
### 添加好友
切换到 `添加好友` 标签页可以看到系统为您生成的4位唯一标识:
![](/img/blog/intro/4.png)
在本例中的唯一标识是`猫之使徒#1216`,您可以通过将这段字符串发送给好友来添加好友。
![](/img/blog/intro/5.png)
### 插件中心
[Tailchat](https://github.com/msgbyte/tailchat) 设计之初就提供了强大的插件系统,通过插件系统能极大拓展[Tailchat](https://github.com/msgbyte/tailchat)自身的能力。
![](/img/blog/intro/6.png)
> NOTICE: 插件本身设计是运行在 可信执行环境(TEE) 中, 请不要随意通过手动安装的方式安装不明来源的第三方插件
### 个人设置
![](/img/blog/intro/7.png)
用户可以在左下方的"···"中打开个人设置。可以自定义头像,修改名称,修改密码等操作。
![](/img/blog/intro/8.png)
通过剪裁工具可以很方便的选出自己想要的部分
![](/img/blog/intro/9.png)
### 系统设置
在系统设置中可以修改界面语言和界面主题。
主题除了 [Tailchat](https://github.com/msgbyte/tailchat) 自带的`暗色主题`和`亮色主题`以及根据当前系统设置自动切换的`自动`以外均为插件提供
![](/img/blog/intro/10.png)
> 目前 Tailchat 支持的语言仅为 `简体中文``英语`。欢迎有能力的开发者帮助 Tailchat 进行国际化翻译的工作
### 群组聊天
![](/img/blog/intro/11.png)
点击左上角导航栏的绿色加号即可创建群组
![](/img/blog/intro/12.png)
预设了两个布局模板,直接选择`默认群组`
![](/img/blog/intro/13.png)
随意取个名字,点击创建。系统则会创建一个群组并自动跳转到该群组
界面布局如下:
![](/img/blog/intro/14.png)
![](/img/blog/intro/15.png)
输入框支持 `@` 群组成员:
![](/img/blog/intro/16.png)
### 群组设置
点击左上角的标题栏可以点开群组详情页
![](/img/blog/intro/17.png)
点击查看详情可以进入到群组的详情页
![](/img/blog/intro/18.png)
在此处可以:
- 修改群组头像
- 修改群组名称
- 管理群组面板
#### 面板管理
![](/img/blog/intro/19.png)
创建面板可以直接创建一个面板,内置的面板类型有:
- 聊天频道
- 面板分组
- 网页面板
网页面板可以直接输入一个网址,可以将自己喜好的网址直接集成到 Tailchat 的群组侧边栏
![](/img/blog/intro/20.png)
比如我们插入一个访问 Bilibili 的网页面板,则效果如下:
![](/img/blog/intro/21.png)
![](/img/blog/intro/22.png)
> NOTICE: 因为浏览器的安全策略问题,如果当前 Tailchat 使用了https协议那么只能支持嵌入 https 协议的网页
> NOTICE: 因为浏览器的安全策略问题嵌入的网址需要允许被使用iframe嵌入
## 相关连接
开源地址:
- [https://github.com/msgbyte/tailchat](https://github.com/msgbyte/tailchat)
- [https://github.com/msgbyte/tailchat-server](https://github.com/msgbyte/tailchat-server)

@ -0,0 +1,6 @@
moonrailgun:
name: moonrailgun
title: Tailchat 开发者
url: https://github.com/moonrailgun
image_url: https://avatars.githubusercontent.com/u/6964737?v=4
email: moonrailgun@gmail.com

@ -0,0 +1,12 @@
---
sidebar_position: 3
title: 架构
---
## 服务端架构
![](/img/architecture/backend.excalidraw.svg)
## 插件机制架构
![](/img/architecture/plugin.excalidraw.svg)

@ -0,0 +1,4 @@
{
"label": "命令行终端",
"position": 30
}

@ -0,0 +1,25 @@
---
sidebar_position: 1
title: 命令行工具 tailchat-cli
---
## 安装
```bash
npm install -g tailchat-cli@latest # 安装与更新同一命令
```
安装成功后输入`tailchat` 后返回如下
```bash
tailchat <command>
Commands:
tailchat create [template] 创建 Tailchat 项目代码
tailchat connect 连接到 Tailchat 节点网络
tailchat declaration [source] Tailchat 插件类型声明
Options:
--version Show version number [boolean]
-h, --help Show help [boolean]
```

@ -0,0 +1,9 @@
---
sidebar_position: 100
title: 演示群组
---
这里有一些用于演示Tailchat功能的群组
- [Tailchat Nightly](https://nightly.paw.msgbyte.com/invite/8Jfm1dWb)
- [原神](https://nightly.paw.msgbyte.com/invite/GFFzfD5H)

@ -0,0 +1,4 @@
{
"label": "部署",
"position": 10
}

@ -0,0 +1,54 @@
---
sidebar_position: 99
title: 开发环境
---
对于开发环境的搭建tailchat 提供了非常简单快捷的方式
## 后端
### 使用Docker快速搭建依赖环境
mongodb
```bash
docker run -d --name mongo -p 27017:27017 mongo:4
```
redis
```bash
docker run -d --name redis -p 6379:6379 redis
```
minio
```bash
docker run -d \
-p 19000:9000 \
-p 19001:9001 \
--name minio \
-e "MINIO_ROOT_USER=tailchat" \
-e "MINIO_ROOT_PASSWORD=com.msgbyte.tailchat" \
minio/minio server /data --console-address ":9001"
```
### 启动开发服务器
```bash
cp .env.example .env
vim .env
```
编辑`.env`的配置为自己相关的上下文
```bash
pnpm install # 安装环境变量
pnpm dev # 启动开发服务器
```
## 前端
```bash
cd web
pnpm install # 安装依赖
pnpm plugins:all # 编译插件
pnpm dev # 进入开发模式
```

@ -0,0 +1,84 @@
---
sidebar_position: 4
title: Docker Compose 部署服务端
---
## 安装环境
### Docker / Docker Compose
首先需要确保有 `Docker / Docker Compose` 环境
安装方式如下:
- [Docker](https://docs.docker.com/engine/install/)
- [Docker Compose](https://docs.docker.com/compose/install/)
### node 环境
- 从[官网下载](https://nodejs.org/en/download/)
- 或者使用[nvm](https://github.com/nvm-sh/nvm)
#### 安装pnpm
`pnpm` 是一个`nodejs`的包管理工具, 是`npm`的替代品, 为了确保能有与开发者一样依赖环境强烈建议你使用pnpm作为后续的包管理工具
```bash
npm install -g pnpm
```
## Clone 项目
将项目从远程下载到本地:
```bash
mkdir msgbyte && cd msgbyte
git clone https://github.com/msgbyte/tailchat.git # clone 客户端
git clone https://github.com/msgbyte/tailchat-server.git # clone 服务端
```
## 编译项目
#### 前端项目
```bash
cd tailchat/web
pnpm install # 安装依赖
export SERVICE_URL=http://127.0.0.1:11000 # 配置服务端地址这里的127.0.0.1 可以替换为任何网页可以访问到的服务端地址
pnpm build # 构建项目
```
构建完毕后会生成一个`tailchat/web/dist`目录,将该目录托管到任意网页托管服务器即可(如使用`http-server`进行静态代理或者直接上传到`oss`)
> NOTICE: 因为webpack编译需要比较大的内存资源占用在服务器资源不足的场合建议使用本地编译完毕以后上传到服务端
#### 使用docker-compose构建服务端
> 在启动前需要检查代码环境变量
修改 `docker-compose.env` 文件的配置,以下内容推荐修改:
- `API_URL` 对外可访问的url地址用于文件服务访问
- `SECRET` 服务端加密秘钥用于生成Token. 默认为 `tailchat`
```bash
cd tailchat-server
docker-compose build
docker-compose up -d
```
*在`docker-compose.env`文件中提供了部分环境变量可供配置。*
`tailchat` 的`docker-compose.yml`配置默认提供了如下配置:
- `mongodb`: 持久化数据库
- `redis`: KV数据库与消息中转服务
- `minio`: 分布式文件服务
其中持久化文件(数据库, 文件存储)通过 `docker volume` 统一管理:
```
docker volume ls | grep "tailchat-server"
```

@ -0,0 +1,39 @@
---
sidebar_position: 7
title: 环境变量
---
## 前端编译环境变量
| 变量名 | 默认值 | 描述 |
| ----- | ------ | --- |
| SERVICE_URL | http://127.0.0.1:11000 | 后端服务地址 |
## 后端环境变量
| 变量名 | 默认值 | 描述 |
| ----- | ------ | --- |
| PORT | 11000 | 网关服务端口号 |
| SECRET | tailchat | 加密秘钥, 用于JWT |
| API_URL | http://127.0.0.1:11000 | 对外可访问的url地址用于文件服务访问 |
| MONGO_URL | - | 数据库服务地址 |
| REDIS_URL | - | Redis服务地址 |
| MINIO_URL | - | 文件服务地址(minio) |
| MINIO_USER | - | 文件服务用户名 |
| MINIO_PASS | - | 文件服务密码 |
| MINIO_BUCKET_NAME | tailchat | 文件服务存储桶名 |
| SMTP_SENDER | - | 邮件服务发件人(示例: `"Tailchat" example@163.com`) |
| SMTP_URI | - | 邮件服务连接地址(示例: `smtp://username:password@smtp.example.com/?pool=true`) |
| FILE_LIMIT | 1048576 | 文件/图片上传的大小限制默认为1m请输入数字 |
> 部分环境变量示例可见: https://github.com/msgbyte/tailchat-server/blob/master/.env.example
### 使用文件进行配置环境变量
如果是本地方式启动,请复制 `.env.example``.env` 然后进行编辑
```bash
mv .env.example .env
vi .env
```
如果是 `docker-compose` 启动,可以直接编辑 `docker-compose.env`, 改动后直接使用 `docker-compose up -d` 即可生效

@ -0,0 +1,57 @@
---
sidebar_position: 2
title: 安装docker环境
---
> 因为 `Tailchat` 的环境对于初学者来说有一些些复杂,因此提供了 `docker` 为主的一键环境搭建配置。但是对于`docker`不熟的同学来说可能`docker`本身也是一种复杂度。
> 因此为了方便大家可以快速搭建 `Tailchat`,提供了本文作为引导。对于 `docker` 有一定了解的同学可以跳过本篇
> 本文以 `linux centos` 为例,目标是方便大家直接在服务器上部署。对于想要在其他系统(`windows`, `mac`) 使用的同学可以参考官方文档进行`docker`的安装
## 安装docker与docker compose
官方文档: [https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/)
```bash
# 如果之前有安装过docker可以执行以下命令删除旧的
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
```
```bash
sudo yum install -y yum-utils # yum-utils 提供了 yum-config-manager 命令
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
```
<!-- 安装docker 与 docker-compose 插件 -->
```bash
sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
```
*PS: `docker-compose-plugin`提供了`docker compose`命令,用法同`docker-compose`*
> 如果`docker ps`显示守护进程没有启动(Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?)的话可以执行以下命令启动: `sudo systemctl start docker`
## 单独安装 docker-compose
如果购买的服务器已经预装了docker, 想要单独安装docker-compose的话可以看本节内容:
官方文档: [https://docs.docker.com/compose/install/](https://docs.docker.com/compose/install/)
```bash
curl -SL https://github.com/docker/compose/releases/download/v2.4.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose # 下载二进制文件
sudo chmod +x /usr/local/bin/docker-compose # 给予执行权限
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose # 软链接到path, 可以直接调用
docker-compose --version # 该行命令返回版本号则成功安装
```

@ -0,0 +1,152 @@
---
sidebar_position: 1
title: 快速开始
---
<iframe src="//player.bilibili.com/player.html?aid=853731563&bvid=BV1aL4y1c7QM&cid=710880091&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" />
[在B站观看](https://www.bilibili.com/video/BV1aL4y1c7QM)
## 前端代码
拉取前端源码:
```bash
git clone https://github.com/msgbyte/tailchat
cd tailchat
```
### 方法一: 使用预编译好的镜像 使用docker-compose 一键启动
请确保已经安装了:
- docker
- docker-compose
*如果不会安装docker可以查看 [安装教程](./install-docker.md) *
```bash
docker pull moonrailgun/tailchat:latest
docker tag moonrailgun/tailchat:latest tailchat-web
SERVICE_URL=http://[Server IP]:11000 docker-compose up -d
```
访问 `http://[Server IP]:11011` 即可访问到Tailchat的前端页面
**`[Server IP]`请换成服务端的ip或者绑定的域名, 11000为服务端默认端口号**
**如果有条件的建议使用https协议在此不进行赘述**
### 方法二: 使用 docker-compose 一键编译并部署
请确保已经安装了:
- docker
- docker-compose
*如果不会安装docker可以查看 [安装教程](./install-docker.md) *
```bash
docker-compose build
SERVICE_URL=http://[Server IP]:11000 docker-compose up -d
```
访问 `http://[Server IP]:11011` 即可访问到Tailchat的前端页面
**`[Server IP]`请换成服务端的ip或者绑定的域名, 11000为服务端默认端口号**
**如果有条件的建议使用https协议在此不进行赘述**
### 方法三: 手动编译
**请确保安装了node环境建议node版本大于 16.x**
编译服务依赖 `pnpm` 进行依赖管理
> pnpm 是 npm 的替代品, 更多信息可见 [https://pnpm.io/](https://pnpm.io/)
```bash
npm install -g pnpm # 如果在此之前没有安装过pnpm
pnpm install
cd web # 切换到web目录
```
创建/修改 `.env` 设置环境变量
```ini
SERVICE_URL=http://127.0.0.1:11000
```
环境变量:
- `SERVICE_URL`: 后端服务的地址
- `PORT`: 前端开发环境的端口(`pnpm dev`)
编译代码
```
pnpm build
```
使用任意http代理 `web/dist` 目录即可注意需要支持spa的fallback机制
- 使用 `http-server-spa` 进行前端文件代理: `npx http-server-spa ./web/dist index.html 11011`
- `11011``Tailchat` 的默认端口号,可以改成任意想要的端口
## 后端服务
拉取后端源码:
```bash
git clone https://github.com/msgbyte/tailchat-server
cd tailchat-server
```
### 单节点部署
#### 方法一: docker-compose 拉取预编译好的镜像并部署 (推荐)
请确保已经安装了:
- docker
- docker-compose
*如果不会安装docker可以查看 [安装教程](./install-docker.md) *
修改 `docker-compose.env` 中的 `API_URL` 配置将其改为服务端可访问的url
在项目根目录下执行
```bash
docker pull moonrailgun/tailchat-server:latest
docker tag moonrailgun/tailchat-server:latest tailchat-server
docker-compose up -d
```
#### 方法二: docker-compose 一键构建并部署
请确保已经安装了:
- docker
- docker-compose
*如果不会安装docker可以查看 [安装教程](./install-docker.md) *
修改 `docker-compose.env` 中的 `API_URL` 配置将其改为服务端可访问的url
在项目根目录下执行
```bash
docker-compose build # 需要编译
docker-compose up -d
```
### k8s集群部署
TODO
### 服务端插件安装方式
安装所有插件
```
pnpm plugin:install all
```
安装单个插件
```
pnpm plugin:install com.msgbyte.tasks
```

@ -0,0 +1,20 @@
---
sidebar_position: 10
title: 常见问题
---
如果开放平台部署在代理之后,如果出现访问 `/open/.well-known/openid-configuration` 结果的json中endpoint不正确的情况请尝试修改代理的配置。
如nginx:
```
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:11000;
proxy_redirect off;
}
```

@ -0,0 +1,75 @@
---
sidebar_position: 1
title: 概述
---
`Tailchat` 是一款插件化易拓展的开源 IM 应用。可拓展架构赋予 `Tailchat` 无限可能性。
前端微内核架构 + 后端微服务架构 使得 `Tailchat` 能够驾驭任何定制化/私有化的场景
## 特性
- 完整的即时通讯基础能力
- 插件化架构的赋予的自由拓展能力
- 微服务架构赋予的水平拓展能力
## 技术栈
- 前端
- `React`
- `Redux`
- `MiniStar`
- `tailwindcss`
- `iconify`
- 后端
- `Nodejs`
- `Socket.io`
- `koa`
- `moleculer`
## 功能列表
- 用户管理
- 基于4位数字标识(战网like)的用户名系统
- 好友管理
- 聊天系统
- 私聊
- 群聊
- 富文本消息
- 图片
- 链接
- 提及(@)
- 代码
- 插件系统
- 前端插件系统(基于`ministar`的微内核架构)
- 自定义主题
- 自定义面板
- 自定义操作
- 消息内容转换
- ...
- 后端插件系统(基于`moleculer`的微服务架构)
- 开放平台
- Connect ID
- *(其他正在开发中)*
- 快速跳转
## 截图
#### 插件中心
![](/img/intro/plugins.png)
#### 各类主题
![](/img/intro/theme.png)
#### Github订阅机器人
![](/img/intro/github-bot.png)
## 开源协议
开源协议请主要参考以下文档:
[GNU General Public License 3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)

@ -0,0 +1,4 @@
{
"label": "视频会议",
"position": 60
}

@ -0,0 +1,96 @@
---
sidebar_position: 2
title: 部署视频会议
---
视频会议服务 `Tailchat Meeting` 可以作为一个独立应用单品存在。再本节中将会讲述如何独立部署 `Tailchat Meeting`
以下内容均基于docker环境请确保服务端有 `docker` 最基本程度的环境。
如果还没有安装 `docker` + `docker-compose` 可以查看文档 [安装docker环境](../deployment/install-docker.md)
## 快速部署
```bash
git clone https://github.com/msgbyte/tailchat-meeting --depth=1
```
> NOTE: 接下来会使用docker 的 host 模式进行安装。即`docker-compose` 会自动绑定主机端口
需要服务器预留端口如下:
- swag(服务器网关, nginx 强化版)
- 80
- 443
- tailchat-meeting
- 13001
- 40000-49999(用于RTC服务, 动态占用)
- redis
- 6379
**以上端口均会在宿主机上暴露为了服务器安全着想建议配置合适的防火墙策略仅暴露必要的端口443和40000-49999**
```bash
cd tailchat-meeting/compose
cp docker-compose.env.example docker-compose.env
vi docker-compose.env
```
修改环境变量。
环境变量如下:
```
# 内网IP
MEDIASOUP_IP=
# 公网IP
MEDIASOUP_ANNOUNCED_IP=
# swag相关
URL=
SUBDOMAINS=
TZ=Asia/Shanghai
```
其中
- 如果仅单机部署的话`MEDIASOUP_IP`和`MEDIASOUP_ANNOUNCED_IP`可以均填写服务器公网ip.
- `tailchat-meeting` 基于 webrtc 服务,因此强依赖 https/wss 协议。swag服务可以为您自动申请https证书但是必须得分配一个有效的域名并确保dns指向已经指向到服务器上。
- 更多相关的文档可以查看 [README](https://github.com/linuxserver/docker-letsencrypt/blob/master/README.md)
- 示例配置:
```bash
URL=meeting.example.com # 这里请填入
SUBDOMAINS= # 该参数用于多域名证书申请,可留空
```
修改完毕以后可以直接执行以下命令
```bash
docker-compose up -d
```
`docker compose` 会自动从网络下载镜像并构建`tailchat-meeting`
构建可能需要花费一定时间和资源。特别是构建前端代码,如果使用的小配置的服务器的话请耐心一点等待。
> 实际测试中使用1核2g的小资源服务器耗时参考如下:
> - 下载依赖包: 3分钟
> - 编译前端代码: 5分钟
访问 `https://meeting.example.com` 即可看到`tailchat-meeting`的页面
## 组合使用
对于傻瓜式部署来说只需要一键就可以执行。如果已经有现成的网关服务(比如nginx, caddy等)以及redis实例可以有选择的启动服务。
如:
```bash
docker-compose up tailchat-meeting -d # 仅运行 tailchat-meeting 实例
```
## 使用host模式的原因
`tailchat-meeting` 核心的RTC服务需要在运行时申请端口但是对于docker来说并不能实现这个功能。而预先申请一定范围的端口绑定即会造成无意义端口的浪费也会在启动时瞬间占据大量资源并把系统打死。
**需要注意的是请不要把redis所在的6379端口暴露出去这可能会产生安全隐患。**

@ -0,0 +1,32 @@
---
sidebar_position: 1
title: 概述
---
## Tailchat Meeting
视频会议模块是 `Tailchat` 系列的一套重要组成部分。提供能力如下:
- 语音通信
- 视频会话
- 屏幕共享
- 虚拟背景
- 文件传输
- 聊天记录
同时 `tailchat-meeting` 还可以作为独立单品存在,无需登录即可快速发起/加入会议
### 项目仓库
- 开源地址: [https://github.com/msgbyte/tailchat-meeting](https://github.com/msgbyte/tailchat-meeting)
- 开源协议: GPL-3.0
:::info 开源声明
本项目基于 [edumeet](https://github.com/edumeet/edumeet) 和 [mediasoup](https://github.com/versatica/mediasoup) 进行二次开发而来。
在此基础上进行了功能追加与SDK实现以及代码优化。如果想要找到开源协议更加宽松(MIT + ISC 协议)的实现可以看一下这两个项目
:::
### 项目架构
![](/img/architecture/meeting.excalidraw.svg)

@ -0,0 +1,6 @@
---
sidebar_position: 3
title: 插件部署
---
TODO

@ -0,0 +1,6 @@
---
sidebar_position: 4
title: SDK
---
TODO

@ -0,0 +1,4 @@
{
"label": "插件列表",
"position": 20
}

@ -0,0 +1,46 @@
---
sidebar_position: 1
title: 纯前端插件
---
### com.msgbyte.bbcode
`默认安装`
一个用于支持bbcode语法解释富文本消息的插件
### com.msgbyte.intro
`默认安装`
新人引导插件
### com.msgbyte.notify
`默认安装`
消息通知插件
### com.msgbyte.webview
`默认安装`
增加用户添加群组网页面板的能力。
### com.msgbyte.draw
允许用户发送自定义绘图
### com.msgbyte.genshin
原神工具箱
### com.msgbyte.miaolang
允许发送喵语,安装插件后的双方加密对话,未安装插件的人看到的是 "喵"
### com.msgbyte.openapi
`WIP`
开放平台管理插件

@ -0,0 +1,36 @@
---
sidebar_position: 3
title: 前后端插件
---
### com.msgbyte.github
github 集成
目前支持接受来自`Github webhooks`的推送
### com.msgbyte.linkmeta
Url元数据展示
用于获取消息中网址的基本信息,如标题/概述/缩略图
支持媒体路径,直接显示媒体播放器
特殊支持bilibili自动加载b站iframe播放器
### com.msgbyte.simplenotify
简易机器人,用于简易推送消息到群组频道
### com.msgbyte.tasks
任务管理插件在个人面板中增加TODO面板用于个人待办事项的管理
### com.msgbyte.meeting
`WIP`
tailchat视频会议与语音频道支持

@ -0,0 +1,19 @@
---
sidebar_position: 2
title: 自定义主题
---
### com.msgbyte.theme.genshin
原神主题
包含主题如下:
- 原神-胡桃
- 原神-琴
- 原神-安柏
- 原神-莫娜
- 原神-罗莎莉亚
### com.msgbyte.theme.miku
初音未来主题,支持亮色与暗色

@ -0,0 +1,4 @@
{
"label": "开发插件",
"position": 99
}

@ -0,0 +1,4 @@
{
"label": "API 接口",
"position": 99
}

@ -0,0 +1,249 @@
---
sidebar_position: 1
title: "@capital/common"
---
## 注册
### regGroupPanel
注册群组面板
```typescript
regGroupPanel({
name: `com.msgbyte.webview/grouppanel`,
label: '网页面板',
provider: PLUGIN_NAME,
extraFormMeta: [{ type: 'text', name: 'url', label: '网址' }],
render: GroupWebPanelRender,
});
```
参数类型: [PluginGroupPanel](#plugingrouppanel)
### regMessageInterpreter
注册消息解释器
```typescript
regMessageInterpreter({
name: '喵语翻译',
explainMessage(message: string) {
// 喵语 -> 人话
if (!isMiao(message)) {
return null;
}
return decode(message);
},
});
```
参数类型: [PluginMessageInterpreter](#pluginmessageinterpreter)
### regMessageRender
*注册多个仅生效最后一个*
注册消息渲染器, 输入消息文本返回渲染内容
```typescript
regMessageRender((message) => {
return <BBCode plainText={message} />;
});
```
### regChatInputAction
注册聊天输入框操作
```typescript
regChatInputAction({
label: '喵言喵语',
onClick: (actions) => {
openModal(createElement(SendMiaoModal, { actions }));
},
});
```
参数类型: [ChatInputAction](#chatinputaction)
### regPluginColorScheme
注册插件配色方案/主题
```typescript
regPluginColorScheme({
label: 'Miku 葱',
name: 'light+miku',
});
```
## 工具函数
### useGroupPanelParams
在`hooks`中获取用户面板相关信息
```typescript
import { useGroupPanelParams } from '@capital/common';
const { groupId, panelId } = useGroupPanelParams();
```
### openModal
打开一个模态框
```typescript
openModal(
content: React.ReactNode,
props?: Pick<ModalProps, 'closable' | 'maskClosable'>
)
```
类型:
- [ModalProps](#modalprops)
### ModalWrapper
模态框包装器
```jsx
<ModalWrapper>
<div></div>
</ModalWrapper>
```
### useModalContext
获取模态框上下文
```typescript
const { closeModal } = useModalContext();
```
### getGlobalState
获取全局 `Redux` 状态上下文
```typescript
const state = getGlobalState();
```
### getCachedUserInfo
获取用户信息, 缓存版本
```typescript
const info = getCachedUserInfo(userId);
```
### getCachedConverseInfo
获取会话信息
```typescript
const info = getCachedConverseInfo(converseId);
```
## 类型
### PluginGroupPanel
```typescript
interface PluginGroupPanel {
/**
* 面板唯一标识
* @example com.msgbyte.webview/grouppanel
*/
name: string;
/**
* 面板显示名
*/
label: string;
/**
* 插件提供者, 用于引导没有安装插件的用户安装插件
*/
provider: string;
/**
* 额外的表单数据, 用于创建面板时使用
*/
extraFormMeta: FastFormFieldMeta[];
/**
* 该面板如何渲染
*/
render: React.ComponentType;
}
```
### PluginMessageInterpreter
插件消息解释器
```typescript
interface PluginMessageInterpreter {
name?: string;
explainMessage: (message: string) => React.ReactNode;
}
```
### ChatInputAction
消息输入框操作对象
```typescript
interface ChatInputAction {
label: string;
onClick: (actions: ChatInputActionContextProps) => void;
}
```
### GroupPanel
```typescript
interface GroupPanel {
id: string; // 在群组中唯一
name: string;
parentId?: string;
type: GroupPanelType;
provider?: string; // 面板提供者
pluginPanelName?: string; // 插件面板名
meta?: Record<string, unknown>;
}
```
### ModalProps
```typescript
interface ModalProps {
visible?: boolean;
onChangeVisible?: (visible: boolean) => void;
/**
* 是否显示右上角的关闭按钮
* @default false
*/
closable?: boolean;
/**
* 遮罩层是否可关闭
*/
maskClosable?: boolean;
}
```

@ -0,0 +1,22 @@
---
sidebar_position: 2
title: "@capital/component"
---
### Button
组件来自 [antd](https://ant.design/)
组件文档: [Button](https://ant.design/components/button-cn/)
### TextArea
组件来自 [antd](https://ant.design/)
组件文档: [TextArea](https://ant.design/components/input-cn/#components-input-demo-textarea)
### Image
组件来自 [antd](https://ant.design/)
组件文档: [Image](https://ant.design/components/image-cn/)

@ -0,0 +1,9 @@
---
sidebar_position: 3
title: 全局CSS变量
---
- `--tc-primary-color`: 主色调
- `--tc-background-image`: 背景图片
- `--tc-content-background-image`: 内容页背景图片
- `--tc-content-background-image-opacity`: 内容页背景图片透明度,默认 **0.15**

@ -0,0 +1,19 @@
---
sidebar_position: 4
title: "data-tc-role"
---
`Tailchat Role` 是一种通过`data-*`方式来标识DOM的一种方式表明该节点在`Tailchat`中的角色, 开发者可以通过这来找到对应角色的DOM节点
例如: `[data-tc-role=navbar]`
- `navbar`: 导航栏
- `navbar-personal`: 导航栏中的个人主页
- `navbar-groups`: 导航栏中的群组
- `navbar-settings`: 导航栏中的设置
- `sidebar-personal`: 个人主页中的侧边栏
- `sidebar-group`: 群组中的侧边栏
- `content-personal`: 个人主页中的内容
- `content-group`: 群组页面中的内容
- `modal`: 模态框
- `modal-mask`: 模态框的遮罩层

@ -0,0 +1,20 @@
---
sidebar_position: 2
title: Icon 图标
---
```ts
import { Icon } from '@capital/component';
```
`tailchat` 的 icon 解决方案来自 `iconify`
使用方法很简单:
- 在下述网站中选择想要的图标: [https://icon-sets.iconify.design/](https://icon-sets.iconify.design/)
- 复制选中的key。传给 `Icon` 组件, 示例:
```tsx
<Icon icon="mdi:account" />
```
推荐使用`mdi`来统一化图标视觉设计:
[https://icon-sets.iconify.design/mdi/](https://icon-sets.iconify.design/mdi/)

@ -0,0 +1,58 @@
---
sidebar_position: 1
title: 开始开发插件
---
## 认识 MiniStar
`MiniStar` 是一套完整的微内核架构开发工具链,`tailchat`的插件架构就是基于 `MiniStar` 进行开发。
关于更多的 MiniStar 相关问题可以查看 MiniStar 的官方文档: [https://ministar.moonrailgun.com/](https://ministar.moonrailgun.com/)
## 创建一个基础项目
首先创建一个基本的 npm 项目, 并全局安装 `MiniStar`
```bash
npm install --global mini-star
```
在项目中执行: `ministar createPlugin` 来创建一个基本的插件
在项目中执行: `ministar buildPlugin` 来编译插件
> 值得一提的是, 虽然 `Tailchat` 并没有强制规定插件命名规范,但是还是推荐使用 `反域名` 的命名方式(类似于java中的包命名), 然后对插件中的部件,使用 `/` 进行分割
>
> 如:
> 插件名: `com.msgbyte.webview`
>
> 注册内容: `com.msgbyte.webview/grouppanel`
## 安装插件
### 手动安装插件
在不经过任何预设的情况下,一个通用的办法是自己构造一个 `manifest` 配置, 然后在 `tailchat` 提供手动安装插件 Tab 中将配置文件粘贴进去安装。
插件的url路径可以通过 `oss`/`static-server` 等办法代理
一个作为示例的`manifest`配置如下:
```json
{
"label": "网页面板插件",
"name": "com.msgbyte.webview",
"url": "/plugins/com.msgbyte.webview/index.js",
"version": "0.0.0",
"author": "msgbyte",
"description": "为群组提供创建网页面板的功能",
"requireRestart": false
}
```
## 其他有用的资源
- 来自基础项目提供的API: [API 文档](./api/common)
- 导出接口源码
- [@capital/common](https://github.com/msgbyte/tailchat/blob/master/web/src/plugin/common/index.ts)
- [@capital/component](https://github.com/msgbyte/tailchat/blob/master/web/src/plugin/component/index.tsx)

@ -0,0 +1,26 @@
---
sidebar_position: 2
title: 使用场景
---
Tailchat 的设计之处就是以插件化的架构来满足不同人群对于不同需求的实现。
## 对于个人用户
- 如果希望和朋友一起玩
- 创建一个群组
- 通过多个频道分割不同的话题
- 使用网页面板来分享喜欢的网页
- 如果希望聚集自己的粉丝圈
- 使用机器人来订阅自己的信息并转发到聊天面板
- 让自己的粉丝集中在一起不需要创建无数个qq群/微信群
- 多个频道让多个话题能够一起产生
- 如果对于自己的隐私非常看中
- 自己部署让一切都能掌控在手中
## 对于企业用户
- 面板化设计满足企业自定义化设计需求
- 插件化架构可以方便基于核心进行二次开发
- 自部署的实现可以让企业价值得到保护,让企业安心
- 开源代码方便审查

@ -0,0 +1,118 @@
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
const themeConfig = {
navbar: {
title: 'Tailchat',
logo: {
alt: 'Tailchat Logo',
src: 'img/logo.svg',
},
items: [
{
type: 'doc',
docId: 'intro',
position: 'left',
label: '文档',
},
{ to: '/blog', label: '博客', position: 'left' },
{
href: 'https://github.com/msgbyte/tailchat',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
// links: [
// {
// title: 'Docs',
// items: [
// {
// label: 'Tutorial',
// to: '/docs/intro',
// },
// ],
// },
// {
// title: 'Community',
// items: [
// {
// label: 'Stack Overflow',
// href: 'https://stackoverflow.com/questions/tagged/docusaurus',
// },
// {
// label: 'Discord',
// href: 'https://discordapp.com/invite/docusaurus',
// },
// {
// label: 'Twitter',
// href: 'https://twitter.com/docusaurus',
// },
// ],
// },
// {
// title: 'More',
// items: [
// {
// label: 'Blog',
// to: '/blog',
// },
// {
// label: 'GitHub',
// href: 'https://github.com/facebook/docusaurus',
// },
// ],
// },
// ],
copyright: `Copyright © ${new Date().getFullYear()} MsgByte, Inc. Built with Docusaurus and ❤.`,
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
},
zoom: {
selector: '.markdown img',
config: {
// options you can specify via https://github.com/francoischalifour/medium-zoom#usage
background: {
light: 'rgb(255, 255, 255)',
dark: 'rgb(50, 50, 50)',
},
},
},
};
/** @type {import('@docusaurus/preset-classic').Options} */
const presetClassicOptions = {
docs: {
sidebarPath: require.resolve('./sidebars.js'),
// Please change this to your repo.
editUrl: 'https://github.com/msgbyte/tailchat-website/edit/master/website/',
},
blog: {
postsPerPage: 5,
},
// blog: false,
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
};
/** @type {import('@docusaurus/types').DocusaurusConfig} */
module.exports = {
title: 'Tailchat',
tagline: '一个插件化易拓展的开源 IM 应用',
url: 'https://tailchat.msgbyte.com', // TODO: 待修改成文档主页
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/logo.svg',
organizationName: 'msgbyte', // Usually your GitHub org/user name.
projectName: 'tailchat', // Usually your repo name.
themeConfig,
presets: [['@docusaurus/preset-classic', presetClassicOptions]],
plugins: [require.resolve('docusaurus-plugin-image-zoom')],
};

@ -0,0 +1,49 @@
{
"name": "website",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start -p 11033",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle --typescript",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "2.0.0-beta.18",
"@docusaurus/preset-classic": "2.0.0-beta.18",
"@mdx-js/react": "^1.6.21",
"@svgr/webpack": "^5.5.0",
"clsx": "^1.1.1",
"docusaurus-plugin-image-zoom": "^0.0.2",
"file-loader": "^6.2.0",
"prism-react-renderer": "^1.2.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"url-loader": "^4.1.1"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^2.0.0-beta.18",
"@tsconfig/docusaurus": "^1.0.5",
"@types/react": "^17.0.26",
"@types/react-helmet": "^6.1.2",
"@types/react-router-dom": "^5.3.0",
"typescript": "^4.4.3"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,26 @@
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
module.exports = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }],
// But you can create a sidebar manually
/*
tutorialSidebar: [
{
type: 'category',
label: 'Tutorial',
items: ['hello'],
},
],
*/
};

@ -0,0 +1,13 @@
/* stylelint-disable docusaurus/copyright-header */
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}

@ -0,0 +1,66 @@
import React from 'react';
import clsx from 'clsx';
import styles from './HomepageFeatures.module.css';
const FeatureList = [
{
title: '易于使用',
Svg: require('../../static/img/undraw_Website_setup_re_d4y9.svg').default,
description: (
<>
<code>Tailchat</code> <code>discord</code>, {' '}
<code>discord</code> , <code>Tailchat</code>{' '}
使
</>
),
},
{
title: '始于插件',
Svg: require('../../static/img/undraw_design_components_9vy6.svg').default,
description: (
<>
<code>Tailchat</code>{' '}
{' '}
<code>Tailchat</code>
</>
),
},
{
title: '面向开源',
Svg: require('../../static/img/undraw_open_source_1qxw.svg').default,
description: (
<>
<code>Tailchat</code>{' '}
Tailchat
</>
),
},
];
function Feature({ Svg, title, description }) {
return (
<div className={clsx('col col--4')}>
<div className="text--center">
<Svg className={styles.featureSvg} alt={title} />
</div>
<div className="text--center padding-horiz--md">
<h3>{title}</h3>
<p>{description}</p>
</div>
</div>
);
}
export default function HomepageFeatures() {
return (
<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
);
}

@ -0,0 +1,33 @@
/* stylelint-disable docusaurus/copyright-header */
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: rgb(33, 175, 144);
--ifm-color-primary-darker: rgb(31, 165, 136);
--ifm-color-primary-darkest: rgb(26, 136, 112);
--ifm-color-primary-light: rgb(70, 203, 174);
--ifm-color-primary-lighter: rgb(102, 212, 189);
--ifm-color-primary-lightest: rgb(146, 224, 208);
--ifm-code-font-size: 95%;
}
.docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.1);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
html[data-theme='dark'] .docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.3);
}
.medium-zoom-image--opened {
z-index: var(--ifm-z-index-fixed);
}

@ -0,0 +1,42 @@
/* stylelint-disable docusaurus/copyright-header */
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
*/
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
@media screen and (max-width: 966px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
}
.videoContainer {
text-align: center;
padding: 40px 0;
}
.videoIframe {
width: 640px;
height: 360px;
}
@media screen and (max-width: 966px) {
.videoIframe {
width: 360px;
height: 240px;
}
}

@ -0,0 +1,62 @@
import React from 'react';
import clsx from 'clsx';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import styles from './index.module.css';
import HomepageFeatures from '../components/HomepageFeatures';
function HomepageHeader() {
const { siteConfig } = useDocusaurusContext();
return (
<header className={clsx('hero hero--primary', styles.heroBanner)}>
<div className="container">
<h1 className="hero__title">{siteConfig.title}</h1>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<div className={styles.buttons}>
<Link
className="button button--secondary button--lg"
to="https://nightly.paw.msgbyte.com/"
>
Nightly
</Link>
</div>
</div>
</header>
);
}
function HomepageVideo() {
return (
<div className={styles.videoContainer}>
<iframe
className={styles.videoIframe}
src="//player.bilibili.com/player.html?aid=340398093&bvid=BV1394y1Z76n&cid=568332564&page=1"
scrolling="no"
border="0"
frameBorder="no"
framespacing="0"
allowFullScreen="true"
>
{' '}
</iframe>
</div>
);
}
export default function Home() {
const { siteConfig } = useDocusaurusContext();
return (
<Layout
title={`Hello from ${siteConfig.title}`}
description="Description will go into a meta tag in <head />"
>
<HomepageHeader />
<main>
<HomepageVideo />
<HomepageFeatures />
</main>
</Layout>
);
}

@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

@ -0,0 +1 @@
tailchat.msgbyte.com

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

@ -0,0 +1,4 @@
{
"extends": "@tsconfig/docusaurus/tsconfig.json",
"include": ["src/"]
}
Loading…
Cancel
Save